미디어 콘텐츠 스터디

Part08. 양손 잡기 상호작용(Two Hand Grab Interation) 본문

가상현실(Virtual Reality)/가상현실 기초 다루기

Part08. 양손 잡기 상호작용(Two Hand Grab Interation)

danmujicat 2022. 8. 11. 14:23

 

1. 총 잡기

하이러키 창에 M4_Complete 모델을 추가하고 인스펙터 창에서 Transform 재설정한다.

M4_Complete.fbx
0.07MB


     Position(-0.747, 0.54, 0.116) 
② 하이러키 창에서 M4_Complete 을 선택하고 TwoHandGrabInteractable.cs 스크립트를 추가한다.

TwoHandGrabInteractable.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

public class TwoHandGrabInteractable : XRGrabInteractable

void Start()
{     
      
}
    
void Update()
{
        
}

③ 하이러키 창에서 M4_Complete 을 선택하고 인스펙터 창에서 Rigidbody 추가한다.
④ 하이러키 창에서 M4_Complete 하위의 모든 오브젝트를 선택하고 인스펙터 창에서 [Add Componenent]버튼을 선택하여 Box Collider를 추가한다.

⑤ 하이러키 창에서 M4_Complete를 선택하고 인스펙터 창에 Colliders로 M4_Complete 하위의 모든 오브젝트를 드래그앤 드룹하여 설정함

⑥ 하이러키 창에서 M4_Complete를 선택하고 인스펙스 창에서 Layer를 Grab을 설정한다.

 

⑦ 하이러키 창에서 M4_Complete를 선택하고 GameObject | Create Empty 메뉴 선택하여 하위에 GameObject 추가하고 이름을 Attach Transform으로 변경하고 중심좌표를 총 뒤쪽 손잡이로 이동  및 y축을 -180도 회전한다.

                 -  Position(0.0318, 0.0031, 0.0498),    Rotation(0, -180, 0) 

⑧ 하이러키 창에서 M4_Complete-> Attach Transform을 선택하여 인스펙터 창에 TwoHandGrabInteractable 컴포넌트의 Attatch Transfrom에 드래그앤드룹하여 설정한다.

⑨ 실행하기

2. 총 쏘기

 하이러키 창에서 M4_Complete를 선택하고 인스펙트 창에서 [Add component] 버튼을 클릭하여 Gun.cs과 Audio Source 컴포넌트를 추가한다. (또는 이전 권총의 Gun과 Audio Source 컴포넌트 복사하여 붙여넣기 해도 된다)


② 하이러키 창에서 M4_Complete를 선택하고 하위에 GameObject | Create Empty 메뉴를 선택하여  GameObject를 만들고 이름을 Barrel를 변경한다.

하이러키 창에서 Barrel를 선택하고  중심좌표를 총입구로 이동하고 y축으로 -180도 회전한다.
                 - Position(0, 0.06, -0.499),  Rotation(0, -180, 0)

④하이러키 창에서 Barrel를 선택하여 인스펙터 창에서 Gun 컴포넌트의 Barrel에 드래드앤드룹, Audio Source을 Gun 컴포넌트의 Audio Source에 드래드앤드롭하여 설정한다

 하이러키 창에서 M4_Complete를 선택하고 인스펙트 창에서 Two Hand Grab Interactable 컴포넌트의 Activate에 Gun  컴포넌트로 드래그앤드룹하여 설정하고 함수는 Gun->Fire()을 설정한다.

⑥실행하기

3. 총 회전시키기

3.1 총 머리와 총 손잡이 회전 만들기

  하이러키 창에서 M4_Complete 선택하고 Cube 추가하고 이름을 Second Grab Point로 변경하고 중심좌표를 총 머리쪽이으로 이동하고 크기를 조정한다. 
                                              Position(0.016, 0.073, -0.222)  Scale(0.0610, 0.066, 0.163)

하이러키 창에서 Second Grab Point를 선택하고 인스펙터 창에서 Cube(Mesh Filter), Mesh Renderer 삭제한다.
③하이러키 창에서 Second Grab Point를 선택하고 인스펙터 창에서 RigidBody 컴포넌트 추가하고 Is Kinematic 체크, 
 XR Simple Interactable 컴포넌트 추가하고 Colliders에 Box Collider을 드래그앤 드룹하여 설정한다.

④ 하이러키 창에서  Second Grab Point를 복사하여 Second Grab Point(1)을 총 앞쪽 손잡이로 이동한다. 또는 인스펙터 창에서 Transform 재설정한다
             Position(0.019, -0.052, -0.07)  Rotation(-80.28, 0,0) Scale(0.033, 0.071 , 0.163)

⑤ 인스펙터 창에서 Layer에서 Add Layer 클릭하여 Second Hand Grab 를 추가한다. 
⑥ 하이러키 창에서 Second Grab Point와 Second Grab Point(1)을 선택하고 인스펙터 창에서 Layer를 Second Hand Grab로 설정한다
⑦ Edit | Project Setting 메뉴 선택하여 Physics항목을 선택하여 Layer Collision Matrix 설정한다.

⑧ 회전 코딩하기

TwoHandGrabInteractable 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

public class TwoHandGrabInteractable : XRGrabInteractable
{
   public List<XRSimpleInteractable> secondHandGrabPoints = new List<XRSimpleInteractable>();
    private XRBaseInteractor secondInteractor;
   
    void Start()
    {
        foreach (var item in secondHandGrabPoints)
        {
            item.onSelectEntered.AddListener(OnSecondHandGrab);
            item.onSelectExited.AddListener(OnSecondHandRelease);
        }
       
    }

    public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase updatePhase)
     {

        if (secondInteractor && selectingInteractor)
        {
            selectingInteractor.attachTransform.rotation = Quaternion.LookRotation(secondInteractor.attachTransform.position - selectingInteractor.attachTransform.position); ;


        }
        base.ProcessInteractable(updatePhase);
      
    }
    
    public void OnSecondHandGrab(XRBaseInteractor interactor)
    {
        Debug.Log("SECOND HAND GRAB");
       secondInteractor = interactor;
       // initialRotationOffset = Quaternion.Inverse(GetTwoHandRotation()) * selectingInteractor.attachTransform.rotation;
    }

    public void OnSecondHandRelease(XRBaseInteractor interactor)
    {
        Debug.Log("SECOND HAND RELEASE");
        secondInteractor = null;

    }
    protected override void OnSelectEntered(XRBaseInteractor interactor)
    {
        Debug.Log("First Grab Enter");
        base.OnSelectEntered(interactor);
    
    }

    protected override void OnSelectExited(XRBaseInteractor interactor)
    {
        Debug.Log("First Grab Exit");
        base.OnSelectExited(interactor);
        secondInteractor = null;
     

    }
      
    public override bool IsSelectableBy(XRBaseInteractor interactor)
    {
        bool isalreadygrabbed = selectingInteractor && !interactor.Equals(selectingInteractor);
        return base.IsSelectableBy(interactor) && !isalreadygrabbed;
    }

}

⑨ 실행하기

3.2  회전 개선하기

① 총을 다시 잡을 경우 손 위치 초기화 시키기

TwoHandGrabInteractable.cs

public class TwoHandGrabInteractable : XRGrabInteractable
{
  private Quaternion attachInitialRotation;
   ....
   protected override void OnSelectEntered(XRBaseInteractor interactor)
    {
        Debug.Log("First Grab Enter");
        base.OnSelectEntered(interactor);
        attachInitialRotation = interactor.attachTransform.localRotation;

    }

    protected override void OnSelectExited(XRBaseInteractor interactor)
    {
        Debug.Log("First Grab Exit");
        base.OnSelectExited(interactor);
       secondInteractor = null;
        interactor.attachTransform.localRotation = attachInitialRotation;

    }
    ...
  }

② 하이러키 창에서 M4_Complete를 선택하고 인스펙트 창에서 Second Hand Points에 하이러키창의 M4_Complete하위에 있는  Second Grab Point와 Second Grab Point(1)을 드래그앤드룹하여 설정한다.

 

③ 코딩 하기

TwoHandGrabInteractable .cs

...

public class TwoHandGrabInteractable : XRGrabInteractable
{
   public List<XRSimpleInteractable> secondHandGrabPoints = new List<XRSimpleInteractable>();
    private XRBaseInteractor secondInteractor;
    private Quaternion attachInitialRotation;
    public bool snapToSecondHand = true;
     public TwoHandRotationType twoHandRotationType;

     public enum TwoHandRotationType { None, First, Second };
     private Quaternion initialRotationOffset;
    
    // Start is called before the first frame update
    void Start()
    {
        foreach (var item in secondHandGrabPoints)
        {
            item.onSelectEntered.AddListener(OnSecondHandGrab);
            item.onSelectExited.AddListener(OnSecondHandRelease);
        }
       
    }

    public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase updatePhase)
     {

      
          if (secondInteractor && selectingInteractor)
          {
              //Compute the rotation 
              if (snapToSecondHand)
                  selectingInteractor.attachTransform.rotation = GetTwoHandRotation();
              else
                  selectingInteractor.attachTransform.rotation = GetTwoHandRotation() * initialRotationOffset;
          }
          base.ProcessInteractable(updatePhase);
       
    }

   private Quaternion GetTwoHandRotation()
     {
         Quaternion targetRotation;
         if (twoHandRotationType == TwoHandRotationType.None)
         {
             targetRotation = Quaternion.LookRotation(secondInteractor.attachTransform.position - selectingInteractor.attachTransform.position); ;
         }
         else if (twoHandRotationType == TwoHandRotationType.First)
         {
             targetRotation = Quaternion.LookRotation(secondInteractor.attachTransform.position - selectingInteractor.attachTransform.position, selectingInteractor.attachTransform.up);
         }
         else
         {
             targetRotation = Quaternion.LookRotation(secondInteractor.attachTransform.position - selectingInteractor.attachTransform.position, secondInteractor.attachTransform.up);
         }

         return targetRotation;
     }

    
    public void OnSecondHandGrab(XRBaseInteractor interactor)
    {
        Debug.Log("SECOND HAND GRAB");
       secondInteractor = interactor;
       initialRotationOffset = Quaternion.Inverse(GetTwoHandRotation()) * selectingInteractor.attachTransform.rotation;
    }

    public void OnSecondHandRelease(XRBaseInteractor interactor)
    {
        Debug.Log("SECOND HAND RELEASE");
        secondInteractor = null;

    }
    protected override void OnSelectEntered(XRBaseInteractor interactor)
    {
        Debug.Log("First Grab Enter");
        base.OnSelectEntered(interactor);
        attachInitialRotation = interactor.attachTransform.localRotation;

    }

    protected override void OnSelectExited(XRBaseInteractor interactor)
    {
        Debug.Log("First Grab Exit");
        base.OnSelectExited(interactor);
       secondInteractor = null;
        interactor.attachTransform.localRotation = attachInitialRotation;

    }
    
   
    public override bool IsSelectableBy(XRBaseInteractor interactor)
    {
        bool isalreadygrabbed = selectingInteractor && !interactor.Equals(selectingInteractor);
        return base.IsSelectableBy(interactor) && !isalreadygrabbed;
    }

}

④실행하기

Comments