이번에는 npc와의 상호작용과 이를 통해 npc와 대화를 할 수 있도록 만들어보자. 상호작용은 저번에 추가해준 InputAction을 통해 작동하도록 하자.
우선 대화 text는 Ink 라이브러리를 통해 만들어줄 것이다. Ink는 Asset Store를 통해 Import해주고 Inky라는 에디터를 통해 대화를 만들어주면 된다.
Inky는 다음 링크를 통해 다운받을 수 있다.
https://www.inklestudios.com/ink/
나는 상호작용 버튼을 눌렀을 때 레이케스트를 실행하고 만약 상호작용 가능한 객체라면 상호작용했을 때의 함수를 호출해주는 식으로 만들어보았다.
// 상호작용 콜백함수
public void Perform(InputAction.CallbackContext context)
{
InteractWithObject();
}
private void InteractWithObject()
{
DebugEx.Log($"InteractStarted");
RaycastHit2D hit = Physics2D.Raycast(transform.position, facingDirection, interactDistance, LayerMask.GetMask("Interactable"));
if (hit.collider != null)
{
IInteractable interactable = hit.collider.GetComponent<IInteractable>();
if (interactable != null)
{
interactable.Interact();
}
}
}
private void OnDrawGizmos()
{
// 레이케스트 시각화
Gizmos.color = Color.green;
Gizmos.DrawLine(transform.position, transform.position + (Vector3)facingDirection * interactDistance);
}
private void OnEnable()
{
playerInputActions.PlayerAction.Interact.performed += Perform;
playerInputActions.Enable();
}
그리고 이와 상호작용할 수 있도록 인터페이스를 하나 만들어주었다. 상호가능한 객체라면 이 인터페이스를 상속받게 하여 상호작용했을 때의 이벤트를 실행하도록 해주었다.
namespace Controllers.Entity
{
public interface IInteractable
{
/// <summary>
/// IInteractable의 필수 구현 사항
/// </summary>
void Interact();
}
}
대화창이 출력되고 대화에 대한 정보를 관리해줄 DialogueManager를 만들어주자.
using Ink.Runtime;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class DialogueManager : MonoBehaviour
{
[Header("NPC Data")]
private npcData npcdata;
public Story currentStory;
private const string SPEAKER_TAG = "speaker"; //테그값들 테그값 : 변수
private const string PORTRAIT_TAG = "portrait";
public UI_DialoguePopup popup;
public bool dialogueIsPlaying { get; private set; } //현재 대화창에 진입했는지 확인할 변수
private void Awake()
{
dialogueIsPlaying = false;
}
public void GetTalk2(TextAsset dialogue, npcData npc)
{
Player player = Managers.Game.GetPlayer().GetComponent<Player>();
npcdata = npc;
currentStory = new Story(dialogue.text);
popup = Managers.UI.ShowPopupUI<UI_DialoguePopup>();
//태그 초기화
dialogueIsPlaying = true;
ContinueStory();
}
public void ContinueStory()
{
if (currentStory.canContinue) //더 보여줄 이야기가 있다면
{
popup.displayNameText.text = npcdata.getName();
popup.dialogueText.text = currentStory.Continue(); // 한줄 출력
}
else
{
ExitDialogueMode();
}
}
private void ExitDialogueMode()
{
dialogueIsPlaying = false;
popup.dialogueText.text = "";
Managers.UI.ClosePopupUI();
}
}
그리고 npc에 관한 정보를 담고 이 상호작용 인터페이스를 상속받는 npcdata 클래스를 만들어주었다. 이때
using Controllers.Entity;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VInspector;
public class npcData : MonoBehaviour, IInteractable
{
[Tab("Visual Cue")]
[SerializeField] public GameObject[] visualCue;
[Tab("NPC Inform")]
[SerializeField] private int npcId;
[SerializeField] private string npcName;
[SerializeField] private bool isNpc;
[SerializeField] public Sprite[] npcPortrait;
[SerializeField] private TextAsset dialogue;
public bool playerInRange;
public string getName() { return npcName; }
private void Awake()
{
playerInRange = false;
foreach (GameObject cue in visualCue)
{
cue.SetActive(false);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
playerInRange = true;
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
playerInRange = false;
}
}
public void Interact()
{
if (playerInRange && !Managers.Dialogue.dialogueIsPlaying)
{
Managers.Dialogue.GetTalk2(dialogue, this);
}
else if (playerInRange && Managers.Dialogue.dialogueIsPlaying)
{
Managers.Dialogue.ContinueStory();
}
}
}
이렇게 해준 다음 대화창 이미지를 관리해줄 클래스도 만들어주자
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class UI_DialoguePopup : UI_Popup
{
[SerializeField] private GameObject DialoguePopup;
[SerializeField] public GameObject dialoguePanel;
[SerializeField] public TextMeshProUGUI dialogueText;
[SerializeField] public TextMeshProUGUI displayNameText;
[SerializeField] public Image portraitImage;
[SerializeField] public TextMeshProUGUI[] choicesText;
[SerializeField] public GameObject[] choices;
[SerializeField] public GameObject choicep;
[SerializeField] public Button[] choiceButton;
private bool isAction;
public override void Init()
{
DialoguePopup = this.gameObject;
dialoguePanel = DialoguePopup.transform.GetChild(0).gameObject;
dialogueText = dialoguePanel.transform.GetChild(0).gameObject.GetComponent<TextMeshProUGUI>();
displayNameText = dialoguePanel.transform.GetChild(3).GetChild(0).GetComponent<TextMeshProUGUI>();
portraitImage = dialoguePanel.transform.GetChild(4).GetComponent<Image>();
choicep = dialoguePanel.transform.GetChild(2).gameObject;
choices = new GameObject[2] { dialoguePanel.transform.GetChild(2).GetChild(0).gameObject, dialoguePanel.transform.GetChild(2).GetChild(1).gameObject };
choicesText = new TextMeshProUGUI[2] { choices[0].GetComponentInChildren<TextMeshProUGUI>(), choices[1].GetComponentInChildren<TextMeshProUGUI>() };
choiceButton = new Button[2] { choices[0].GetComponent<Button>(), choices[1].GetComponent<Button>() };
}
private void Awake()
{
Init();
}
}
이렇게 해주면 대화창이 올바르게 출력되는 것을 볼 수 있다.
'게임공부 > Unity' 카테고리의 다른 글
[C#][Unity][나만의 탑뷰 게임 만들기]1. 이동 및 타일맵 (1) | 2024.10.04 |
---|---|
[Unity][C#]개발꿀팁 모음 (1) | 2024.06.15 |
[Unity]게임 만들어보기 0장. 기획 (0) | 2024.02.06 |
[Unity]게임테일러-우리만의 게임으로 바꿔보기 (3) | 2023.11.26 |
0장.뱀서라이크 게임으로 난이도 최적화모델 만들기 (0) | 2023.11.04 |