FPS서바이벌 디펜스

5-3.명중, 피격 이팩트

ruripanda 2025. 4. 21. 17:15

본 강좌는 케이디님의 유튜브강좌 영상을 보고 유니티6로 구현한 것 입니다 이렇게 멋진 강좌를 만드신 케이디님을 존경합니다

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

public class GunController : MonoBehaviour
{
    //현재 장착된 총
    [SerializeField]
    private Gun currentGun;
    //연사속도 계산
    private float currentFireRate;//정조준 위치를 위한 값

    //상태변수
    private bool isReload = false;
    [HideInInspector]//인스팩터에세 숨김
    public bool isFineSightMode = false;//정조준 위치 bool 값

    // 본래 포지션 값.
    [SerializeField]
    private Vector3 originPos;//원래 조준위치

    //효과음
    private AudioSource audioSource;

    //레이져 충돌 정도 받아옴
    private RaycastHit hitInfo;

    //필요한 카메라 컴포넌트
    [SerializeField]
    private Camera theCam;

    //피격 이팩트
    [SerializeField]
    private GameObject hit_effect_prefab;
    void Start()
    {
        originPos = Vector3.zero;
        audioSource = GetComponent<AudioSource>();
    }

    // Update is called once per frame
    void Update()
    {
        GunFireRateCalc();
        TryFire();
        TryReload();
        TryFineSight();
    }

    //연사속도 재계산
    private void GunFireRateCalc()
    {
        if (currentFireRate > 0)
            currentFireRate -= Time.deltaTime;
    }

    //발사시도
    private void TryFire()
    {
        if (Input.GetButton("Fire1") && currentFireRate <= 0 && !isReload)
        {
            Fire();
        }
    }

    //발사전 계산
    private void Fire()
    {
        if (!isReload)
        {
            if (currentGun.currentBulletCount > 0)
                Shoot();
            else
            {
                CancelFineSight();
                StartCoroutine(ReloadCoroutine());
            }


        }
    }

    //발사 후 계산
    private void Shoot()
    {
        currentGun.currentBulletCount--;
        currentFireRate = currentGun.fireRate; // 연사 속도 재계산.
        PlaySE(currentGun.fire_Sound);
        currentGun.muzzleFlash.Play();
        Hit();
        StopAllCoroutines();
        StartCoroutine(RetroActionCoroutine());
    }

    private void Hit()
    {
        if (Physics.Raycast(theCam.transform.position, theCam.transform.forward, out hitInfo, currentGun.range))
        {
            GameObject clone = Instantiate(hit_effect_prefab, hitInfo.point, Quaternion.LookRotation(hitInfo.normal));
            Destroy(clone, 2f);
            //Debug.Log(hitInfo.transform.name);
        }
    }

    //재장전 시도
    private void TryReload()
    {
        if (Input.GetKeyDown(KeyCode.R) && !isReload && currentGun.currentBulletCount < currentGun.reloadBulletCount)
        {
            CancelFineSight();
            StartCoroutine(ReloadCoroutine());
        }
    }

    //재장전
    IEnumerator ReloadCoroutine()
    {
        if (currentGun.carryBulletCount > 0)
        {
            isReload = true;

            currentGun.anim.SetTrigger("Reload");


            currentGun.carryBulletCount += currentGun.currentBulletCount;
            currentGun.currentBulletCount = 0;

            yield return new WaitForSeconds(currentGun.reloadTiem);

            if (currentGun.carryBulletCount >= currentGun.reloadBulletCount)
            {
                currentGun.currentBulletCount = currentGun.reloadBulletCount;
                currentGun.carryBulletCount -= currentGun.reloadBulletCount;
            }
            else
            {
                currentGun.currentBulletCount = currentGun.carryBulletCount;
                currentGun.carryBulletCount = 0;
            }


            isReload = false;
        }
        else
        {
            Debug.Log("소유한 총알이 없습니다.");
        }
    }

    //정조준 시도
    private void TryFineSight()
    {
        if (Input.GetButtonDown("Fire2") && !isReload)
        {
            FineSight();
        }
    }

    //정조준 취소
    public void CancelFineSight()
    {
        if (isFineSightMode)
            FineSight();
    }

    //정조준 로직가동
    private void FineSight()
    {
        isFineSightMode = !isFineSightMode;
        currentGun.anim.SetBool("FineSightMode", isFineSightMode);

        if (isFineSightMode)
        {
            StopAllCoroutines();
            StartCoroutine(FineSightActivateCoroutine());
        }
        else
        {
            StopAllCoroutines();
            StartCoroutine(FineSightDeactivateCoroutine());
        }

    }

    //정조준 활성화
    IEnumerator FineSightActivateCoroutine()
    {
        while (currentGun.transform.localPosition != currentGun.fineSightOriginPos)
        {
            currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, currentGun.fineSightOriginPos, 0.2f);
            yield return null;
        }
    }

    //정조준 비활성화
    IEnumerator FineSightDeactivateCoroutine()
    {
        while (currentGun.transform.localPosition != originPos)
        {
            currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, originPos, 0.2f);
            yield return null;
        }
    }

    //총 반동
    IEnumerator RetroActionCoroutine()
    {
        Vector3 recoilBack = new Vector3(currentGun.retroActionForce, originPos.y, originPos.z);
        Vector3 retroActionRecoilBack = new Vector3(currentGun.retroActionFineSightForce, currentGun.fineSightOriginPos.y, currentGun.fineSightOriginPos.z);

        if (!isFineSightMode)
        {

            currentGun.transform.localPosition = originPos;

            // 반동 시작
            while (currentGun.transform.localPosition.x <= currentGun.retroActionForce - 0.02f)
            {
                currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, recoilBack, 0.4f);
                yield return null;
            }

            // 원위치
            while (currentGun.transform.localPosition != originPos)
            {
                currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, originPos, 0.1f);
                yield return null;
            }
        }
        else
        {
            currentGun.transform.localPosition = currentGun.fineSightOriginPos;

            // 반동 시작
            while (currentGun.transform.localPosition.x <= currentGun.retroActionFineSightForce - 0.02f)
            {
                currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, retroActionRecoilBack, 0.4f);
                yield return null;
            }

            // 원위치
            while (currentGun.transform.localPosition != currentGun.fineSightOriginPos)
            {
                currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, currentGun.fineSightOriginPos, 0.1f);
                yield return null;
            }
        }

    }

    //사운드 재생
    private void PlaySE(AudioClip _clip)
    {
        audioSource.clip = _clip;
        audioSource.Play();
    }
}

 

이번시간에는 명중과 피격 이팩트를 구현해보겠다

그전에 맨위에 올려둔 스크립트를 참고해서 우리가 작성한 스크립트가 어떤 기능을 가진 부분인지 스크립트를 참고해서 주석을 달아놓자

그리고 GunController 스크립트를 보자

아래에 분홍빛으로 표시한 부분을 작성해주자

 

다음은 void Start()함수이다

originPos를 Vector3.zero로 초기화해주자

 

다음은 void Shoot()함수이다

Hit()함수를 호출해주고 위 사진과 같이 수정해주자

 

다 작성되면 GunController스크립트에 Hit()함수를 작성하자

 

Hit()함수이다

98번줄에 조건문으로 Physics.Raycast를 작성하고 theCam.transform.position은 발사위치

theCam.transform.forward는 정면으로 발사하는 방향

out hitInfo는 접촉하는 위치

currentGun.range는 사정거리이다

그뒤 실행되는 함수는

GameObject clone로 변수를 지정해주고 Instantiate()함수 즉 인스턴스로

hit_effect_prefab를 생성해주고 생성위치는 hitInfo.point

바라보는 방향은 Quaternion.LookRotation(hitInfo.normal)로 작성해준다

 

이렇게 해주면 접촉하는 곳에 신호가 간다

이제 신호가 가는 접촉 방향에 히트이팩트가 생길 총알 피격 이팩트

파티클 시스템을 만들어주자

 

하이러키창에 마우스 오른쪽 클릭하고 Particle system을 만들자

일단 Rotation X 방향에 -90을 넣어서 생성되는 방향을 바꿔줍니다

그리고 Scale를 0.1, 0.1, 0.1로 맞춰줍니다

Start Color을 노란 빛으로 조절하여 총알이 튀는 듯한 연출을 해줍니다

Gravity Modifler을 0.4로 중력을 맞춰줍니다

Looping를 체크 해제해서 반복하지 않게 해줍니다

파티클의 Shape를 설정해줍시다

Angle를 70으로 올려서 앵글을 맞춰주고

Radius를 0.01로 맞춰서 범위를 최소로 내려줍니다

Color over Lifetime를 가면 갈 수 록 사라지게 세팅해줍시다

그 다음 Size over Lifetime를 가면 갈수록 내려가게 그래프를 그려줍시다

Trails를 체크하고 설정해줍니다

Width over Trail을 0.1로 설정하여 두깨를 설정해줍니다

LifeTime를 0.2로 설정하여 잔상을 빠르게 없세줍니다

Ratlo를 1로 만들어서 잔상이 몇개 나올지 설정해줍니다 최소값이 0.1부터 최대 값이 1까지입니다

Minimum Vertex Distanc...을 0.2로 설정하여 버택스 설정을 잡아줍니다

Clor over LifeTime를 오랜지색으로 설정하여 색을 설정합니다

이때 알파값을 건드리면 투명도를 설정할 수 있습니다

 

렌더러에서 Material은 Default-Particle를 검색해서 설정해주고

Trail Material은 Sprites-Default로 검색해서 이미지를 설정해줍니다

 

이제 만들어진 파티클을 프리팹화 해줍니다

프리팹폴더를 만들어 주고 Partcile 폴더를 만들어서 넣어주고 Hit_Effect로 이름을 명명해줍니다

이 이팩트는 불똥이 튀는 듯한 연출을 해줍니다

 

이제 인스팩터의 슬롯에 The Cam은 Main Camera를 넣어주고

Hit_effect_prefab 슬롯에는 우리가 오늘 만들어준 프리팹을 넣어줍니다

이렇게 해서 구현된 것을 확인하면!

 

이렇게 총알이 맞은 위치에 불똥이 튀는 것을 확인 할 수 있다

 

오늘 구현은 여기까지~~~~

'FPS서바이벌 디펜스' 카테고리의 다른 글

7.크로스헤어(조준점)  (0) 2025.04.23
6.HUD  (0) 2025.04.22
5-2.재장전, 정조준, 반동  (0) 2025.04.17
5.총 구현과 총구섬광  (0) 2025.04.15
4.팔 구현  (0) 2025.04.14