#5 Robot! Escape! - Gun System

w298·2022년 10월 20일
0

DEVLOG - Robot! Escape!

목록 보기
5/21
post-thumbnail

1. FireAction 바인딩

public class RobotInputHandler : MonoBehaviour
{
    ...
    public InputAction fireAction;

    public bool isFire = false;
    ...
    
    public UnityEvent fireEvent;
    public UnityEvent fireStartEvent;
    public UnityEvent fireEndEvent;

    void Start()
    {
        ...
        fireAction.Enable();

        fireAction.started += context => fireStartEvent.Invoke();
        fireAction.canceled += context => fireEndEvent.Invoke();
        
        ...
    }

    void FixedUpdate()
    {
        ...
        if (isFire)
        {
            fireEvent.Invoke();
        }
    }
}

UnityEventGunController 와 바인딩을 진행했다.

  • OnFire : 버튼이 눌려있을 때마다 호출
  • OnFireStart : 버튼이 처음 눌렸을 때 호출
  • OnFireEnd : 버튼에서 땠을 때 호출

이와 같이 나눈 이유는 FULL-AUTO 모드와 SEMI-AUTO 모드 둘 다 사용하기 위해서이다.

2. GunController 구현

내용이 많아서 좀 쪼갰다.

OnFire, OnFireStart, OnFireEnd

  • 아까 위에서 이벤트 바인딩한 함수이다. 이벤트가 발생하면 가장 먼저 실행된다.
  • FIREMODE 에 따라서 로직이 다르다. fireDelay 변수를 설정해 총기의 발사 딜레이를 구현하였다.

실제 딜레이에 맞춰서 FireWeapon 함수가 실행된다.

public void OnFire()
{
    if (fireMode != FIREMODE.FULL) return;

    if (timeLastFired + fireDelay <= Time.time)
    {
        FireWeapon();
    }
}

public void OnFireStart()
{
    if (fireMode != FIREMODE.SEMI || semiFired) return;

    if (timeLastFired + fireDelay <= Time.time)
    {
        FireWeapon();
        semiFired = true;
    }
}

public void OnFireEnd()
{
    semiFired = false;
}
  • fireDelay = 0.5

  • fireDelay = 0.05

FireWeapon

private void FireWeapon()
{
    Instantiate(muzzleFlash, muzzleFireStart);
    // Muzzle Flash Effect 를 `muzzleFireStart` Transform 에 소환한다.

    RaycastHit hit = CheckHit();
    if (hit.collider) SpawnHitParticle(hit.point, hit.normal);
    // Hit 체크를 하고, `hit.collider` 가 true면 HitParticle 을 소환한다.

    StartCoroutine(SpawnTrail(hit));
    // Bullet Trail 을 그린다. (Corutine 사용)

    audioSource.Play(); // 총기 사운드를 플레이한다.
    camController.ShakeCamera(0.7f, 0.07f); // 카메라 Shake Effect 를 적용한다.

    timeLastFired = Time.time; // 딜레이 설정을 위해 필요
}

SpawnHitParticle

private void SpawnHitParticle(Vector3 hitPoint, Vector3 hitNormal)
{
    Instantiate(hitParticle, hitPoint, Quaternion.LookRotation(hitNormal));
}

파티클은 유니티에서 제공하는 파티클 팩 중 Metal Hit 이펙트를 사용했다.

  • Spark Effect
  • Dust Effect
  • Decal

SpawnTrail

Bullet Trail 을 그린다. 프레임당 3f 씩 움직인다.
while 문이 종료되면 Destroy 를 진행한다.

private IEnumerator SpawnTrail(RaycastHit hit)
{
    GameObject trail = Instantiate(bulletTrail, muzzleFireStart.position, muzzleFireStart.rotation);

    Vector3 hitPoint = hit.collider ? hit.point : muzzleFireStart.position + muzzleFireStart.right * 100;
    Vector3 startPoint = muzzleFireStart.position;
    float distance = Vector3.Distance(startPoint, hitPoint);
    Vector3 direction = (hitPoint - startPoint).normalized;

    while (Vector3.Distance(startPoint, trail.transform.position) < distance)
    {
        trail.transform.position += direction * 3f;
        yield return null;
    }

    trail.transform.position = hitPoint;

    Destroy(trail);
}

BulletTrail Prefab 을 만들어 사용했고, Trail Renderer 를 이용했다.

이렇게 생겼다.

Camera Shake Effect

카메라 Shake Effect 는 다음 장에서 작성하겠다.


Full-Code

public class GunController : MonoBehaviour
{
    public GameObject muzzleFlash;
    public Transform muzzleFireStart;

    private AudioSource audioSource;
    public AudioClip audioClip;

    public GameObject bulletTrail;
    public ParticleSystem hitParticle;

    private PlayerCameraController camController;

    public FIREMODE fireMode = FIREMODE.FULL;
    public float fireDelay = 0.1f;
    private float timeLastFired = 0;
    private bool semiFired = false;

    void Start()
    {
        audioSource = GetComponentInChildren<AudioSource>();
        audioSource.clip = audioClip;
        camController = transform.root.GetComponent<PlayerCameraController>();
    }

    private RaycastHit CheckHit()
    {
        Physics.Raycast(muzzleFireStart.position, muzzleFireStart.right, out RaycastHit hit, 100);
        return hit;
    }

    private IEnumerator SpawnTrail(RaycastHit hit)
    {
        GameObject trail = Instantiate(bulletTrail, muzzleFireStart.position, muzzleFireStart.rotation);

        Vector3 hitPoint = hit.collider ? hit.point : muzzleFireStart.position + muzzleFireStart.right * 100;
        Vector3 startPoint = muzzleFireStart.position;
        float distance = Vector3.Distance(startPoint, hitPoint);
        Vector3 direction = (hitPoint - startPoint).normalized;

        while (Vector3.Distance(startPoint, trail.transform.position) < distance)
        {
            trail.transform.position += direction * 3f;
            yield return null;
        }

        trail.transform.position = hitPoint;

        Destroy(trail);
    }

    private void SpawnHitParticle(Vector3 hitPoint, Vector3 hitNormal)
    {
        Instantiate(hitParticle, hitPoint, Quaternion.LookRotation(hitNormal));
    }

    private void FireWeapon()
    {
        Instantiate(muzzleFlash, muzzleFireStart);

        RaycastHit hit = CheckHit();
        if (hit.collider) SpawnHitParticle(hit.point, hit.normal);
        
        StartCoroutine(SpawnTrail(hit));

        audioSource.Play();
        camController.ShakeCamera(0.7f, 0.07f);

        timeLastFired = Time.time;
    }

    public void OnFire()
    {
        if (fireMode != FIREMODE.FULL) return;

        if (timeLastFired + fireDelay <= Time.time)
        {
            FireWeapon();
        }
    }

    public void OnFireStart()
    {
        if (fireMode != FIREMODE.SEMI || semiFired) return;

        if (timeLastFired + fireDelay <= Time.time)
        {
            FireWeapon();
            semiFired = true;
        }
    }

    public void OnFireEnd()
    {
        semiFired = false;
    }
}
profile
Game Developer & Web Developer

0개의 댓글