DEG-118 [Feat] GameManager에 대화창 접근 메서드 추가

This commit is contained in:
Sehyeon 2025-04-30 14:15:30 +09:00
parent 57419f5c01
commit a61f52fb10
9 changed files with 242 additions and 186 deletions

BIN
Assets/KSH/DungeonTestScene.unity (Stored with Git LFS)

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -73,9 +73,9 @@ Material:
- _OutlineWidth: 0 - _OutlineWidth: 0
- _PerspectiveFilter: 0.875 - _PerspectiveFilter: 0.875
- _Reflectivity: 10 - _Reflectivity: 10
- _ScaleRatioA: 1 - _ScaleRatioA: 0.8333333
- _ScaleRatioB: 1 - _ScaleRatioB: 0.6770833
- _ScaleRatioC: 1 - _ScaleRatioC: 0.6770833
- _ScaleX: 1 - _ScaleX: 1
- _ScaleY: 1 - _ScaleY: 1
- _ShaderFlags: 0 - _ShaderFlags: 0
@ -120,9 +120,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 71c1514a6bd24e1e882cebbe1904ce04, type: 3} m_Script: {fileID: 11500000, guid: 71c1514a6bd24e1e882cebbe1904ce04, type: 3}
m_Name: Galmuri9 SecondNew SDF m_Name: Galmuri9 SecondNew SDF
m_EditorClassIdentifier: m_EditorClassIdentifier:
hashCode: 0 hashCode: -1168553587
material: {fileID: -8204146546353023211} material: {fileID: -8204146546353023211}
materialHashCode: 0 materialHashCode: 851507981
m_Version: 1.1.0 m_Version: 1.1.0
m_SourceFontFileGUID: 9ff625240d1af4e419ba1a029337b761 m_SourceFontFileGUID: 9ff625240d1af4e419ba1a029337b761
m_SourceFontFile_EditorRef: {fileID: 12800000, guid: 9ff625240d1af4e419ba1a029337b761, m_SourceFontFile_EditorRef: {fileID: 12800000, guid: 9ff625240d1af4e419ba1a029337b761,

View File

@ -80,14 +80,7 @@
{ {
"id": "player_gameplay_3", "id": "player_gameplay_3",
"name": "주인공", "name": "주인공",
"text": "한다면... 설마 부와 명예를?", "text": "한다면... 뭐지?",
"nextId": "fairy_gameplay_4",
"phase": "gameplay"
},
{
"id": "fairy_gameplay_4",
"name": "냉장고 요정",
"text": "축하의 박수를 쳐줄게~",
"nextId": "", "nextId": "",
"phase": "gameplay" "phase": "gameplay"
}, },
@ -150,7 +143,7 @@
{ {
"id": "fairy_end_5", "id": "fairy_end_5",
"name": "냉장고 요정", "name": "냉장고 요정",
"text": "... 엔딩 크레딧 출력!", "text": "... GameManager.Instance.ShowCredit(GamePhase.End);",
"nextId": "", "nextId": "",
"phase": "end" "phase": "end"
} }

View File

@ -44,6 +44,7 @@ public class ChatWindowController : MonoBehaviour, IPointerClickHandler
[SerializeField] private TMP_Text chatText; [SerializeField] private TMP_Text chatText;
[SerializeField] private Image clickIndicator; [SerializeField] private Image clickIndicator;
[SerializeField] private GameObject chatWindowObject; // 대화 종료용 [SerializeField] private GameObject chatWindowObject; // 대화 종료용
[SerializeField] private AudioClip typingClip; // 타이핑 사운드
private Coroutine _typingCoroutine; private Coroutine _typingCoroutine;
private Coroutine _clickCoroutine; private Coroutine _clickCoroutine;
@ -53,8 +54,6 @@ public class ChatWindowController : MonoBehaviour, IPointerClickHandler
public delegate void OnComplete(); public delegate void OnComplete();
public OnComplete onComplete; public OnComplete onComplete;
private bool _dialogueEnded = false; // 대화 종료 여부
private FairyDialogueManager _dialogueManager; private FairyDialogueManager _dialogueManager;
private void Awake() private void Awake()
@ -65,48 +64,29 @@ public class ChatWindowController : MonoBehaviour, IPointerClickHandler
private void Start() private void Start()
{ {
// FairyDialogueManager 초기화
_dialogueManager = new FairyDialogueManager(this); _dialogueManager = new FairyDialogueManager(this);
// 완료 콜백 설정
onComplete = () => { onComplete = () => {
// 대화문 종료 call back
Debug.Log("대화가 완료되었습니다."); Debug.Log("대화가 완료되었습니다.");
}; };
// 테스트 코드: 인트로 대화 시작
Debug.Log("인트로 대화 시작");
_dialogueManager.SetGamePhase(GamePhase.Intro);
// 테스트 코드: 게임플레이 및 엔딩 대화 테스트를 위한 코루틴 시작
StartCoroutine(TestDialogueSequence());
} }
// 테스트 코드 // 외부 호출용 함수 (대화 시작)
private IEnumerator TestDialogueSequence() public void SetGamePhase(GamePhase phase)
{ {
// 인트로 대화가 끝날 시간을 대략적으로 기다림 _dialogueManager.SetGamePhase(phase);
yield return new WaitForSeconds(5f);
// Gameplay 상태라면 랜덤 대화 출력
// 게임플레이 단계로 전환 if (phase == GamePhase.Gameplay) _dialogueManager.ShowRandomGameplayDialogue();
Debug.Log("게임플레이 대화 시작");
_dialogueManager.SetGamePhase(GamePhase.Gameplay);
_dialogueManager.TalkToFairy();
// 3초 후 다른 게임플레이 대화 테스트
yield return new WaitForSeconds(3f);
_dialogueManager.StartDialogueById("fairy_gameplay_2");
// 3초 후 엔딩 대화 테스트
yield return new WaitForSeconds(3f);
Debug.Log("엔딩 대화 시작");
_dialogueManager.SetGamePhase(GamePhase.End);
} }
#region Show and Hide
// 대화창 표시 // 대화창 표시
public void ShowWindow() public void ShowWindow()
{ {
chatWindowObject.SetActive(true); chatWindowObject.SetActive(true);
_dialogueEnded = false;
if (_inputQueue.Count > 0) if (_inputQueue.Count > 0)
{ {
@ -134,12 +114,13 @@ public class ChatWindowController : MonoBehaviour, IPointerClickHandler
} }
} }
#endregion
// 대화 시퀀스 설정 // 대화 시퀀스 설정
public void SetDialogueSequence(List<DialogueStruct> sequence) public void SetDialogueSequence(List<DialogueStruct> sequence)
{ {
// 기존 큐 초기화 // 기존 큐 초기화
_inputQueue.Clear(); _inputQueue.Clear();
_dialogueEnded = false;
// 새 대화 시퀀스를 큐에 추가 // 새 대화 시퀀스를 큐에 추가
foreach (DialogueStruct dialog in sequence) foreach (DialogueStruct dialog in sequence)
@ -153,7 +134,8 @@ public class ChatWindowController : MonoBehaviour, IPointerClickHandler
{ {
if (_inputQueue.Count == 0) if (_inputQueue.Count == 0)
{ {
_dialogueEnded = true; HideWindow();
onComplete?.Invoke();
return; return;
} }
@ -191,6 +173,7 @@ public class ChatWindowController : MonoBehaviour, IPointerClickHandler
strText.Append(text[i]); strText.Append(text[i]);
chatText.text = strText.ToString(); chatText.text = strText.ToString();
yield return new WaitForSeconds(0.05f); yield return new WaitForSeconds(0.05f);
SoundManager.Instance.PlaySFX(typingClip); // 타이핑 사운드
} }
_clickCoroutine = StartCoroutine(ClickIndicatorCoroutine()); _clickCoroutine = StartCoroutine(ClickIndicatorCoroutine());
@ -217,8 +200,8 @@ public class ChatWindowController : MonoBehaviour, IPointerClickHandler
{ {
StopCoroutine(_typingCoroutine); StopCoroutine(_typingCoroutine);
_typingCoroutine = null; _typingCoroutine = null;
chatText.text = _inputText; if (chatText != null) chatText.text = _inputText;
_clickCoroutine = StartCoroutine(ClickIndicatorCoroutine()); if (clickIndicator != null) _clickCoroutine = StartCoroutine(ClickIndicatorCoroutine());
} }
else else
{ {
@ -228,23 +211,14 @@ public class ChatWindowController : MonoBehaviour, IPointerClickHandler
_clickCoroutine = null; _clickCoroutine = null;
} }
// 대화가 끝났으면 창 닫기
if (_dialogueEnded)
{
HideWindow();
onComplete?.Invoke();
return;
}
if (_inputQueue.Count > 0) // 대화가 남은 경우 if (_inputQueue.Count > 0) // 대화가 남은 경우
{ {
ShowNextDialogue(); ShowNextDialogue();
} }
else else
{ {
_dialogueEnded = true; // 일단 대화창 닫고 이후 콜백 호출 HideWindow(); // 대화 종료되면 창 닫기
} }
} }
} }
} }

View File

@ -4,25 +4,22 @@ using System.Linq;
using UnityEngine; using UnityEngine;
using Random = UnityEngine.Random; using Random = UnityEngine.Random;
// 일반 클래스로 변경된 FairyDialogueManager
public class FairyDialogueManager public class FairyDialogueManager
{ {
private ChatWindowController _chatWindow; private ChatWindowController _chatWindow;
private string _dialogueFileName; private string _dialogueFileName;
// 현재 게임 단계 private GamePhase _currentGamePhase = GamePhase.Intro; // 현재 게임 단계
private GamePhase _currentGamePhase = GamePhase.Intro;
private DialogueData _database; private DialogueData _database;
private Dictionary<string, DialogueStruct> _dialogueDict; private Dictionary<string, DialogueStruct> _dialogueDict;
// 단계별 대화 모음
private Dictionary<string, List<DialogueStruct>> _phaseDialogues; private Dictionary<string, List<DialogueStruct>> _phaseDialogues;
// 이미 보여준 게임플레이 대화 추적 // 이미 보여준 게임플레이 대화 추적
private HashSet<string> _shownGameplayDialogueIds; private HashSet<string> _shownGameplayDialogueIds;
private string _fairyName = "냉장고 요정";
// 생성자
public FairyDialogueManager(ChatWindowController chatWindow, string dialogueFileName = "dialogue.json") public FairyDialogueManager(ChatWindowController chatWindow, string dialogueFileName = "dialogue.json")
{ {
_chatWindow = chatWindow; _chatWindow = chatWindow;
@ -41,33 +38,15 @@ public class FairyDialogueManager
if (jsonFile == null) if (jsonFile == null)
{ {
Debug.LogError($"Failed to load dialogue database: {filePath}"); Debug.LogError($"경로 오류로 인한 대화 데이터 로딩 실패 : {filePath}");
_database = new DialogueData { dialogues = new List<DialogueStruct>() }; _database = new DialogueData { dialogues = new List<DialogueStruct>() };
return; return;
} }
Debug.Log($"JSON 파일 내용: {jsonFile.text.Substring(0, Mathf.Min(200, jsonFile.text.Length))}..."); // 파일 내용 확인
try { try {
_database = JsonUtility.FromJson<DialogueData>(jsonFile.text); _database = JsonUtility.FromJson<DialogueData>(jsonFile.text);
Debug.Log($"대화 항목 수: {_database.dialogues.Count}");
_dialogueDict = new Dictionary<string, DialogueStruct>(); // 대화 사전 초기화
// 검증: 각 단계별 대화 항목 수 확인
Dictionary<string, int> phaseCount = new Dictionary<string, int>();
foreach (var dialogue in _database.dialogues)
{
if (!phaseCount.ContainsKey(dialogue.phase))
phaseCount[dialogue.phase] = 0;
phaseCount[dialogue.phase]++;
}
foreach (var phase in phaseCount)
{
Debug.Log($"단계 '{phase.Key}': {phase.Value}개 항목");
}
// 대화 사전 초기화
_dialogueDict = new Dictionary<string, DialogueStruct>();
foreach (DialogueStruct entry in _database.dialogues) foreach (DialogueStruct entry in _database.dialogues)
{ {
_dialogueDict[entry.id] = entry; _dialogueDict[entry.id] = entry;
@ -95,12 +74,11 @@ public class FairyDialogueManager
} }
} }
// 게임 단계 설정 // 게임 단계 설정, GamePlay는 따로 실행
public void SetGamePhase(GamePhase phase) public void SetGamePhase(GamePhase phase)
{ {
_currentGamePhase = phase; _currentGamePhase = phase;
// Intro 또는 End 단계로 진입 시 자동으로 해당 대화 시작
if (phase == GamePhase.Intro) if (phase == GamePhase.Intro)
{ {
StartPhaseDialogue("intro"); StartPhaseDialogue("intro");
@ -120,13 +98,12 @@ public class FairyDialogueManager
return; return;
} }
// 해당 단계의 첫 대화 항목 찾기 (요정이 먼저 말하는 대화) // 첫 대화 찾기 (요정이 먼저 말함)
DialogueStruct startEntry = _phaseDialogues[phaseName] DialogueStruct startEntry = _phaseDialogues[phaseName]
.FirstOrDefault(d => d.name == "냉장고 요정" && !string.IsNullOrEmpty(d.nextId)); .FirstOrDefault(d => d.name == _fairyName && !string.IsNullOrEmpty(d.nextId));
if (startEntry == null) if (startEntry == null)
{ {
// 적합한 시작 대화가 없으면 첫 번째 대화 사용
startEntry = _phaseDialogues[phaseName][0]; startEntry = _phaseDialogues[phaseName][0];
} }
@ -134,18 +111,14 @@ public class FairyDialogueManager
StartDialogueSequence(startEntry.id); StartDialogueSequence(startEntry.id);
} }
// 랜덤 게임플레이 대화 보여주기 // Gameplay일 때 랜덤 대화 출력
public void ShowRandomGameplayDialogue() public void ShowRandomGameplayDialogue()
{ {
if (_currentGamePhase != GamePhase.Gameplay) if (_currentGamePhase != GamePhase.Gameplay) return;
{
Debug.LogWarning("Random dialogue can only be shown during gameplay phase");
return;
}
// 게임플레이 단계의 요정 대화 중 아직 보여주지 않은 것 찾기 // 요정 대화 중 아직 보여주지 않은 것 찾기
var availableDialogues = _phaseDialogues["gameplay"] var availableDialogues = _phaseDialogues["gameplay"]
.Where(d => d.name == "냉장고 요정" && !_shownGameplayDialogueIds.Contains(d.id)) .Where(d => d.name == _fairyName && !_shownGameplayDialogueIds.Contains(d.id))
.ToList(); .ToList();
// 모든 대화를 다 보여줬다면 다시 초기화 // 모든 대화를 다 보여줬다면 다시 초기화
@ -153,7 +126,7 @@ public class FairyDialogueManager
{ {
_shownGameplayDialogueIds.Clear(); _shownGameplayDialogueIds.Clear();
availableDialogues = _phaseDialogues["gameplay"] availableDialogues = _phaseDialogues["gameplay"]
.Where(d => d.name == "냉장고 요정") .Where(d => d.name == _fairyName)
.ToList(); .ToList();
} }
@ -174,17 +147,11 @@ public class FairyDialogueManager
// 대화 ID로 대화 시작 // 대화 ID로 대화 시작
public void StartDialogueSequence(string startDialogueId) public void StartDialogueSequence(string startDialogueId)
{ {
if (!_dialogueDict.ContainsKey(startDialogueId)) if (!_dialogueDict.ContainsKey(startDialogueId)) return;
{
Debug.LogError($"Dialogue ID not found: {startDialogueId}");
return;
}
// 시작 대화 항목 가져오기 DialogueStruct startEntry = _dialogueDict[startDialogueId]; // 시작 대화
DialogueStruct startEntry = _dialogueDict[startDialogueId];
// 대화 시퀀스 구성 List<DialogueStruct> sequence = BuildDialogueSequence(startEntry); // 대화 시퀀스 구성
List<DialogueStruct> sequence = BuildDialogueSequence(startEntry);
// 대화창에 대화 시퀀스 전달 // 대화창에 대화 시퀀스 전달
_chatWindow.SetDialogueSequence(sequence); _chatWindow.SetDialogueSequence(sequence);
@ -217,24 +184,7 @@ public class FairyDialogueManager
return sequence; return sequence;
} }
// 요정에게 말 걸기 (게임 중 언제든 사용 가능) // 특정 대화 ID로 대화 시작 (세이브/로드 시 사용)
public void TalkToFairy()
{
switch (_currentGamePhase)
{
case GamePhase.Intro:
StartPhaseDialogue("intro");
break;
case GamePhase.Gameplay:
ShowRandomGameplayDialogue();
break;
case GamePhase.End:
StartPhaseDialogue("end");
break;
}
}
// 특정 대화 ID로 대화 시작 (외부에서 호출 가능)
public void StartDialogueById(string dialogueId) public void StartDialogueById(string dialogueId)
{ {
if (_dialogueDict.ContainsKey(dialogueId)) if (_dialogueDict.ContainsKey(dialogueId))
@ -246,10 +196,4 @@ public class FairyDialogueManager
Debug.LogError($"Dialogue ID not found: {dialogueId}"); Debug.LogError($"Dialogue ID not found: {dialogueId}");
} }
} }
// 완료 콜백 설정
public void SetOnCompleteCallback(ChatWindowController.OnComplete callback)
{
_chatWindow.onComplete = callback;
}
} }

View File

@ -21,6 +21,8 @@ public partial class GameManager : Singleton<GameManager>
// 날짜 변경 이벤트, 추후에 UI 상의 날짜를 변경할 때 사용 // 날짜 변경 이벤트, 추후에 UI 상의 날짜를 변경할 때 사용
public event Action<int> OnDayChanged; public event Action<int> OnDayChanged;
private ChatWindowController chatWindowController; // 대화창 컨트롤러
private void Start() private void Start()
{ {
// 오디오 초기화 // 오디오 초기화
@ -39,6 +41,23 @@ public partial class GameManager : Singleton<GameManager>
} }
playerStats.OnDayEnded += AdvanceDay; playerStats.OnDayEnded += AdvanceDay;
} }
#region
public void StartNPCDialogue(GamePhase phase) // intro, gameplay, end 존재
{
if(chatWindowController == null)
SetChatWindowController();
chatWindowController.SetGamePhase(phase);
}
private void SetChatWindowController()
{
chatWindowController = FindObjectOfType<ChatWindowController>();
}
#endregion
// 날짜 진행 // 날짜 진행
public void AdvanceDay() public void AdvanceDay()

View File

@ -10,7 +10,7 @@ public enum EndingType
Happy // 던전 공략 O, 평판 기본 스탯 값 * 1.5 이상 Happy // 던전 공략 O, 평판 기본 스탯 값 * 1.5 이상
} }
public partial class GameManager : Singleton<GameManager> public partial class GameManager
{ {
private float happyEndReputation = 3.0f; private float happyEndReputation = 3.0f;
@ -23,6 +23,9 @@ public partial class GameManager : Singleton<GameManager>
// 엔딩 관련 메서드. 7일차에 실행 // 엔딩 관련 메서드. 7일차에 실행
private void TriggerTimeEnding() private void TriggerTimeEnding()
{ {
// npc와의 마지막 대화 출력
StartNPCDialogue(GamePhase.End);
// 플레이어 상태에 따라 엔딩 판별 // 플레이어 상태에 따라 엔딩 판별
EndingType endingType = DetermineEnding(); EndingType endingType = DetermineEnding();

View File

@ -3,7 +3,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
// 게임 매니저의 오디오 관련 부분 클래스 // 게임 매니저의 오디오 관련 부분 클래스
public partial class GameManager : Singleton<GameManager> public partial class GameManager
{ {
// 오디오 클립 참조 // 오디오 클립 참조
[Header("오디오 설정")] [Header("오디오 설정")]