코드는 일부 기능만 정리하고 전반적인 흐름 위주로 정리하고자 한다. Dialogue System과 Quest System을 연동해 크게 다음의 과정을 진행하고자 한다.
NPC와 상호작용을 통한 퀘스트 받기
플레이어가 현재 퀘스트를 가지고 있는 NPC와 상호작용을 시도하면 NPC는 해당되는 퀘스트 dialogue를 dialogue system에 넘겨 UI로 보여준다. 해당 Dialogue가 모두 UI로 보여지고 나면 Dialogue가 끝났다고 알려주고 NPC Trigger는 이후 퀘스트 수락 메시지를 UI Controller를 통해 보여준다. 플레이어가 수락하면 퀘스트가 시작되며, 이때 Quest Manager의 진행 중 퀘스트 목록이 업데이트 되며 UI Controller는 현재 관련된 퀘스트 목록 UI를 업데이트한다.
퀘스트 수행 및 퀘스트 완료
플레이어가 퀘스트와 관련된 오브젝트와 상호작용을 시도하면 퀘스트 매니저 내의 진행 중인 퀘스트 리스트에서 퀘스트의 목표 물체와 동일한지 확인한다. 동일한 경우 퀘스트의 상태를 업데이트 해주며 최종 목표 값에 도달하면 퀘스트 매니저는 NPC에게 퀘스트를 완료 가능한 상태로 변경하도록 요청한다. 이후 플레이어가 NPC에게 대화를 시도하면 퀘스트 완료 메시지를 Dialogue System으로 넘겨 UI로 보여준다. Dialouge가 끝나면 NPC Trigger은 퀘스트 완료 메시지를 UI Controller를 통해 보여준다. 확인 버튼을 누르면 퀘스트가 완료되며 UI Controller의 퀘스트 목록 UI를 업데이트하고 현재 진행중인 퀘스트 목록에서 해당 퀘스트를 제거한다.
다음 시작 가능한 퀘스트 업데이트
퀘스트 1번이 끝났을 때 다음으로 시작 가능한 퀘스트의 번호 정보를 기반으로 다음 시작 가능한 퀘스트를 업데이트 한다. 해당 퀘스트들은 Quest Manager에게 시작 가능한 상태라고 알려주고, Quest Manager는 관련된 NPC들에게 퀘스트를 지정해준다.
퀘스트는 기본적으로 다음의 구성요소를 가진다.
Quest Id: 퀘스트 고유 식별 정보
Next Quest Id: 다음으로 진행될 퀘스트 id 목록
Quest Name: 퀘스트 이름
Quest Type: 퀘스트 종류
NPC Id: 관련 NPC 이름
Target: 퀘스트 진행시 필요한 오브젝트 이름 (몬스터명, 아이템명 등)
Progress Num: 진행 수
Goal Num: 목표 수
Reward: 퀘스트 완료 시 보상
추가로 퀘스트는 다음의 행동을 수행한다.
퀘스트 매니저는 다음의 작업을 수행한다.
quest id들을 넘겨받았을 때 퀘스트를 시작 가능 상태로 만든다.
public void MakeQuestStartable(string[] questIds)
{
possibleQuestIds.Add(questId);
Quest currentQuest = QDataManager.instance.questDict[questId];
//refactoring 필요
NPCTrigger currentNPC = GameObject.Find("NPCs").transform.Find(currentQuest.npcId).GetComponent<NPCTrigger>();
currentNPC.AddStartableQuest(currentQuest);
}
quest id를 넘겨받았을 때 퀘스트를 완료 가능 상태로 만든다.
public void MakeQuestFinishable(string questId)
{
Quest quest = QDataManager.instance.questDict[questId];
NPCTrigger currentNPC = GameObject.Find("NPCs").transform.Find(quest.npcId).GetComponent<NPCTrigger>();
currentNPC.MakeQuestFinishable();
inProgressQuestList.Remove(quest);
}
현재 제거하거나 수집한 오브젝트가 발생할 때 다음의 함수가 호출된다. 해당 함수는 목표로 둔 오브젝트와 동일한 경우 퀘스트의 progress를 update해준다.
public void UpdateQuestProgress(string name)
{
for (int i = 0; i < inProgressQuestList.Count; i++)
{
Quest inProgressQuest = inProgressQuestList[i];
if (inProgressQuest.IsTarget(name))
{
inProgressQuest.UpdateProgress();
}
}
}
(큐로 받아서 npc가 여러 퀘스트를 동시에 지니는 케이스들에 대한 문제점 해결 -> 추후 포스트 예정)
Npc는 퀘스트가 있는 경우 퀘스트와 관련된 대화를 시도하며 Quset Manager가 currentQuest를 할당했는지 아닌지 여부로 결정된다.
public virtual void Interact()
{
if (currentQuest != null)
{
// quest 대화 실행
}
else
{
// 기본 대화 실행
}
}
Dialogue Manager에 string 매개변수를 받는 OnDialogueEnd 이벤트를 정의한다. 현재 대화를 시도한 NPC 이름을 넘겨받아 Dialogue가 끝날 때 해당 string 값으로 호출한다.
public void AddStartableQuest(Quest quest)
{
currentQuest = quest; // quest 할당
// Dialogue 끝나면 퀘스트 수락 메시지 띄우기 위함
DialogueManager.instance.OnDialogueEnd.AddListener(ShowQuestAcceptMessage);
}
private void ShowQuestAcceptMessage(string name)
{
if (name == npcName)
{
UIController.instance.ShowMessage(MessageType.YesOrNo, currentQuest.questName, AcceptQuest);
}
}
private void AcceptQuest()
{
currentQuest.StartQuest();
DialogueManager.instance.OnDialogueEnd.RemoveListener(ShowQuestAcceptMessage);
}
퀘스트 시작과 비슷한 로직으로 동작한다.
FinishQuest 호출 후 동일한 NPC에게 새로운 currentQuest가 할당될 수 있기 때문에 현재의 currentQuest가 새롭게 할당된 것인지 아니면 끝난 퀘스트인지 확인 후 null 여부를 결정하도록 작성했다.
public void MakeQuestFinishable()
{
DialogueManager.instance.OnDialogueEnd.AddListener(ShowQuestFinishMessage);
}
private void ShowQuestFinishMessage(string name)
{
if (name == npcName)
{
UIController.instance.ShowMessage(MessageType.Confirm, currentQuest.reward.ToString(), FinishQuest);
}
}
private void FinishQuest()
{
currentQuest.FinishQuest();
DialogueManager.instance.OnDialogueEnd.RemoveListener(ShowQuestFinishMessage);
if (currentQuest.IsFinished())
{
currentQuest = null;
}
}
퀘스트의 상태가 업데이트 되면 자동으로 현재 퀘스트 목록을 업데이트 하고자 한다.
우선 Scene에서는 Scroll View를 통해 여러 퀘스트가 목록에 보여지도록 만들었다.
퀘스트 UI는 현재의 퀘스트가 시작 가능한지 완료 가능한지에 대한 상태를 파악해 목록에 추가할지 제거할지 결정한다. 해당 함수는 UI Controller의 이벤트에 등록하고 관련 퀘스트들이 Invoke 해주는 방식으로 사용된다.
void Start()
{
// ui 정보 캐싱 (중략) ..
OnQuestUIUpdate.AddListener((int status, string title) =>
{
UpdateQuestUI(status, title);
});
}
private void UpdateQuestUI(int status, string questTitle)
{
if (status == 0)
{
TextMeshProUGUI questTitleText = Instantiate(questInfo, questTransform).GetComponent<TextMeshProUGUI>();
questTitleText.gameObject.name = questTitle;
questTitleText.text = questTitle;
}
else
{
Destroy(questTransform.Find(questTitle).gameObject);
}
}
UI를 생성하거나 보여주는 역할을 담당한다.
지금 만들면서 부족한 부분이 있다고 생각이 들었다. 다음 요소들을 고려해서 업그레이드 해볼 수 있을 것 같다.