[XR 전문 인력 과정] 유니티 타워디펜스 만들기(2) -총쏘기(Raycast), 무기 잡기, 무기 던지기

2023. 11. 23. 22:07Unity

728x90
반응형

Player와 Tower는 빼고 다른 물체는 Ray에 맞게 구현할 예정

Ray가 맞은 부분에 이펙트 생성

 

 


 

 

PlayerFire 스크립트 생성

XRInput.cs 스크립트의 IndexTrigger를 사용한다.

    public enum Button
    {
#if PC
        One = ButtonTarget.Fire1,
        Two = ButtonTarget.Jump,
        Thumbstick = ButtonTarget.Fire1,
        IndexTrigger = ButtonTarget.Fire2
#elif Oculus
        One = OVRInput.Button.One,
        Two = OVRInput.Button.Two,
        Thumbstick = OVRInput.Button.PrimaryThumbstick,
        IndexTrigger = OVRInput.Button.PrimaryIndexTrigger
#endif
    }

 

 

XRInput 스크립트에서 Button에서 IndexTrigger가져오기

void Update()
{
    //버튼을 누르면 Ray를 생성
    if (XRInput.GetDown(XRInput.Button.IndexTrigger))
    {
        //레이 생성 구조체 변수로 가져오기. 괄호안에 시작점의 위치와 방향 넣기
        Ray ray = new Ray(XRInput.RHandPosition, XRInput.RHandDirection);
        //충돌 정보 저장(RaycastHit)
        RaycastHit hitInfo;
    }
}

 

 

Player에 Player 레이어 추가, Tower에 Tower 레이어 추가

 

 

레이어 관련 코드줄 추가 Update 함수에서,

void Update()
{
    //버튼을 누르면 Ray를 생성
    if (XRInput.GetDown(XRInput.Button.IndexTrigger))
    {
        //레이 생성 구조체 변수로 가져오기. 괄호안에 시작점의 위치와 방향 넣기
        Ray ray = new Ray(XRInput.RHandPosition, XRInput.RHandDirection);
        //충돌 정보 저장(RaycastHit)
        RaycastHit hitInfo;
        //플레이어 레이어를 받아오기 레이어 번호 숫자 형태로 가져올 수 있다.
        int playerLayer = 1 << LayerMask.NameToLayer("Player"); ;  //꺾쇠는 레이어를 쭉 훑어보는 느낌
        //타워 레이어 받아오기
        int towerLayer = 1 << LayerMask.NameToLayer("Tower");
        int layerMask = playerLayer | towerLayer; //레이어 마스크에 저장
    }
}

 

 


 

 

총 효과 에셋 가져오기

https://assetstore.unity.com/packages/vfx/particles/war-fx-5669

 

War FX | 시각 효과 파티클 | Unity Asset Store

Add depth to your next project with War FX from Jean Moreno. Find this & more 시각 효과 파티클 on the Unity Asset Store.

assetstore.unity.com

 

 

에셋 효과 하나 선택해서 Hierarchy로 가져오고 안에 원래 들어있는 스크립트 삭제

 

 

해당 이펙트에 접근해서 Particle System에 접근하기

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

public class PlayerFire : MonoBehaviour
{
    //Transform 가져오기
    public Transform bulletObj;
    //bulletObj의 Particle System 가져오기
    ParticleSystem bulletEffect;
    void Start()
    {
        //bulletEffect 할당하기
        bulletEffect = bulletObj.GetComponent<ParticleSystem>();
    }

    void Update()
    {
        //버튼을 누르면 Ray를 생성
        if (XRInput.GetDown(XRInput.Button.IndexTrigger))
        {
            //레이 생성 구조체 변수로 가져오기. 괄호안에 시작점의 위치와 방향 넣기
            Ray ray = new Ray(XRInput.RHandPosition, XRInput.RHandDirection);
            //충돌 정보 저장(RaycastHit)
            RaycastHit hitInfo;
            //플레이어 레이어를 받아오기 레이어 번호 숫자 형태로 가져올 수 있다.
            int playerLayer = 1 << LayerMask.NameToLayer("Player"); ;  //꺾쇠는 레이어를 쭉 훑어보는 느낌
            //타워 레이어 받아오기
            int towerLayer = 1 << LayerMask.NameToLayer("Tower");
            int layerMask = playerLayer | towerLayer; //레이어 마스크에 저장

            //Ray 발사 괄호 안에 (ray 대상, 충돌 정보, 얼마만큼 뻗어나갈 것인지, )
            if (Physics.Raycast(ray, out hitInfo, 1200, ~layerMask)) //~ 뜻은 ~빼고
            {
                //이펙트 생성. 이펙트를 한 번 Stop하고 그 다음 Play
                bulletEffect.Stop();
                bulletEffect.Play();
                //파티클 이펙트를 맞은 위치에 순간이동
                bulletObj.position = hitInfo.point;
                //땅에 맞으면 해당 부분에 normal로 나타나게
                bulletObj.forward = hitInfo.normal;
            }
        }
    }
}

 

 

스크립트를 Player에 넣고 비어있는 Bullet Obj에 파티클 프리팹을 넣는다.

 

 


 

 

폭탄 에셋 받아오기

https://assetstore.unity.com/packages/3d/props/weapons/rockets-missiles-bombs-cartoon-low-poly-pack-73141

 

Rockets, Missiles & Bombs - Cartoon Low Poly Pack | 3D 무기 | Unity Asset Store

Elevate your workflow with the Rockets, Missiles & Bombs - Cartoon Low Poly Pack asset from AurynSky. Find this & other 무기 options on the Unity Asset Store.

assetstore.unity.com

 

 

마음에 드는 에셋 가져오기

 

 

폭탄 에셋에 Capsule Collider, Rigidbody 추가, IsKinematic 활성화

 

Weapon 레이어 생성해서 프리팹에 적용

 

 


 

 

HandGrab 스크립트 만들기

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

public class HandGrab : MonoBehaviour
{
    //변수 생성
    //물체를 잡고 있는지 여부 처음에는 안잡고 있기
    bool isGrabbing = false;
    //잡고 있는 물체
    GameObject grabbedObject;
    //잡을 물체 레이어
    public LayerMask grabbedLayer;
    //잡을 거리 설정
    public float grabRange = 5f;
    void Update()
    {
        if(isGrabbing == false)
        {
            TryGrab();
        }
    }
    void TryGrab()
    {
        //만약 잡기 버튼을 누른다면 일정 영역 안의 무기를 잡는다
        if (XRInput.GetDown(XRInput.Button.Thumbstick, XRInput.Controller.RTouch))
        {
            //구 모양의 일정 영역안의 무기 찾기. 배열 사용
            //영역 안 모든 무기를 찾기
            Collider[] hitObjects = Physics.OverlapSphere(XRInput.RHandPosition, grabRange, grabbedLayer);
            //가장 가까운 무기 인덱스를 찾아옴
            int closest = 0;

            //손과 가장 가까운 물체 선택
            for (int i = 1; i < hitObjects.Length; i++)
            {
                //손과 가장 가까운 물체의 거리
                Vector3 closestPos = hitObjects[closest].transform.position;
                float closestDistance = Vector3.Distance(closestPos, XRInput.RHandPosition);
                //다음 물체와 손의 거리
                Vector3 nextPos = hitObjects[i].transform.position;
                float nextDistance = Vector3.Distance(nextPos, XRInput.RHandPosition);
                //만약 다음 물체 거리가 더 가까우면, 인덱스 교체
                if(nextDistance < closestDistance)
                {
                    closest = i;
                }
            }
            //만약 검출된 물체가 있다면
            if(hitObjects.Length > 0)
            {
                //잡은 상태로 전환
                isGrabbing = true;
                grabbedObject = hitObjects[closest].gameObject;
                //잡은 물체를 손의 자식으로 등록
                grabbedObject.transform.parent = XRInput.RHand;
                //물체의 물리 기능 정지 Is Kinematic
                grabbedObject.GetComponent<Rigidbody>().isKinematic = true;
                print("Grab");
            }
        }
    }
}

 

 

Player에 HandGrab 스크립트 적용, 

 

 


 

 

물건 던지기

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

public class HandGrab : MonoBehaviour
{
    //변수 생성
    //물체를 잡고 있는지 여부 처음에는 안잡고 있기
    bool isGrabbing = false;
    //잡고 있는 물체
    GameObject grabbedObject;
    //잡을 물체 레이어
    public LayerMask grabbedLayer;
    //잡을 거리 설정
    public float grabRange = 5f;
    //오브젝트를 던질 힘
    float throwPower = 30f;
    //이전 위치
    Vector3 prePos;

    void Update()
    {
        if(isGrabbing == false)
        {
            TryGrab();
        }
        else
        {
            //잡고있다면
            TryUngrab();
        }
    }
    void TryGrab()
    {
        //만약 잡기 버튼을 누른다면 일정 영역 안의 무기를 잡는다
        if (XRInput.GetDown(XRInput.Button.Thumbstick, XRInput.Controller.RTouch))
        {
            //구 모양의 일정 영역안의 무기 찾기. 배열 사용
            //영역 안 모든 무기를 찾기
            Collider[] hitObjects = Physics.OverlapSphere(XRInput.RHandPosition, grabRange, grabbedLayer);
            //가장 가까운 무기 인덱스를 찾아옴
            int closest = 0;

            //손과 가장 가까운 물체 선택
            for (int i = 1; i < hitObjects.Length; i++)
            {
                //손과 가장 가까운 물체의 거리
                Vector3 closestPos = hitObjects[closest].transform.position;
                float closestDistance = Vector3.Distance(closestPos, XRInput.RHandPosition);
                //다음 물체와 손의 거리
                Vector3 nextPos = hitObjects[i].transform.position;
                float nextDistance = Vector3.Distance(nextPos, XRInput.RHandPosition);
                //만약 다음 물체 거리가 더 가까우면, 인덱스 교체
                if(nextDistance < closestDistance)
                {
                    closest = i;
                }
            }
            //만약 검출된 물체가 있다면
            if(hitObjects.Length > 0)
            {
                //잡은 상태로 전환
                isGrabbing = true;
                grabbedObject = hitObjects[closest].gameObject;
                //잡은 물체를 손의 자식으로 등록
                grabbedObject.transform.parent = XRInput.RHand;
                //물체의 물리 기능 정지 Is Kinematic
                grabbedObject.GetComponent<Rigidbody>().isKinematic = true;
                print("Grab");
            }
        }
    }
    void TryUngrab()
    {
        //던질 방향
        Vector3 throwDirection = XRInput.RHandPosition - prePos;
        prePos = XRInput.RHandPosition;
        //잡기 버튼을 놓았다면
        if(XRInput.GetUp(XRInput.Button.Thumbstick, XRInput.Controller.RTouch))
        {
            //잡지 않은 상태로 전환
            isGrabbing = false;
            //물리 기능 활성화
            grabbedObject.GetComponent<Rigidbody>().isKinematic = false;
            //손에서 무기 떼어내기
            grabbedObject.transform.parent = null;
            //던지기 (방향 * 힘)
            grabbedObject.GetComponent<Rigidbody>().velocity = throwDirection * throwPower;
            //잡은 물체를 비워줌
            grabbedObject = null;
        }
    }
}

 

 


 

 

728x90
반응형