반응형
UniRX를 공부한 김에 이를 이용한 npc와 대화 로직을 만들었습니다.
유니티 RPG게임이나 추리게임에서 유용하게 사용할 수 있는 코드입니다.
프로그램은 다음과 같이 구성하였습니다.
1. NPC 가까이 가면 대화할 수 있음 ( InAreaNPC 메서드 )
2. 스페이스바를 누르면 대화창이 뜨고 대화 시작
3. 대화문장은 한글자씩 천천히 출력됨
4. 만약 이를 기다리기 싫다면 스페이스바를 한번 더 눌려서 전체 문장을 한번에 볼 수 있음
5. 해당 문장을 보기 싫다면 스페이스바를 더블클릭하여 다음 대화로 스킵가능
6. 대화가 끝나거나 대화도중 npc영역에서 벗어나면 대화 끝
7. 대화가 끝나면 무조건 대화는 처음부터 시작
게임을 좋아하시는 분들이라면 익숙한 동작이시죠?
이를 구현하면 다음과 같습니다.
using System.Collections;
using UnityEngine;
using UniRx;
using UniRx.Triggers;
using System;
using System.Linq;
public class TalkManager : MonoBehaviour
{
NPCInfo npcOBJ;
public float setTalkSpeed = 0.1f;
WaitForSeconds talkSpeed;
bool isTalkToAnyNPC = false;
bool isTalking = false;
IObservable<Unit> clickStream;
void Start()
{
talkSpeed = new WaitForSeconds(setTalkSpeed);
// 대화 스트림
// 대화가능 상태에서 트리거버튼을 누르면 대화 시작
// StopCoroutine으로 기존 대화중이던 내용 삭제
clickStream = this.UpdateAsObservable().Where(_ => (Input_Z.Is_Down_Left_Trigger() || Input_Z.Is_Down_Right_Trigger() || Input.GetKeyDown("space")))
.Publish() // Hot 변환
.RefCount(); // Observer 추가시 자동 Connect
clickStream
.Where(_ => isTalkToAnyNPC && !isTalking)
.Subscribe(_ => { StartCoroutine("TalkTalk"); });
clickStream // 한번 클릭
.Buffer(clickStream.Throttle(TimeSpan.FromMilliseconds(150)))
.Where(x => x.Count < 2 && isTalking)
.Subscribe(_ => isClicked = true);
clickStream // 더블 클릭
.Buffer(clickStream.Throttle(TimeSpan.FromMilliseconds(150)))
.Where(x => x.Count >= 2 && isTalking)
.Subscribe(_ => isDoubleClicked = true);
}
// 대화 클릭감지를 위한 변수들
bool isClicked = false;
bool isDoubleClicked = false;
IEnumerator TalkTalk()
{
// 대화창 초기화
npcOBJ.talkContent.text = "";
npcOBJ.canvas_TalkWindow.SetActive(true);
isTalking = true;
int currContentNum = 0;
while (true) // 할말없으면 break
{
yield return null;
while (true) //한 마디가 끝나거나 더블클릭, 클릭 후 클릭하면 break
{
yield return null;
if (isClicked || isDoubleClicked) break;
}
DoubleClicked:
isClicked = false;
isDoubleClicked = false;
npcOBJ.talkContent.text = "";
// 한글자씩 대화 출력
// 클릭시 대화 전체출력
// 더블클릭시 다음 대화로 이동
foreach (char c in npcOBJ.talkContentList[currContentNum])
{
npcOBJ.talkContent.text += c;
yield return talkSpeed;
if (isDoubleClicked && currContentNum < (npcOBJ.talkContentList.Count -1))
{
currContentNum++;
goto DoubleClicked;
}
if (isClicked || isDoubleClicked) break;
}
npcOBJ.talkContent.text = npcOBJ.talkContentList[currContentNum];
// 한마디 끝, 클릭초기화 후 다음 이벤트 대기
currContentNum++;
isClicked = false;
isDoubleClicked = false;
if (currContentNum >= npcOBJ.talkContentList.Count) break;
}
while (true) // 씬 이동전 클릭대기
{
yield return null;
if (isClicked || isDoubleClicked) break;
}
// 대화 후 추가적인 로직
}
public void InAreaNPC(NPCInfo _npcOBJ)
{
// NPC 앞으로 들어가면 true 나오면 false
if (isTalkToAnyNPC == true) return;
npcOBJ = _npcOBJ;
isTalkToAnyNPC = true;
npcOBJ.canvas_CanTalk.SetActive(true);
// NPC 애니메이션 동작
}
public void OutAreaNPC(NPCInfo _npcOBJ)
{
// NPC 앞으로 들어가면 true 나오면 false
if (isTalkToAnyNPC == false) return;
npcOBJ = _npcOBJ;
isTalkToAnyNPC = false;
npcOBJ.canvas_CanTalk.SetActive(false);
// 대화 초기화
StopCoroutine("TalkTalk");
npcOBJ.canvas_TalkWindow.SetActive(false);
isTalking = false;
isClicked = false;
isDoubleClicked = false;
}
}
NPCINfo 클래스에서 LIST<string> talkContent 에 원하는 대화를 순서대로 집어넣으면 됩니다.
모든 NPC에 이 클래스를 넣고 스트림을 만드는 것은 정말 비효율적이라 생각해서
모든 NPC가 공유할 수 있는 한개의 TalkManager 코드를 제작하였습니다.
이보다 더 좋은 예제가 있을 수 있습니다.
특히 goto문 혐오자분들이 싫어하시는 코드도 포함되있죠 ㅎㅎ..(전 이정도는 좋은 활용법이라 생각합니다.)
이 코드보다 더 효율적인 방법이 있다고 생각하면 그 방법을 사용하시면 됩니다.
이건 그냥 참고용으로만 쓰시죠.
그리고 더 좋은 방법이 있다면 댓글로 공유해주세요!!
반응형
'유니티 > 스크립트' 카테고리의 다른 글
유니티 Rest API 통신 UnityWebRequest (0) | 2021.07.01 |
---|---|
유니티 특정 시간동안 조건을 유지할 경우 (0) | 2020.12.25 |
[유니티]공 이동거리 계산 프로그램 (0) | 2020.08.02 |
[유니티]오브젝트 순차적 색상변경 (0) | 2020.08.02 |