개발/Unity

[Unity3D] 마우스 좌표에 미사일 투하

Majangnan 2023. 3. 13. 12:59

Unity 3D 에서 버튼을 클릭하면 마우스 좌표를 따라다니는 과녁이 생성되고, 마우스 클릭 시 좌표 지점에 미사일 오브젝트가 떨어지는 기능을 구현해 보겠다. 이 기능은 현재 진행중인 디펜스 게임 프로젝트에서 추가 기능으로 넣으려고 직접 구현해 본 기능이다.

 

우선 Plane 과 Quad 를 생성해 준 뒤, Quad를 x축 방향으로 90도 회전시켜서 바닥과 평행하게 만들어주고 적당한 과녁 이미지를 넣어준다. 이미지 투명 처리를 하지 못해서 배경과 바닥 색을 똑같게 하여 과녁만 보이는 것 같이 꼼수를 부렸다. ㅎㅎ

 

 

그 후 미사일 역할을 할 캡슐 하나를 생성해 주고, Collider의 Is Trigger를 체크해준후, Rigidbody를 추가해주었다. 충돌을 감지하기 위해서는 충돌을 일으키는 양쪽 게임오브젝트 모두 Collider 컴포넌트가 있어야 하고, 움직이는 쪽에는 Rigidbody 컴포넌트가 있어야 한다. 

 

 

다음으로 미사일 발사를 활성화 시킬 버튼 하나를 추가해주도록 하겠다. 캔버스를 하나 생성해준뒤 Button을 생성하여 적절히 이름을 바꿔주었다.

 

 

레이에 감지될 특정 레이어만 설정하기 위해 새로운 FLOOR 레이어를 하나 생성해주고, Floor 오브젝트에 레이어를 지정해주었다. 

 

 

다음으로 빈 오브젝트를 하나 생성해주고 MissleManager라고 이름을 지은 후, SpawnMissle 스크립트를 생성한 후 컴포넌트로 추가해주었다.

 

 

처음에 만든 Missle 캡슐을 프리팹화 시켜준 후, 또 다른 스크립트 MissleCtrl을 추가해주도록 한다.

 

 

 

이제 스크립트 작성만 남았다. 스크립트를 작성해보도록 하자. 

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

public class MissleCtrl : MonoBehaviour
{
    public float mSpeed = 10;
    public GameObject mParticle;

    void Update()
    {
        this.transform.Translate(Vector3.down * mSpeed * Time.deltaTime);
    }

    private void OnTriggerEnter(Collider other)
    {
        GameObject missleParticle = Instantiate(mParticle, this.transform.position - Vector3.down, mParticle.transform.rotation);
        Destroy(missleParticle, 1f);
        Destroy(this.gameObject);
    }
}

 

미사일 프리팹 안에 들어가는 미사일의 움직임을 구현하는 스크립트이다. 미사일이 소환되면 미사일은 주어진 속도만큼 아래로 떨어진다. 미사일이 떨어져 바닥에 충돌하면 폭발 파티클을 생성하면서 사라지게 된다. 

 

다음은 MissleManager안에 들어가는 버튼을 누르면 과녁을 마우스 좌표에 위치시키고 한번 더 클릭하면 좌표 위쪽에 미사일을 소환하는 스크립트이다.

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

public class SpawnMissle : MonoBehaviour
{
    public GameObject targetMark;       // 과녁
    GameObject target;

    public GameObject missleObj;        // 미사일 오브젝트

    public LayerMask layermask;         

    private static bool isClick = false;

    void Start()
    {
        // 바닥 레이어 설정
        layermask = 1 << LayerMask.NameToLayer("FLOOR");
    }

    void Update()
    {
        if (target != null && isClick == false && Input.GetMouseButtonDown(0))
        {
            isClick = true;
            SpawnMissleObj();
            Destroy(target, 1.5f);
        }
    }

    // 버튼 누르면 호출
    public void spawnTarget()
    {
        StartCoroutine(cospawnTarget());
    }
    
    IEnumerator cospawnTarget()
    {
        isClick = false;
        // 카메라에서 마우스 위치로 레이를 쏜다
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        // 지정된 레이어에 맞으면
        if (Physics.Raycast(ray, out hit, Mathf.Infinity, layermask))
        {
            target = Instantiate(targetMark, hit.point, targetMark.transform.rotation);
            while (!isClick)
            {
                Ray rayTarget = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hitTarget;
                if (Physics.Raycast(rayTarget, out hitTarget, Mathf.Infinity, layermask))
                {
                    // 마우스 위치로 계속 좌표 위치를 변환시켜줌
                    target.transform.position = hitTarget.point;
                    yield return null; 
                }
            }
        }
    }

    // 마우스 클릭 시 호출
    private void SpawnMissleObj()
    {
        StartCoroutine(coSpawnMissleObj());
    }

    IEnumerator coSpawnMissleObj()
    {
        if (isClick)
        {
            // y축으로 10만큼 위에 미사일 오브젝트 생성
            GameObject missle = Instantiate(missleObj, target.transform.position + Vector3.up * 10f,
                                                        missleObj.transform.rotation);
        }
        yield return null;
    }
}

 

코드를 살펴보자. 우선 버튼을 클릭하면 spawnTarget() 함수가 호출된다. 이 함수는  cospawnTarget() 코루틴 함수를 호출한다. 코루틴 함수를 보면 카메라에서 마우스 좌표 위치로 레이를 쏘고 지정된 레이어에 맞으면 target을 생성한다. 클릭을 하지 않는 동안 타겟의 위치를 마우스 좌표 위치로 position 값을 계속해서 변화시켜주어 마우스를 따라다니도록 하였다. (코루틴에 관한 설명은 생략)

 ScreenPointToRay 함수는 Camera 클래스의 함수이다. 스크린 좌표를 인수로 넘겨주면 카메라에서 시작하여 스크린 좌표에 해당하는 3차원의 좌표로 Ray를 생성시켜준다.

// 버튼 누르면 호출
public void spawnTarget()
    {
        StartCoroutine(cospawnTarget());
    }    

IEnumerator cospawnTarget()
    {
        isClick = false;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        // 카메라에서 마우스 위치로 레이를 쏜다
        if (Physics.Raycast(ray, out hit, Mathf.Infinity, layermask))
        {
            target = Instantiate(targetMark, hit.point, targetMark.transform.rotation);
            while (!isClick)
            {
                Ray rayTarget = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hitTarget;
                if (Physics.Raycast(rayTarget, out hitTarget, Mathf.Infinity, layermask))
                {
                    // 마우스 위치로 계속 좌표 위치를 변환시켜줌
                    target.transform.position = hitTarget.point;
                    yield return null; 
                }
            }
        }
    }

 

만약 마우스를 클릭하면 isClick 은 true로 바뀌고, SpawnMissleObj() 함수가 실행된다. 생성된 좌표는 1.5초 후에 삭제된다.

void Update()
    {
        if (target != null && isClick == false && Input.GetMouseButtonDown(0))
        {
            isClick = true;
            SpawnMissleObj();
            Destroy(target, 1.5f);
        }
    }

 

SpawnMissleObj() 함수 역시 코루틴을 호출하고 코루틴에서는 타겟의 y축 10만큼의 높이에서 미사일 오브젝트를 생성한다.

// 마우스 클릭 시 호출
    private void SpawnMissleObj()
    {
        StartCoroutine(coSpawnMissleObj());
    }

    IEnumerator coSpawnMissleObj()
    {
        if (isClick)
        {
            // y축으로 10만큼 위에 미사일 오브젝트 생성
            GameObject missle = Instantiate(missleObj, target.transform.position + Vector3.up * 10f,
                                                        missleObj.transform.rotation);
        }
        yield return null;
    }

 

다시 유니티 화면으로 돌아와서, 버튼 이벤트에 MissleManager를 넣고 spawnTarget() 함수를 연결시켜준다.

 

 

Missle 프리팹과 MissleManager의 스크립트 안에 각각 알맞는 게임 오브젝트들을 넣어준다.

 

 

프로젝트를 실행해보면, 마우스를 따라 타겟이 이동하고 클릭시 생성된 미사일 오브젝트는 아래로 떨어져 충돌 이벤트가 발생하면 폭발 파티클을 생성하며 사라지게 된다.