ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스파르타 3D 서바이벌게임 / 상호작용기능 만들기 / 유니티 숙련주차
    스파르타 게임 개발 2024. 6. 4. 02:19

    우린 마우스를 아이템이나 특정 오브젝트들에게 갖다 대면 클릭이나 어떤 것을 할 수 있게 하고 상호작용이 일어나게 할 것인데 이 기능을 Ray를 통해 구현 할 것이다. 화면 중앙에 계속 해서 Ray를 쏴서 아이템에 충돌이 되면 그 아이템에 관련 된 기능들이 실행이 되게 할 것이다. 그래서 화면 중앙에 계속 해서 Ray를 쏘는 Class를 만들어 주자. 

     

    Interaction이란 이름으로 클래스를 만들어 주겠다. 

     

    Ray를 계속 쏘게 하기 위해서 몇 가지 변수들이 필요하다. Ray를 Update에서 계속 쏘게 할 것인데 이거를 매 프레임마다 계속해서 쏘게 되면 성능에 문제가 생길 것이다. 그래서 변수들을 만들어서 특정 시간마다 Ray를 쏘게 해준다. 

     

    그래서 먼저 지정해둔 시간마다 Ray를 쏘게 해줄 변수를 만들어 준다. 

     

     public float checkRate = 0.05f;
     private float lastCheckTime;

     

    라는 변수 2개를 만들어 준다. 이 2개의 변수를 가지고 Ray가 시간마다 쏘게 해줄 것이다. checkRate 0.05f의 값으로 쏘게 해줄 건데 0.05초마다 Ray를 쏘는 것이다. 활용 하는 법은 이렇다. 

     

    if (Time.time - lastCheckTime > checkRate)
    {
        lastCheckTime = Time.time;

    }

     

    이렇게 코드를 사용 할 것인데 여기서 Time.time은 게임이 시작하고 경과 된 시간이다. 그러니 이 코드를 예를 들어서 설명하자면, 현재 lastCheckTime의 값은 0이다. 그래서 게임이 시작되고 0.01초가 되었다면 0.01초 - 0 은 0.01초다.

     

    그러면 0.05초보다 작으므로 false가 되어서 if조건문이 실행이 안 된다. 

     

    그러다가 Time.time이 0.05초가 넘는다면 true가 되어서 if조건문이 실행이 되고 Ray를 쏘는 로직을 수행 한다. 

    그리고 lastCheckTime = Time.time; 라는 코드를 사용해서 lastCheckTime 의 값을 변경을 해주는 것인데 이 코드는 다시 예를 보여주면서 설명을 하자면 0.05초가 되어서 lastCheckTime = Time.time; 가 사용이 되면 lastCheckTime 가 0.05초가 되고 Time.time - lastCheckTime 이 코드에서 사용이 된다. 

     

    Time.time이 0.09초가 되었다면 checkRate보다 크다. 그렇다면 if조건문이 실행이 될 것인데 실제로는 0.04초가 지난 것이고 실행이 되면 안 된다. 그래서 lastCheckTime  0.05로 변경이 된 값을 빼줌으로써 0.09-0.05가 되어서 0.04가 되어 false가 되어서 실행이 되지 않게 한다. 이렇게 코드를 작성 함으로써 checkRate 값마다 Ray를 호출 하게 하는 것이다. 

     

    이제 Ray를 만들어 준다. 

     

    Ray는 시작점과 방향을 가질 수 있는 광선이다. 

     

    Ray ray = camera.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2));

     

    라고 작성을 해주게 되면 width / 2, height / 2 를 통해서 화면 중앙에 발사 되는 Ray를 생성 하게 되는 것이다. 방향도 설정 해줄 수가 있는데 지금은 전방에 나가게 된다. 매개변수를 통해 방향도 설정이 가능하다. 

     

    이 화면 중앙에서 발사 되는 Ray를 만들고, Ray이 충돌이 된 정보를 담을 변수도 만들어 준다. 

     

    RaycastHit는 Hit 된 정보를 담을 수 있는 Struct다. 충돌한 객체의 위치, 충돌 지점의 노멀, 충돌한 게임 오브젝트 등 다양한 정보를 제공한다. 

     

    RaycastHit hit; 를 선언해서 충돌한 정보를 담을 수 있게 하고, Physics.Raycast를 사용해서 Ray를 쏘고 Hit 된 애를 저장 하게 하면 된다. 

     

    Physics.Raycast는 쏠 Ray(시작점과 방향을 갖고 있음), 그리고 Hit 된 정보를 담을 변수, Ray의 길이, 그리고 Layer를 통해 특정 Layer만 검사를 하게 할 수가 있다. 

     

    if (Physics.Raycast(ray, out hit, maxCheckDistance, layerMask))

     

    이렇게 코드를 작성 해줘서 만들어 둔 ray를 쏘고 hit에 저장 하면서 maxCheckDistance이라는 만들어둔 변수(0.3f)의 길이를 가진 ray로 쏘게 하고, layerMask(enum으로 설정 돼 있음)에 맞는 Layer만 검사를 하게 한다.(Hit 된 Layer가 맞는 거면 if가 true가 된다.)

     

    이렇게 하면 Ray를 쏘는 코드는 끝이 나게 되고 Lay를 맞은 게 우리가 원하는 결과라면 로직이 실행 되게 하면 된다. 

     

    if (Physics.Raycast(ray, out hit, maxCheckDistance, layerMask))

    { // 충돌한 객체가 있을 경우의 처리 }

    else { // 충돌한 객체가 없을 경우의 처리 }

     

    먼저 우리는 계속 해서 화면에 Ray를 쏘고 있으니 hit에 저장 된 정보가 Ray로 쏘고 있는 정보와 같다면 로직을 실행 하지 않는 코드를 작성 해주어야 한다. 똑같은 오브젝트에 마우스를 갖다 대고 있는데 계속 실행을 하면 안 되니까다.

     

    if (hit.collider.gameObject != curInteractGameObject) 

    라는 코드를 작성 해줘서 hit 된 Object가 curInteractGameObject(만들어둔 변수, 현재 null이다)과 다르다면 로직이 실행 되게 해준다. 만약 같다라면 로직이 실행 되지 않는 것.

     

    이렇게 해서 만약 같지 않다면 curInteractGameObject 이 변수가 hit 된 변수의 오브젝트로 저장이 되게끔 해준다. 

    curInteractGameObject = hit.collider.gameObject; 

    이렇게 해주면 curInteractGameObject 는 hit 된 오브젝트가 되고 같은 걸 hit 할 경우에 로직이 실행 되지 않게 한다. 

     

    그리고 우리는 지금 마우스를 갖다대면 아이템의 정보문구가 출력 되게 할 것이니 아이템 클래스에서 만들어둔 Interface를 갖고와준다.

     

    curInteractable라는 만들어둔 Interface자료형에 hit 된 거를 GetComponent를 해준다. 

    curInteractable = hit.collider.GetComponent<IInteractable>();

    이렇게 해주면 hit 된 거에 IInteractable이 들어가게 된다. Item에는 ItemObject 클래스가 들어가 있고, 거기에는 IInteractable이 들어가 있다. 그래서 GetComponent가 가능하다. 

     

     if (Physics.Raycast(ray, out hit, maxCheckDistance, layerMask))
     {
         if (hit.collider.gameObject != curInteractGameObject)
         {
             curInteractGameObject = hit.collider.gameObject;
             curInteractable = hit.collider.GetComponent<IInteractable>();
             SetPromptText();
         }
     }

    if true의 총 코드.

     

    그 후에 SetPromptText();를 호출 시켜 준다. 

     

     

    SetPromptText 는 현재 클래스에 있는 UI의 Text를 바꾸게 하는 함수.

     private void SetPromptText()
     {
         promptText.gameObject.SetActive(true);
         promptText.text = curInteractable.GetInteractPrompt();
     }

    이렇게 만들어 준다. promptText.gameObject.SetActive(true);는 Text가 활성화 되게 하는 것이고, Text의 내용을 promptText.text = curInteractable.GetInteractPrompt(); 이 코드로써 변경이 되게 해준다. 

     

    **

    curInteractable에 IInteractable 인터페이스를 구현한 '객체'가 저장.

    이는 ItemObject 클래스가 IInteractable을 구현하고 있으므로 가능.

     

    GetInteractPrompt 호출이 되면서 아이템의 이름과 설명이 나오게 하는 기능이 호출 되게 됨으로써 promptText의 내용이 변경을 하게 해준다. 

     

    그리고 이제 화면을 다시 다른 쪽으로 움직였을 때, Ray가 충돌을 하지 않을 때를 만들어 줘야 한다. 

    다시 promptText가 표시 되지 않게 하면 될 것이다.

     

    else
    {
        curInteractGameObject = null;
        curInteractable = null;
        promptText.gameObject.SetActive(false);
    }

     

    라는 코드를 만들어 줘서 Ray가 다른 걸 충돌 했을 때, 우리가 화면을 다른 걸 보고 있을 때에 다시 초기세팅값으로 바뀌게끔 해주면 된다. 

     

    이렇게 해주면, 우리는 화면 중앙에 Ray를 계속 해서 쏴서 아이템과 상호작용을 하게 할 수 있는 기반을 만들었다. 

    이제 인벤토리를 만들고, 특정 키를 누르면 아이템을 줍게 하는 기능을 만들어 주면 된다. 

     

    누가 나처럼 정리 해주는 사람 없나. 그 사람거 보게... 이해 하는 난이도가 비슷 할 거 아냐.. 없나..

     

     

     

Designed by Tistory.