KLingo Project Documentation 1.0.0
Unreal Engine 5.6 C++ Project Documentation
로딩중...
검색중...
일치하는것 없음
UPopup_DailyStudy.cpp
이 파일의 문서화 페이지로 가기
1// Copyright (c) 2025 Doppleddiggong. All rights reserved. Unauthorized copying, modification, or distribution of this file, via any medium is strictly prohibited. Proprietary and confidential.
2
3#include "UPopup_DailyStudy.h"
4
5#include "APlayerActor.h"
6#include "UPopupManager.h"
8#include "ULingoGameHelper.h"
9#include "GameLogging.h"
10#include "UBroadcastManager.h"
13#include "UDialogManager.h"
14#include "UGameSoundManager.h"
15
16#include "Components/Image.h"
17#include "Components/TextBlock.h"
18#include "Components/RichTextBlock.h"
19#include "UTextureButton.h"
20#include "Components/CanvasPanel.h"
21#include "Misc/FileHelper.h"
22#include "Misc/Paths.h"
23
24// HyperLink Plugin
25#include "HyperLinkPluginBPLibrary.h"
26
27// Media Player
28#include "MediaPlayer.h"
29#include "MediaTexture.h"
30#include "MediaSource.h"
31#include "Components/Border.h"
32
34{
36
37 Super::NativeConstruct();
38
39 // 미디어 플레이어 초기화
40 if (!MediaPlayer)
41 {
42 MediaPlayer = NewObject<UMediaPlayer>(this);
43 MediaPlayer->SetLooping(false);
44 MediaPlayer->OnEndReached.AddDynamic(this, &UPopup_DailyStudy::OnVideoFinished);
45 }
46
47 // 미디어 텍스처 생성
48 if (!MediaTexture)
49 {
50 MediaTexture = NewObject<UMediaTexture>(this);
51 MediaTexture->SetMediaPlayer(MediaPlayer);
52 MediaTexture->UpdateResource();
53 }
54
55 // Img_Correct에 미디어 텍스처 바인딩
57 {
58 FSlateBrush Brush;
59 Brush.SetResourceObject(MediaTexture);
60 Brush.ImageSize = FVector2D(1920, 1080); // 영상 해상도에 맞게 조정
61 Img_Correct->SetBrush(Brush);
62 }
63}
64
66{
67 Super::NativeDestruct();
68
69 // 타이머 정리
70 if (GetWorld())
71 {
72 GetWorld()->GetTimerManager().ClearTimer(NextTimerHandle);
73 GetWorld()->GetTimerManager().ClearTimer(ThinkingTimerHandle);
74 GetWorld()->GetTimerManager().ClearTimer(VideoCheckTimerHandle);
75 }
76
77 // 미디어 플레이어 정리
78 MediaPlayer->Close();
79}
80
81void UPopup_DailyStudy::InitPopup(const TArray<FWordData>& WordDataArray)
82{
83 // 기본 초기화 (델리게이트 바인딩 등)
84 if (Btn_Close)
85 {
86 Btn_Close->OnButtonClickedEvent.RemoveDynamic(this, &UPopup_DailyStudy::OnClickClose);
87 Btn_Close->OnButtonClickedEvent.AddDynamic(this, &UPopup_DailyStudy::OnClickClose);
88 }
89
90 if (auto DM = UBroadcastManager::Get(GetWorld()))
91 {
92 DM->OnAudioCapture.RemoveDynamic(this, &UPopup_DailyStudy::OnAudioCapture);
93 DM->OnAudioCapture.AddDynamic(this, &UPopup_DailyStudy::OnAudioCapture);
94 }
95
97 {
98 CountDown_Widget->OnCountDownFinished.RemoveDynamic(this, &UPopup_DailyStudy::OnCountDownFinished);
99 CountDown_Widget->OnCountDownFinished.AddDynamic(this, &UPopup_DailyStudy::OnCountDownFinished);
100 }
101
102 // 초기화
103 CurIndex = 0;
104 CurrentScore = 0;
106
107 QuestionList.Empty();
108 QuestionList.Append(WordDataArray);
109
110 AnswerList.Empty();
111
112 Canvas_Correct->SetVisibility(ESlateVisibility::Hidden);
113 Canvas_Question->SetVisibility(ESlateVisibility::Hidden);
114
115 Txt_CurScore->SetText(FText::FromString(FString::Printf(TEXT("Score : %d"), CurrentScore)));
116
117 // 카운트다운 시작
118 CountDown_Widget->StartCountDown(3);
119}
120
122{
123 if (!QuestionList.IsValidIndex(CurIndex))
124 return;
125
126 Border_Question->SetVisibility(ESlateVisibility::Visible);
127 Txt_Infomation->SetVisibility(ESlateVisibility::Visible);
128
129 Rich_Text->SetText(FText::FromString(QuestionList[CurIndex].Eng));
130 Txt_SubTitle->SetText(FText::FromString(FString::Printf(TEXT("[%s]"), *QuestionList[CurIndex].Pronunciation)));
131
132 // 진행 상황 업데이트
133 Txt_QuestionProgress->SetText(FText::FromString(
134 FString::Printf(TEXT("%02d/%02d"),
135 CurIndex + 1,
136 QuestionList.Num())));
137
139}
140
142{
143 if ( auto PopupMgr = UPopupManager::Get(GetWorld()) )
144 {
145 PopupMgr->HideCurrentPopup();
146 }
147}
148
150{
151 if (QuestionList.IsValidIndex(CurIndex))
152 return QuestionList[CurIndex].Kor;
153
154 return FString();
155}
156
158{
159 if (!QuestionList.IsValidIndex(CurIndex))
160 return;
161
162 // 답변 리스트에 추가
163 AnswerList.Add(JudgeResult);
164
165 CurrentScore += JudgeResult.final_overall_score;
166 Txt_CurScore->SetText(FText::FromString(FString::Printf(TEXT("Score : %d"), CurrentScore)));
167
168 // 정답/오답 판별 (50점 미만이면 오답)
169 bLastAnswerCorrect = (JudgeResult.final_overall_score >= 50);
170
171 if ( bLastAnswerCorrect )
173
174 // 정답/오답 화면 표시 (영상 + TTS)
176}
177
179{
181
182 ProgressBar_RemainTime->SetPercent(1.0f);
183 Txt_RemainTime->SetText(FText::FromString(FString::Printf(TEXT("%.1f"), RemainingThinkTime)));
184
185 GetWorld()->GetTimerManager().SetTimer(ThinkingTimerHandle, this, &UPopup_DailyStudy::UpdateThinkTimer, 0.1f, true);
186}
187
189{
190 RemainingThinkTime -= 0.1f;
191 if (RemainingThinkTime <= 0.f)
192 {
193 RemainingThinkTime = 0.f;
195 }
196
198 Txt_RemainTime->SetText(FText::FromString(FString::Printf(TEXT("%.1f"), RemainingThinkTime)));
199}
200
202{
203 GetWorld()->GetTimerManager().ClearTimer(ThinkingTimerHandle);
204
205 // 타임업 = 오답 처리
206 bLastAnswerCorrect = false;
207
208 // 빈 답변 추가 (건너뜀 처리)
209 if (QuestionList.IsValidIndex(CurIndex))
210 {
211 FResponseSpeakingJudes EmptyAnswer;
212 EmptyAnswer.final_overall_score = 0;
213 EmptyAnswer.grammar_score = 0;
214 EmptyAnswer.context_score = 0;
215 EmptyAnswer.final_feedback = TEXT("Time Over");
216 AnswerList.Add(EmptyAnswer);
217 }
218
219 // 오답 화면 표시 (영상 + TTS)
220 ShowCorrectData(false);
221}
222
224{
225 if (bIsRecording)
226 {
227 GetWorld()->GetTimerManager().ClearTimer(ThinkingTimerHandle);
228 }
229}
230
232{
233 if (bWasSuccessful)
234 {
235 if (auto PlayerActor = ULingoGameHelper::GetPlayerActor(this))
236 PlayerActor->PlayTTSAudio(ResponseData.audio_base64);
237
238 // 다음 문제로 이동 (기존 타이머 로직 사용)
239 GetWorld()->GetTimerManager().SetTimer(
243 false
244 );
245 }
246 else
247 {
248 // 다음 문제로 이동 (기존 타이머 로직 사용)
249 GetWorld()->GetTimerManager().SetTimer(
253 false
254 );
255 }
256}
257
259{
260 if ( auto DM = UDialogManager::Get(GetWorld()))
261 DM->HideToastImmediately();
262
263 Canvas_Correct->SetVisibility(ESlateVisibility::Visible);
264
265 // FWordData에서 한글 단어 표시
266 Txt_Correct->SetText(FText::FromString(QuestionList[CurIndex].Kor));
267
268 Border_Question->SetVisibility(ESlateVisibility::Hidden);
269 Txt_Infomation->SetVisibility(ESlateVisibility::Hidden);
270
271 if (UKLingoNetworkSystem* NetworkSystem = UKLingoNetworkSystem::Get(GetWorld()))
272 {
273 // 한글 단어를 음성으로 요청
274 NetworkSystem->RequestListenAudio(
276 FResponseListenAudioDelegate::CreateUObject(this, &UPopup_DailyStudy::OnResponseListenAudio)
277 );
278 }
279
280 PlayVideo(bIsCorrect);
281}
282
284{
285 Canvas_Question->SetVisibility(ESlateVisibility::Visible);
286
288}
289
291{
292 Canvas_Correct->SetVisibility(ESlateVisibility::Hidden);
293
294 CurIndex++;
295
296 if (CurIndex < QuestionList.Num())
297 {
299 }
300 else
301 {
302 // 현재 팝업 닫기
303 OnClickClose();
304
305 // 모든 문제 완료 - DailyResult 팝업 표시
306 if (auto DailyResultPopup = UPopupManager::Get(GetWorld())->ShowPopupAs<UPopup_DailyResult>(EPopupType::DailyResult))
307 {
308 // FDailyStudyResult 생성
309 FDailyStudyResult Result;
310 Result.CurrentScore = CurrentScore;
311 Result.QuestionList = QuestionList;
312 Result.AnswerList = AnswerList;
313
314 DailyResultPopup->InitPopup(Result);
315 }
316 }
317}
318
319void UPopup_DailyStudy::PlayVideo(bool bIsCorrect)
320{
321 if (!MediaPlayer || !Img_Correct)
322 {
323 // 영상 재생 실패 시 바로 다음 문제로
325 return;
326 }
327
328 // 재생할 영상 소스 선택
329 UMediaSource* SourceToPlay = bIsCorrect ? CorrectVideoSource : WrongVideoSource;
330 UGameSoundManager::Get(GetWorld())->PlaySound2D(bIsCorrect? EGameSoundType::UI_Success : EGameSoundType::UI_Failure);
331
332 if (!SourceToPlay)
333 {
334 // 영상이 없으면 바로 다음 문제로 이동
336 return;
337 }
338
339 // Img_Correct 표시
340 Img_Correct->SetVisibility(ESlateVisibility::Visible);
341
342 // 영상 열기 및 재생
343 MediaPlayer->OpenSource(SourceToPlay);
344 MediaPlayer->Play();
345
346 // 시작 위치로 Seek
347 MediaPlayer->Seek(FTimespan::FromSeconds(VideoStartTime));
348
349 // 재생 시간 체크 타이머 시작 (0.1초마다 체크)
350 if (GetWorld())
351 {
352 GetWorld()->GetTimerManager().SetTimer(
355 0.1f,
356 true // 반복
357 );
358 }
359}
360
362{
363 if (!MediaPlayer)
364 return;
365
366 // 현재 재생 시간 확인
367 FTimespan CurrentTime = MediaPlayer->GetTime();
368 float CurrentSeconds = CurrentTime.GetTotalSeconds();
369
370 // 종료 시간 도달 체크
371 if (CurrentSeconds >= VideoEndTime)
372 {
373 // 타이머 중지
374 GetWorld()->GetTimerManager().ClearTimer(VideoCheckTimerHandle);
375
376 // 영상 종료 처리
378 }
379}
380
382{
383 // 영상 숨기기
384 Img_Correct->SetVisibility(ESlateVisibility::Hidden);
385
386 // 미디어 플레이어 정지
387 MediaPlayer->Close();
388
389 // 타이머 정리
390 GetWorld()->GetTimerManager().ClearTimer(VideoCheckTimerHandle);
391}
Declares the player-controlled character actor.
YiSan 전반에서 사용하는 공용 인터페이스를 선언합니다.
UDialogManager 클래스를 선언합니다.
UGameSoundManager 클래스를 선언합니다.
KLingo API 요청을 담당하는 서브시스템을 선언합니다.
bool bAllowPlayerControl
이 팝업이 활성화되어 있을 때 플레이어 조작을 허용할지 여부
Definition UBasePopup.h:105
KLingo 서버와의 HTTP 요청을 중재하는 게임 인스턴스 서브시스템입니다.
static class APlayerActor * GetPlayerActor(const UObject *WorldContextObject)
첫 번째 플레이어의 PlayerActor를 가져옵니다.
void CheckVideoPlayback()
영상 재생 시간 체크
void MoveToNextQuestion()
다음 질문으로 이동
virtual void NativeConstruct() override
TObjectPtr< class UCircularProgressBar > ProgressBar_RemainTime
TObjectPtr< class UCanvasPanel > Canvas_Question
void LoadCurQuestion()
현재 질문 UI 업데이트
TObjectPtr< class UTextBlock > Txt_SubTitle
TArray< FResponseSpeakingJudes > AnswerList
답변 리스트 (FResponseSpeakingJudes)
FString GetCurrentQuestionText() const
virtual void NativeDestruct() override
TArray< struct FWordData > QuestionList
AI 생성 단어 데이터 목록 (한글/영어/발음)
bool bLastAnswerCorrect
마지막 답변이 정답인지 여부 (타임업 또는 50점 미만 = false)
float VideoEndTime
영상 재생 종료 시간 (초)
TObjectPtr< class UBorder > Border_Question
TObjectPtr< class UMediaSource > WrongVideoSource
오답 영상 소스
TObjectPtr< class UImage > Img_Correct
void OnVideoFinished()
영상 재생 완료 콜백
TObjectPtr< class UTextBlock > Txt_CurScore
TObjectPtr< class UTextureButton > Btn_Close
FTimerHandle NextTimerHandle
다음 문제 이동 타이머
TObjectPtr< class UMediaPlayer > MediaPlayer
미디어 플레이어
TObjectPtr< class UCanvasPanel > Canvas_Correct
FTimerHandle VideoCheckTimerHandle
영상 재생 시간 체크 타이머
void ShowCorrectData(bool bIsCorrect)
정답 데이터 표시 (정답/오답 판별)
void OnClickClose()
닫기 버튼 클릭
int32 CurIndex
현재 질문 인덱스 (0-based)
TObjectPtr< class UTextBlock > Txt_QuestionProgress
void OnResponseListenAudio(FResponseListenAudio &Response, bool bWasSuccessful)
TObjectPtr< class UMediaTexture > MediaTexture
미디어 텍스처
TObjectPtr< class UTextBlock > Txt_RemainTime
TObjectPtr< class UTextBlock > Txt_Correct
float VideoStartTime
영상 재생 시작 시간 (초)
void PlayVideo(bool bIsCorrect)
영상 재생 (정답/오답)
void OnResponseSpeakingsJudges(const FResponseSpeakingJudes &JudgeResult)
void InitPopup(const TArray< struct FWordData > &WordDataArray)
AI 생성 단어 데이터로 Daily Study 초기화
void OnAudioCapture(bool bIsRecording)
TObjectPtr< class UCountDown > CountDown_Widget
TObjectPtr< class URichTextBlock > Rich_Text
TObjectPtr< class UTextBlock > Txt_Infomation
FTimerHandle ThinkingTimerHandle
TObjectPtr< class UMediaSource > CorrectVideoSource
정답 영상 소스
static constexpr float THINK_TIME
static constexpr float NEXT_QUESTION
TArray< FWordData > QuestionList
질문 단어 데이터 리스트 (한글/영어/발음)
int32 CurrentScore
현재 점수
TArray< FResponseSpeakingJudes > AnswerList
답변 리스트
TArray< uint8 > audio_base64
Speaking Questions 응답 구조체입니다.