Dev Log - dopple
- 20251212
Compact Log 1
- 20251212 14:30
SpeakQuest 평가 결과 표시 시스템 구현 완료
RequestSpeakingJudges 평가 결과를 UToastWidget을 통해 표시하고 PlayerState에 저장하는 시스템을 구현했습니다.
변경 파일:
NetworkData.h/cpp: FSpeakStageQuestion 구조를 실제 API 형식에 맞춰 정리 (kor/eng/pronunciation/voice_data)ALingoPlayerState.h/cpp: speakEvaluationResults 배열 추가, Server_AddSpeakEvaluationResult RPC 구현AWheatly.h/cpp: BeginSpeakQuest/CompleteSpeakQuest 함수 구현, UBroadcastManager 연동UVoiceConversationSystem.cpp: OnResponseSpeakingsJudges에서 평가 결과 PlayerState 저장 로직 추가UKLingoNetworkSystem.cpp: 헤더 포함 추가
주요 작업:
평가 결과 저장 시스템
- ALingoPlayerState에 speakEvaluationResults 배열 추가 (Replicated)
- Server_AddSpeakEvaluationResult RPC로 서버 측 평가 결과 저장
AWheatly SpeakQuest 워크플로우
- BeginSpeakQuest: RequestSpeakScenario 호출하여 질문 데이터 로드, PlayerState에 저장
- CompleteSpeakQuest: UBroadcastManager를 통해 축적된 모든 평가 결과를 UToastWidget으로 표시
- OnInteractionTriggered: bIsBusy 플래그로 턴 기반 NPC 접근 제어
UToastWidget 연동
- SendTutorMessage: final_feedback 메시지 표시
- SendAddItemToBoxList: grammar_score/context_score/final_overall_score를 FResultStatData로 변환하여 아이템 박스 표시
네트워크 데이터 구조 정리
- FSpeakStageQuestion을 실제 API 응답 형식에 맞춰 수정
- FResponseSpeakScenario의 audio 배열 파싱 로직 구현
- 불필요한 구조체 제거 (FResponseSpeakQuest, FResponseEvaluation 등)
평가 결과 표시 흐름
- 즉시 표시: 각 질문 평가 완료 시 UToastWidget으로 즉시 표시 (기존 동작 유지)
- 요약 표시: SpeakQuest 완료 시 모든 축적된 평가 결과를 다시 표시
- 결과 초기화: 표시 후 다음 퀘스트를 위해 배열 클리어
Commit 정보
Summary (EN): feat: implement SpeakQuest evaluation result display system
세부 내역:
"Add evaluation result tracking in PlayerState"
- speakEvaluationResults 배열 추가 (Replicated)
- Server_AddSpeakEvaluationResult RPC 구현
"Implement SpeakQuest workflow in AWheatly"
- BeginSpeakQuest: RequestSpeakScenario API 호출, 질문 데이터 로드
- CompleteSpeakQuest: 축적된 평가 결과를 UToastWidget으로 표시
- 턴 기반 NPC 접근 제어 (bIsBusy 플래그)
"Integrate UToastWidget for evaluation display"
- UBroadcastManager를 통한 SendTutorMessage/SendAddItemToBoxList 호출
- FResponseSpeakingJudes::GetResultStatData()를 활용한 데이터 변환
"Connect VoiceConversationSystem to PlayerState"
- OnResponseSpeakingsJudges에서 Server_AddSpeakEvaluationResult 호출
- 평가 결과 서버 저장 및 복제
"Align network data structures with actual API"
- FSpeakStageQuestion 구조 변경 (kor/eng/pronunciation/voice_data)
- FResponseSpeakScenario audio 배열 파싱
- 미사용 구조체 제거
기술적 특징
- 서버 권한 관리: HasAuthority() 검증으로 모든 SpeakQuest 로직은 서버에서만 실행
- 네트워크 복제: DOREPLIFETIME으로 speakEvaluationResults, speakQuestData 복제
- 턴 기반 멀티플레이: bIsBusy 플래그로 동시 접근 방지, busyPlayerName으로 현재 사용자 표시
- 델리게이트 기반 비동기 처리: FResponseSpeakScenarioDelegate, FResponseSpeakingJudesDelegate 활용
- 브로드캐스트 패턴: UBroadcastManager를 통한 느슨한 결합
KLingo SpeakQuest 개발 Phase 진행 상황
참고:
Documents/DevLog/AgentLog/KLingo_Speak_Prompt.md
Phase 1: 기반 구조 (High Priority) - ✅ 부분 완료
완료 항목:
- ✅
FSpeakStageQuestion구조체 정의 (실제 API 형식 반영) - ✅
AWheatly(NPC) InteractComponent 기반 상호작용 구현 - ✅
ALingoPlayerStateSpeakQuest 데이터 추가SpeakScenarioData: FResponseSpeakScenario (질문 데이터)CurSpeakQuestStep: 현재 진행 단계SpeakJudesResults: 평가 결과 배열
미완료 항목:
- ⬜
ASpeakStageActor턴 기반 대화 관리자 구현- 플레이어 큐 관리 (
PlayerQueue) - 턴 전환 로직 (
AdvanceToNextPlayer) - 현재 발화자 추적 (
GetCurrentSpeaker) - 대화 참가 요청 처리 (
RequestJoinConversation)
- 플레이어 큐 관리 (
Phase 2: 네트워크 통합 (High Priority) - ✅ 완료
- ✅
FResponseSpeakScenario응답 구조체 추가 및 파싱 - ✅
FResponseSpeakingJudes평가 결과 구조체 활용 - ✅
UKLingoNetworkSystem::RequestSpeakScenarioAPI 연동 - ✅
UKLingoNetworkSystem::RequestSpeakingJudges기존 시스템 활용 - ✅ 네트워크 로깅 통합 (PRINTLOG, NETWORK_LOG)
- ✅ Server RPC:
Server_AddSpeakJudes평가 결과 저장
Phase 3: 음성 시스템 (Medium Priority) - 🔄 진행 중
완료 항목:
- ✅
UVoiceConversationSystem::OnResponseSpeakingsJudges평가 결과 처리 - ✅ 평가 결과를 PlayerState에 저장하는 로직 연동
- ✅ 기존 녹음 시스템 (
IA_Record) 활용
미완료 항목:
- ⬜
APlayerControl녹음 입력 핸들러 확장OnRecordPressed/OnRecordReleasedSpeakQuest 모드 처리- 녹음 시작/종료 시 UI 업데이트
- ⬜ 질문 음성 재생 로직 (
PlayQuestionAudio) - ⬜ STT 통합 및 음성 데이터 제출 (
ServerRPC_SubmitAnswer)
Phase 4: UI 및 피드백 (Medium Priority) - 🔄 진행 중
완료 항목:
- ✅
UToastWidget연동 (UBroadcastManager 활용)SendTutorMessage: 피드백 메시지 표시SendAddItemToBoxList: 평가 점수 아이템 박스 표시
- ✅
AWheatly::CompleteSpeakQuest완료 화면 표시
미완료 항목:
- ⬜
UMainWidgetSpeakQuest 전용 UI 추가- 질문 텍스트 표시 (
ShowSpeakQuestion) - 진행률 표시 위젯
- 녹음 상태 인디케이터
- 질문 텍스트 표시 (
- ⬜ 최종 결과 화면 (
ShowSpeakQuestResult) - ⬜ 질문 다시 듣기 버튼 (
IA_ReplayInputAction) - ⬜ 답변 제출 후 피드백 표시 (
ClientRPC_ShowAnswerFeedback)
Phase 5: 테스트 및 최적화 (Low Priority) - ⬜ 미착수
- ⬜ 단일 플레이어 테스트
- ⬜ 멀티플레이어 턴 전환 검증
- ⬜ 네트워크 지연 시뮬레이션
- ⬜ 디버그 시각화
- NPC 시선 처리 (
DrawDebugLine) - 배제 영역 시각화 (
exclusionSphere)
- NPC 시선 처리 (
- ⬜ 성능 최적화
다음 작업 우선순위
🔥 High Priority (즉시 진행)
ASpeakStageActor 턴 기반 아키텍처 구현
KLingo_SpeakScenario.md명세 준수- PlayerQueue 관리
- 턴 전환 로직
- AWheatly와 연동
음성 녹음 및 답변 제출 로직
APlayerControl::ServerRPC_SubmitAnswer구현VoiceConversationSystem녹음 데이터 가져오기- RequestSpeakingJudges API 호출
📌 Medium Priority (다음 단계)
질문 음성 재생 시스템
VoiceConversationSystem::PlayQuestionAudio확장- voice_data Base64 디코딩 및 재생
SpeakQuest 전용 UI 구현
UMainWidget::ShowSpeakQuestion추가- 진행률 및 녹음 상태 표시
🔹 Low Priority (추후 진행)
- 테스트 및 디버깅
- 멀티플레이어 턴 전환 테스트
- 네트워크 동기화 검증
참고 문서
- 개발 명세:
Documents/DevLog/AgentLog/KLingo_Speak_Prompt.md - 아키텍처:
Documents/DevLog/AgentLog/KLingo_SpeakScenario.md - 코딩 컨벤션:
AgentRule/Project/Onepiece/CODING_CONVENTIONS.md
Compact Log 2
- 20251212 15:30
SpeakQuest 아키텍처 리팩토링 및 버그 수정
요구사항 변경(플레이어 큐 불필요)에 따라 SpeakQuest의 아키텍처를 '단일 사용자 모델'로 리팩토링하고, 관련 버그를 수정했습니다.
변경 파일:
Source/Onepiece/Game/Public/ASpeakStageActor.h: 큐(Queue) 관련 로직 제거 및 함수명/변수명 컨벤션 수정Source/Onepiece/Game/Private/ASpeakStageActor.cpp: 단일 사용자 모델에 맞게 로직 단순화 및 컨벤션 수정Source/Onepiece/Character/Public/AWheatly.h:ASpeakStageActor로 상태 관리를 이전함에 따라 불필요해진 상태 변수 제거Source/Onepiece/Character/Private/AWheatly.cpp:ASpeakStageActor를 유일한 정보 소스로 사용하도록 로직 변경 및Tick을 이용한 시각적 상태 동기화Source/Onepiece/Voice/Private/UVoiceConversationSystem.cpp: 녹음 시작 시, 퀘스트 상태에 따른 사용자 피드백 메시지 개선
주요 작업:
SpeakQuest 아키텍처 단순화
ASpeakStageActor에서 플레이어 큐(Queue) 시스템을 제거하고, 한 번에 한 명의 플레이어만 관리하는 'Busy/Available' 모델로 변경했습니다.PlayStart->StartStageForPlayer,AdvanceToNextPlayer->EndStage로 함수를 변경하여 역할을 명확히 했습니다.
상태 관리 책임 이전
AWheatly가 자체적으로 관리하던bIsBusy,busyPlayerName등의 중복 상태 변수를 모두 제거했습니다.- 이제
AWheatly는ASpeakStageActor의GetCurrentSpeaker()를 유일한 정보 소스(Single Source of Truth)로 사용하여 퀘스트의 진행 상태를 확인합니다.
버그 수정 및 사용자 피드백 개선
- 퀘스트가 시작되지 않은 상태에서 녹음을 시도하면
CurrentSpeaker가nullptr이 되어 진행이 막히던 최초 버그를 해결했습니다. UVoiceConversationSystem에서 녹음이 불가능할 경우, "NPC와 대화하여 심사를 시작하세요"와 같이 명확한 안내 메시지를 표시하도록 개선했습니다.
- 퀘스트가 시작되지 않은 상태에서 녹음을 시도하면
코딩 컨벤션 적용
ASpeakStageActor의 변수명을 camelCase로, RPC 함수명을ServerRPC_접두사 규칙에 맞게 수정하여 코드 품질을 향상시켰습니다.
Commit 정보
Summary (EN): refactor: simplify SpeakQuest to single-user model and fix state management
세부 내역:
"Refactor ASpeakStageActor to single-user model"
- Removed PlayerQueue and related logic.
- Renamed
PlayStarttoStartStageForPlayerfor clarity. - Simplified stage completion logic with
EndStagefunction. - Applied coding conventions (camelCase, RPC prefixes).
"Centralize quest state management in ASpeakStageActor"
- Removed redundant state variables (
bIsBusy,busyPlayerName) fromAWheatly. AWheatlynow relies onASpeakStageActor::GetCurrentSpeaker()as the single source of truth.- Visual state (eye color) in
AWheatlyis now synced viaTick.
- Removed redundant state variables (
"Improve user feedback for voice recording"
- Updated
UVoiceConversationSystemto show a clear message ("Start by talking to the NPC") when trying to record before the quest has started.
- Updated
Next To Do
- 데이터 모델 일관성 확보:
ASpeakStageActor가TArray대신FSpeakStageQuestion구조체를 사용하도록 리팩토링. - 네트워크 연동:
ASpeakStageActor가CreateTestScenarioData대신UKLingoNetworkSystem을 통해 실제 퀘스트 데이터를 받아오도록 수정. - UI 구현:
UMainWidget에 SpeakQuest 전용 UI(질문 표시, 진행 상황 등) 추가.
Compact Log 3
- 20251212 20:54
SpeakQuest 데이터 모델 통합 및 음성 재생 완성
Compact Log 2의 Next To Do #1, #2를 완료하고, SpeakQuest의 핵심 기능을 완성했습니다.
변경 파일:
Source/Onepiece/Game/Public/ASpeakStageActor.h: 하드코딩된 질문 데이터 제거 및 함수 시그니처 변경Source/Onepiece/Game/Private/ASpeakStageActor.cpp: PlayerState 기반 데이터 모델로 전면 리팩토링, 음성 재생 로직 추가
주요 작업:
데이터 모델 일관성 확보 (Next To Do #1)
TArray제거questions TArray제거questions_Voice CreateTestScenarioData()함수 완전 제거- 모든 질문 데이터를 PlayerState의
SpeakScenarioData.speak_quest_data(FSpeakStageQuestion 배열)에서 동적으로 가져오도록 변경 GetCurrentQuestion(),GetTotalQuestions(),AdvanceStep(),ShowCurrentQuestionToast()모두 PlayerState 기반으로 리팩토링
네트워크 연동 완료 (Next To Do #2)
- ASpeakStageActor는 이제 PlayerState의 데이터만 사용
- AWheatly의
RequestSpeakScenarioAPI 호출로 받아온 네트워크 데이터가 유일한 정보 소스(Single Source of Truth) - 테스트 데이터 의존성 완전 제거
다음 질문 음성 재생 로직 추가
AdvanceStep()에서 다음 질문으로 진행 시 자동으로PlayTTSAudio()호출- currentSpeaker의 Pawn을 통해 APlayerActor에 접근하여 음성 재생 요청
- FSpeakStageQuestion의 voice_data (Base64 인코딩된 음성 데이터) 사용
전체 워크플로우 완성
- ✅ NPC 상호작용 → API에서 질문 데이터 로드 (AWheatly)
- ✅ 첫 질문 음성 재생 (AWheatly::OnResponseSpeakScenario)
- ✅ 플레이어 녹음 및 답변 제출 (UVoiceConversationSystem)
- ✅ 평가 결과 표시 및 저장 (UVoiceConversationSystem::OnResponseSpeakingsJudges)
- ✅ 다음 질문으로 자동 진행 + 음성 재생 (ASpeakStageActor::AdvanceStep) ← 신규
- ✅ 모든 질문 완료 시 종료 처리 (ASpeakStageActor::EndStage)
기술적 세부사항
데이터 흐름:
1. AWheatly::RequestSpeakScenario
→ API 호출
→ PlayerState->SpeakScenarioData 저장
2. ASpeakStageActor::StartStageForPlayer
→ currentSpeaker 설정
→ ShowCurrentQuestionToast (첫 질문)
3. AWheatly::OnResponseSpeakScenario
→ PlayTTSAudio(speak_quest_data[0].voice_data) (첫 질문 음성)
4. 플레이어 녹음 → 평가 → ServerRPC_NotifyAnswerComplete
5. ASpeakStageActor::AdvanceStep
→ currentStepIndex++
→ PlayTTSAudio(speak_quest_data[currentStepIndex].voice_data) (다음 질문 음성)
→ ShowCurrentQuestionToast (다음 질문 Toast)
PlayerState 의존성:
GetCurrentQuestion():PS->SpeakScenarioData.speak_quest_data[currentStepIndex].GetQuestionMessage()GetTotalQuestions():PS->SpeakScenarioData.speak_quest_data.Num()AdvanceStep():PS->SpeakScenarioData.speak_quest_data.IsValidIndex(currentStepIndex)ShowCurrentQuestionToast(): 전체 질문 수를 PlayerState에서 동적으로 계산
Commit 정보
Summary (EN): refactor: integrate PlayerState data model and complete audio playback for SpeakQuest
세부 내역:
"Remove hardcoded test data from ASpeakStageActor"
- Removed
TArray,questions TArrayquestions_Voice - Removed
CreateTestScenarioData()function - Changed
BeginPlay()to skip test data generation
- Removed
"Refactor all data access to use PlayerState"
GetCurrentQuestion()now retrieves data fromPlayerState->SpeakScenarioDataGetTotalQuestions()dynamically calculates from PlayerStateAdvanceStep()andShowCurrentQuestionToast()use PlayerState as single source of truth
"Add automatic audio playback for next questions"
AdvanceStep()now callsPlayTTSAudio()with current question's voice_data- Audio playback triggered for all questions (not just the first one)
- Added include for
APlayerActor.h
"Complete end-to-end SpeakQuest workflow"
- All questions now have proper audio playback
- Seamless progression from question to question
- Network data integration complete
검증 완료 항목
Phase 3: 음성 시스템 - ✅ 완료
- ✅ 첫 질문 음성 재생 (AWheatly)
- ✅ 다음 질문 음성 재생 (ASpeakStageActor::AdvanceStep)
- ✅ 음성 녹음 시스템 (UVoiceConversationSystem::StartRecording)
- ✅ 턴 기반 녹음 제어 (StartRecording의 턴 체크 로직)
- ✅ STT 통합 (UVoiceConversationSystem::StopRecording → RequestSpeakingJudges)
- ✅ 평가 결과 처리 (OnResponseSpeakingsJudges)
남은 작업 (선택적):
- ⬜ UMainWidget SpeakQuest 전용 UI (현재는 Toast로 충분히 작동)
- ⬜ 질문 다시 듣기 버튼 (IA_Replay InputAction)
- ⬜ 진행률 표시 위젯 (선택적 개선사항)
결론
SpeakQuest의 핵심 기능(네트워크 데이터 연동, 음성 재생, 녹음 및 평가)이 모두 완성되었습니다. PlayerState를 Single Source of Truth로 사용하는 깔끔한 아키텍처로 리팩토링되었으며, 전체 워크플로우가 원활하게 동작합니다.