티스토리 뷰

🎨/Unity

[Unity] NPC와 대화하기 (2)

eungding 2022. 2. 18. 12:34
반응형

[Unity] NPC와 대화하기 (1) 에서 이어집니다. 

 

⭐️ 목표 ⭐️ 

- NPC와 대화 중에는 플레이어의 이동을 막는다. 

- 다른 Object와 소통하는 다양한 방법을 알아본다.  (SendMessage ,  UnityEvent )

 

플레이어의 이동을 막기 위해

moveSpeed를 0으로 설정해주겠습니다. 

 

[1] 

첫번째 방법은 NPCAreaController에 ThirdPersonController를 주입받아서 moveSpeed를 0으로 직접 설정해주는 방법입니다. 

에디터에서 controller 필드에 플레이어를 설정해주면 됩니다. 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using StarterAssets;

public class NPCAreaController : MonoBehaviour
{
    public GameObject dialogPanel;
    public GameObject joysticksCanvas;
    public ThirdPersonController controller;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Player")
        {
            controller.MoveSpeed = 0;
            joysticksCanvas.SetActive(false);
            dialogPanel.SetActive(true);
        };
    }
}

 

[2]

두번째 방법은 SendMessage 를 이용하는 방법입니다. 

특정 게임오브젝트한테 message를 날리면

그 게임오브젝트에 붙어있는 스크립트가 message를 받아서 처리할 수 있습니다. 

 

이렇게 NPCAreaController에서 Player한테 StopMoving이라는 메세지를 날리고

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using StarterAssets;

public class NPCAreaController : MonoBehaviour
{
    public GameObject dialogPanel;
    public GameObject joysticksCanvas;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Player")
        {
            other.gameObject.SendMessage("StopMoving");
            joysticksCanvas.SetActive(false);
            dialogPanel.SetActive(true);
        };
    }
}

 

ThirdPersonController에서는 해당 메세지를 받아서 자신의 필드인 MoveSpeed 를 0으로 직접 세팅할 수 있습니다. 

 public class ThirdPersonController : MonoBehaviour 
 {
 
    public float MoveSpeed = 2.0f;
    ...
    public void StopMoving()
    {
        Debug.Log("StopMoving!");
        MoveSpeed = 0;
    }
}

 

 

[3]

세번째 방법은 UnityEvent 를 이용하는 방법입니다. 

유니티이벤트에 여러 콜백을 등록해둘 수 있고 Invoke 를 콜하면 등록한 콜백들이 불리게 됩니다.

이 글의 설명을 참고하시면 좋습니다. 

 

UnityEngine.Events 를 임포트해주고 

onPlayerEntered 라는 UnityEvent를 만듭니다.

그리고 Player가 NPCArea 영역에 들어오면 해당 이벤트를 Invoke 합니다. 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;

public class NPCAreaController : MonoBehaviour
{
    public GameObject dialogPanel;
    public GameObject joysticksCanvas;

    public UnityEvent onPlayerEntered;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Player")
        {
            onPlayerEntered.Invoke();
            joysticksCanvas.SetActive(false);
            dialogPanel.SetActive(true);
        };
    }
}

 

이렇게 스크립트를 작성해주고 유니티 에디터로 돌아와보면

추가한 유니티이벤트 필드가 나옵니다. 

 

 

+ 를 누르고 Player를 끌어와준 후, Fuction에서 

StopMoving을 선택해줍니다. 

 

 

그럼 OnPlayerEntered에 대한 콜백을 등록한 것이고,

이 이벤트가 불리면 StopMoving 함수가 불리게 될 것입니다. 

 

참고로 + 를 눌러서 다른 Object의 함수도 여러개 등록할 수도 있답니다!

 

 

 

저는 유알못이지만.. 유니티 하시는 분들은 이 방법을 가장 선호하시는 것 같아요!

(제가 봐도 이게 가장 깔끔하고 유지보수성이 좋은 것 같아요)

그래서 저도 NPC Controller 를 아래와 같이 바꾸고 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class NPCAreaController : MonoBehaviour
{
    public UnityEvent onPlayerEntered;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Player")
        {
            onPlayerEntered.Invoke();
        };
    }
}

 

 

직접 오브젝트를 참조해서 부르던 setActive 들을 모두 콜백으로 등록하도록 바꿔줬어요! 

 

 

 

[ 기록 ]

원하는 스펙

 

1. NPC와 대화 중

- DialogPanel 보이게 

- 모바일 조이스틱 안보이게

- 플레이어 움직일 수 없게

 

2. NPC와 대화가 끝나면

- DialogPanel 안보이게  

- 모바일 조이스틱 보이게

- 플레이어 움직이게

 

DialogController를 아래처럼 작성해줬고 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;

public class DialogController : MonoBehaviour
{
    public Text dialogText;

    public UnityEvent onDialogFinished;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void StartDialog()
    {
        dialogText.text = "";
        string sampleText = "안녕하세요 지니지니테스트 님,\n무엇을 도와드릴까요?";
        StartCoroutine(Typing(sampleText));
    }

    public void FinishDialog()
    {
        gameObject.SetActive(false);
        StopAllCoroutines();
        onDialogFinished.Invoke();
    }

    IEnumerator Typing(string text)
    {
        foreach (char letter in text.ToCharArray())
        {
            dialogText.text += letter;
            yield return new WaitForSeconds(0.1f);
        }

        // TODO
        yield return new WaitForSeconds(0.5f);
        FinishDialog();
    }
}

 

NPCController는 아래와 같고

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class NPCAreaController : MonoBehaviour
{
    public UnityEvent onPlayerEntered;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Player")
        {
            onPlayerEntered.Invoke();
        };
    }
}

 

ThirdPersonController에 이 함수를 추가해줬어요  (위에서 설명한 StopMoving은 지워줌) 

public void SetMoveSpeed(float speed)
{
    Debug.Log("setMoveSpeed");
    MoveSpeed = speed;
}

 

콜백 등록은 이렇게 해줬습니다. 

 

 

 

 

[ 모바일 이슈 대응 ]

모바일 조이스틱을 setActive(false) 했다가 다시 setActive(true) 로 바꾸면 

마지막 드래그된 포지션으로 조이스틱이 가있고 플레이어 계속 움직이게 됩니다. 

 

 

그래서 StarterAssets의 UIVirtualJoystick 스크립트에 

Reset 함수를 추가해줬습니다.

조이스틱 UI의 포지션을 zero 로 해줘서 조이스틱 UI가 센터로 오게 해주고

OutputEvent도 zero로 날려줘서 플레이어를 멈춰주는 코드입니다.  

    public void Reset()
    {
        UpdateHandleRectPosition(Vector2.zero);
        joystickOutputEvent.Invoke(Vector2.zero);
    }

 

그리고 NPCAreaController 스크립트의 OnPlayerEntered 이벤트에 

Reset을 콜백으로 등록해줍니다.

 

 

저는 Drag 해서 움직이는 툴인 Move이랑 Look 두개 다 등록해줬습니다.

 

 

그럼 이제 잘 동작합니다. (NPC와 대화가 끝나고 조이스틱이 디폴트 상태로 되어있고 플레이어가 멈춰있다면 성공임) 

 

 

 

이거 말고 다른 방법이 있을 것 같아서 StarterAsset을 한참 살펴봤지만, 잘 모르겠네요 🥲

 

 

 

 

 

 

반응형

'🎨 > Unity' 카테고리의 다른 글

[Unity] Coroutines 코루틴  (0) 2022.02.20
[Unity] UnityWebRequest / JsonUtility  (0) 2022.02.18
[Unity] Xcode Simulator로 빌드하기  (0) 2022.02.17
[Unity] NPC와 대화하기 (1)  (0) 2022.02.17
[Unity] NPC와 충돌감지하기  (0) 2022.02.17
댓글