게임개발/드림랜드

2024.09.04 npc 상호작용

서지현 2024. 9. 4. 15:32

 

 

플레이어가 NPC를 인식해야한다.

 - NPCTrigger 라는 스크립트를 만들어 player에게 붙이고, npc와 player에게 콜라이더 컴포넌트를 만들어줬다.

 

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

public class NPCTrigger : MonoBehaviour
{

    public delegate void NPCDetected(Transform npc);
    public static event NPCDetected OnNPCDetected;

    public delegate void NPCExited(Transform npc);
    public static event NPCExited OnNPCExited;


    private void OnTriggerEnter(Collider other)

    {

        if (other.CompareTag("NPC"))
        {


            
            OnNPCDetected?.Invoke(other.transform);


        }


    }

    private void OnTriggerExit(Collider other)
    {
        if (other.CompareTag("NPC"))
        {


            
            OnNPCExited?.Invoke(other.transform);


        }
    }


}

 

하나하나 살펴보자.

 

public delegate void NPCDetected(Transform npc);
public static event NPCDetected OnNPCDetected;

public delegate void NPCExited(Transform npc);
public static event NPCExited OnNPCExited;

 

Delegate : delegate는 '특정 형식'의 메서드를 참조할 수 있는 타입이다. 여기서는 'Transform'을 매개변수로 받아들이는 메서드를 가리킬 수 있는 NPCDectected와 NPCExited라는 두개의 델리게이트가 정의되었다.

Event : Event 키워드는 델리게이트 타입을 기반으로 이벤트를 선언한다.

OnDectected와 OnNPCExited이벤트는 각각 NPC와의 충돌이 시작되고 종료될때 발생된다.

 

private void OnTriggerEnter(Collider other)
{
    if (other.CompareTag("NPC"))
    {
        OnNPCDetected?.Invoke(other.transform);
    }
}

 

OnTriggerEnter(Collider other) : 이 메서드는 이 스크립트가 붙어있는 객체가 다른 Collider와 충돌할 때 호출된다. 충돌한 객체의 Collider를 other로 받는다.

other.CompareTag("NPC") : 충돌한 객체의 태그가 NPC인지 확인한다. 태그가 NPC인 경우에만 해당 충돌을 처리한다.

Invoke: 이벤트가 발생했을 때 연결된 메서드를 호출하는 역할을 한다.

private void OnTriggerExit(Collider other)
{
    if (other.CompareTag("NPC"))
    {
        OnNPCExited?.Invoke(other.transform);
    }
}

 

ONTriggerExit(Collider other) : 이 메서드는 충돌이 종료될때 호출된다. other는 충돌이 종료된 객체의 Collider를 나타낸다.

 

한 가지 문제가 생겼다. 그건 카메라가 상호작용을 하고있는데도 NPC를 바라보지 않는 문제였는데,

MainCameraAction 스크립트가 그걸 방해하고있는듯 했다. 두 스크립트가 충돌하는 상황이다.

 

챗 gpt는 두가지 대안을 제시해주었는데, 첫번째는 MainCameraAction 스크립트를 비활성화하는 방법이다.

 

private MainCamera_Action mainCameraAction; // MainCamera_Action 스크립트

private void Start()
{
    // MainCamera_Action 스크립트를 찾아서 참조합니다.
    mainCameraAction = mainCamera.GetComponent<MainCamera_Action>();

    originalCameraPosition = mainCamera.transform.position;
    originalCameraRotation = mainCamera.transform.rotation;
}

private void StartInteraction(Transform npc)
{
    if (mainCameraAction != null)
    {
        mainCameraAction.enabled = false; // MainCamera_Action 스크립트 비활성화
    }

    isInteracting = true;
    Vector3 npcPosition = npc.position;
    Vector3 npcForward = npc.forward;

    Vector3 cameraPosition = npcPosition + npcForward * 1.5f + Vector3.up * 1.5f;
    mainCamera.transform.position = cameraPosition;
    mainCamera.transform.LookAt(npc);

    Debug.Log($"카메라 위치 변경: {cameraPosition}");
    Debug.Log($"카메라 방향 변경: {npc.name}");
}

private void EndInteraction()
{
    isInteracting = false;
    mainCamera.transform.position = originalCameraPosition;
    mainCamera.transform.rotation = originalCameraRotation;

    if (mainCameraAction != null)
    {
        mainCameraAction.enabled = true; // MainCamera_Action 스크립트 활성화
    }

    Debug.Log($"카메라 복원: {originalCameraPosition}");
}

 

 

성공적이었다! 하지만, 다만, 한 가지 더 오류가 있었다. NPC와 한번 상호작용 하고나면 연속해서 상호작용 할 수가 없었다.

 

알고보니 마지막 EndInteraction에서 isInteractin을 true로 오타낸 내 잘못이었다...

 

    private void EndInteraction()
    {
        isInteracting = false;
        // 카메라를 원래 위치와 회전으로 복원합니다.
        mainCamera.transform.position = originalCameraPosition;
        mainCamera.transform.rotation = originalCameraRotation;


        if (mainCameraAction != null)
        {


            mainCameraAction.enabled = true; // 스크립트 활성화



        }

        Debug.Log($"카메라 복원: {originalCameraPosition}");

    }
}

 

이벤트가 끝나면 isInteracting이 false가 되도록 해주자.

 

 

NPC 카메라 앵글 변경

 

 

    private void StartInteraction(Transform npc)
    {
        

        if ( mainCameraAction != null )
        {


            mainCameraAction.enabled = false; // 스크립트 비활성화



        }


        isInteracting = true;
        Vector3 npcPosition = npc.position;
        Vector3 npcForward = npc.forward;
        Vector3 npcRight = npc.right;

        Vector3 cameraPosition = npcPosition + npcForward * 1.5f + Vector3.up * 1.3f + npcRight * -1.0f;
        mainCamera.transform.position = cameraPosition;
        mainCamera.transform.LookAt(npc);


        Quaternion CameraRotation = Quaternion.LookRotation(npc.position + Vector3.up * 1.2f - cameraPosition);
        mainCamera.transform.rotation = CameraRotation;


        Debug.Log($"카메라 위치 변경: {cameraPosition}");
        Debug.Log($"카메라 방향 변경: {npc.name}");
    }

    private void EndInteraction()
    {
        isInteracting = false;
        // 카메라를 원래 위치와 회전으로 복원합니다.
        mainCamera.transform.position = originalCameraPosition;
        mainCamera.transform.rotation = originalCameraRotation;


        if (mainCameraAction != null)
        {


            mainCameraAction.enabled = true; // 스크립트 활성화



        }

        Debug.Log($"카메라 복원: {originalCameraPosition}");

    }
}

 

예전부터 나왔던 Quarternion을 살펴보자.

 

Quarternion.LookRotation : LookRotation 함수는 두 점 사이의 방향 벡터를 받아 그 방향으로 카메라가 바라보도록 회전값을 계산해준다.

 

https://docs.unity3d.com/kr/2021.3/Manual/class-Quaternion.html

 

중요 클래스 - Quaternion - Unity 매뉴얼

Unity는 Quaternion 클래스를 사용하여 게임 오브젝트의 3차원 방향을 저장하고, 이를 통해 한 방향에서 다른 방향으로의 상대 회전을 설명합니다.

docs.unity3d.com

 

 

 

이제 카메라 각도는 된거같다. 이제, 대화하는 동안 캐릭터가 움직이는걸 제어하면 된다.