Chapter 2-1. 무기 : 총 구현 1️⃣ (애니메이션, 파티클, 오디오)

Date:     Updated:

카테고리:

태그:

인프런에 있는 케이디님의 [유니티 3D] 실전! 생존게임 만들기 - Advanced 강의를 듣고 정리한 필기입니다. 😀
🌜 강의 들으러 가기 Click


Chapter 2. 무기

총 구현

🚖 총 플레이어에게 붙이기

image

서브 머신건을 들고 있는 팔 모델 프리팹을 씬에 임포트 한다. 이름은 “SubMachineGun1”로 바꿔준다.

image

image

  • Player
    • Main Camera
      • Weapon Camera
      • Holder (📜HandController.cs, 📜GunController.cs)
        • Handle Holder 서브 머신건 트랜스폼을 맞추기 위해 잠깐 비활성화 해두었다.
          • Hand (📜Hand.cs)
        • Gun Holder
          • 빈 게임 오브젝트로 총 오브젝트를 자식으로 두며 총의 트랜스폼을 대신 담당 한다.
          • 위치 (0, -0.8, 0), 회전 (0, 90, 0) 👈 기본 팔 붙일 때와 마찬가지로 Y축 90도 회전시켜 줌.
          • SubMachineGun1 (📜Gun.cs)
            • 트랜스폼은 원점으로 맞춘다. 부모인 Gun Holder의 트랜스폼을 변경해 서브 머신건의 트랜스폼은 Gun Holder 얘가 담당할 것.
            • Layer를 "Weapon"으로 설정해준다.
              • Weapon Camera가 오로지 SubMachineGun1만을 촬영할 수 있도록. 그래야 총이 나무나 다른 오브젝트에 파묻히지 않는다.

Hand 형 무기들은 Handle Holder의 자식으로 들어 갈 것이다. 원거리형 무기들은 Gun Holder의 자식으로 들어갈 것이다. 본인들의 트랜스폼은 원점이지만 부모인 땡땡 홀더들이 트랜스폼 값을 대신해서 담당하게끔. 나중에 두 번재 총 종류인 SubMachineGun2도 추가할 것이다.


📜Gun.cs

위에서 추가한 SubMachineGun1오브젝트에 붙인다.

나중에 다른 종류의 총 오브젝트들이 추가되면 그 곳에도 붙을 수 있다.

여러 종류의 총들의 공통적인 부분.

📜Hand.cs 와 비슷하게 짜면 된다.

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

public class Gun : MonoBehaviour
{
    /* ✨모든 종류의 총들이 공통적으로 갖고 있는 속성들(멤버 변수들)✨ */

    public string gunName;  // 총의 이름. 총의 종류가 여러개이기 때문에.
    public float range;     // 총의 사정 거리. 총마다 사정 거리 다름. 총알이 너무 멀리까지 영원히 날아가면 안되니까.
    public float accuracy;  // 총의 정확도. 총의 종류마다 정확도가 다름.
    public float fireRate;  // 연사 속도. 즉 한발과 한발간의 시간 텀. 높으면 높을 수록 연사가 느려짐. 총의 종류마다 다름.
    public float reloadTime;// 재장전 속도. 총의 종류마다 다름.

    public int damage;      // 총의 공격력. 총의 종류마다 다름.

    public int reloadBulletCount;   // 총의 재장전 개수. 재장전 할 때 몇 발씩 될지. 총의 종류마다 다름.
    public int currentBulletCount;  // 현재 총 안의 탄창에 남아있는 총알의 개수.
    public int maxBulletCount;      // 총알을 최대 몇 개까지 소유할 수 있는지. 
    public int carryBulletCount;    // 현재 총 바깥에서 소유하고 있는 총알의 총 개수.

    public float retroActionForce;  // 반동 세기. 총의 종류마다 다름.
    public float retroActionFineSightForce; // 정조준시 반동 세기. 총의 종류마다 다름.

    public Vector3 findSightOriginPos;  // 정조준시 총이 향할 위치. 정조준 할 때 총의 위치가 변하니까 그 때의 위치!

    public Animator anim;   // 총의 애니메이션을 재생할 애니메이터 컴포넌트
    public ParticleSystem muzzleFlash;  // 화염구 이펙트 재생을 담당할 파티클 시스템 컴포넌트
    public AudioClip fire_Sound;    // 총 발사 소리 오디오 클립

}

image

아직 할당 안한 것들이 많다. 일단 몇 개만 값을 설정함.


🚖 총 애니메이션

총 애니메이션 만들기

팔 애니메이션 하나를 가지고 프레임 별로 여러개 쪼개어 애니메이션을 만들었던 것처럼 총도 똑같은 방식으로 동작에 따라 프레임 구간을 떼와 애니메이션들을 만들자. 임포트 받은 서브 머신건 1의 3D 모델 파일(lowPoly_arms_SubMachineGun)또한 이미 애니메이션이 내장되어 있어서 이 애니메이션 프레임을 쪼개어서 각 상황에 맞는 애니메이션을 만들어볼 것이다.

image

  • 총 8 개의 애니메이션 파일을 만들었다.
  • Idle, Walk, Run
    • 👉 한번 재생 되면 계속 재생시킬 것이라서 Loop Time과 Loop Pose 를 체크해준다.
  • 나머지는 반복 필요 없이 한번만 재생 되면 되므로 이 부분을 체크하지 않는다.
    • Reload : 재장전 애니메이션
    • FindSightMode : 정조준시 애니메이션
    • Weapon_Out : 총을 꺼낼 때 애니메이션
    • Weapon_In : 총을 꺼낸 후 애니메이션
    • Item : 총을 든 상태에서 아이템을 주울 때 애니메이션

발사 할 때 재생될 반동 애니메이션은 없다. 총 자체를 위아래로 움직여서 구현할 것이라서 따로 없다!


총 애니메이션 컨트롤러

  • “GunController” 이름의 총 애니메이션 컨트롤러를 만든다.
  • SubMachineGun1의 Animator 컴포넌트의 Controller 에 할당
  • 상태도에 위에서 만든 8 개의 총 관련 애니메이션 파일들을 드래그 해준다.

image

파라미터는 위와 같이 3 가지를 추가 해준다. Reload만 Trigger고, Walk, Run은 Bool.

image

  • Any State 👉 Gun0_Reload
    • 조건
      • 어떤 상태이던 간에 Reload Trigger가 발동하면 즉시 재장전 애니메이션을 재생한다.
      • 즉시 재생하게 Hax Exit Time 체크 해제
  • Gun0_Reload 👉 Gun0_Idle
    • 조건
      • 재장전 애니메이션이 완전히 끝나면 바로 총들고 가만히 있을 때 애니메이션을 재생한다.
      • Has Exit Time 체크
      • Exit Time 은 1 로 설정. 재장전 애니메이션을 100퍼센트 재생 다 할 수 있도록.
      • Translation Duration은 0으로 설정. 딜레이 없이 즉시 변경.
  • 나머지 애니메이션은 HandController 에서 짰던 것처럼 똑같이 전이 조건 설정해주면 된다.
    • Walk, Run 의 True, False
    • Has Exit Time 체크 해제
    • Translation Duration은 0.1 으로 설정


📜GunController.cs

Holder에 붙인다.

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

public class GunController : MonoBehaviour
{
    [SerializeField]
    private Gun currentGun; // 현재 들고 있는 총의 📜Gun.cs 가 할당 됨.

    private float currentFireRate; // 이 값이 0 보다 큰 동안에는 총알이 발사 되지 않는다. 초기값은 연사 속도인 📜Gun.cs의 fireRate 

    private AudioSource audioSource;  // 발사 소리 재생기

    void Update()
    {
        GunFireRateCalc();
        TryFire();
    }

    private void GunFireRateCalc()
    {
        if (currentFireRate > 0)
            currentFireRate -= Time.deltaTime;  // 즉, 1 초에 1 씩 감소시킨다.
    }

    private void TryFire()  // 발사 입력을 받음
    {
        if(Input.GetButton("Fire1") && currentFireRate <= 0)
        {
            Fire();
        }
    }

    private void Fire()  // 발사를 위한 과정
    {
        currentFireRate = currentGun.fireRate;
        Shoot();
    }

    private void Shoot()  // 실제 발사 되는 과정
    {
        Debug.Log("총알 발사 함");
    }
}
  • currentFireRate
    • 단발로 여러번 쏠 때, 그 한발 한발간의 텀이 길 수록 연사 속도가 낮은 것이다.
    • 📜Gun.cs 에서 연사 속도(한발 한발 사이의 텀)인 fireRate값이 0.2 이라고 예를 들어 보면, currentFireRate = fireRate 이렇게 currentFireRate은 0.2 에서 시작하고, 이 currentFireRate를 1 초마다 1 씩 감소 시킨다.
      • currentFireRate가 0 보다 클 때까진 발사를 못하게 한다.
      • 따라서 이 총은 한번 발사한 후 0.2초 동안은 발사를 못하는 것과 같다! 연사 속도를 0.2라고 볼 수 있다.
  • 매 프레임마다
    • GunFireRateCalc()
      • 👉 currentFireRate을 Time.deltaTime 만큼 감소시킨다. (즉, 1초 동안 1씩 감소하는 것과 마찬가지)
    • TryFire()
      • 👉발사해도 되는 상황인지를 검사한다.
        • 마우스 좌클 입력이 들어왔는지 검사
        • 그리고 currentFireRate가 0 이하인지를 검사한다.
        • 둘 다 만족한다면 Fire()를 실행한다.
      • Fire()
        • 👉 발사를 위한 과정
          • 이제 발사를 곧 바로 할 것이니 currentFireRate을 다시 📜Gun.cs의 fireRate 값으로 초기화 해야 한다. currentFireRate값은 fireRate에서 시작하여 다시 매 프레임마다 Time.deltaTime 만큼 감소되며, 이 값이 0 이하로 떨어질 때까진 발사하지 못한다.
          • 실제 발사를 처리하는 Shoot()을 실행한다.
            • Shoot()
              • 👉 실제 발사를 처리하는 과정.

image


🚖 총 발사시 화염구 이펙트, 파티클 시스템

Hierarchy - 우클 - Create - Effect - Particle System

파티클 시스템 1 : 이펙트 효과

이름은 “MuzzleFlash”

image

  • MuzzleFlash 오브젝트의 Transform Scale 을 (0.1, 0.1, 0.1) 로 줄여주었다.
    • 입자 크기를 줄임.
  • MuzzleFlash의 Particle System 컴포넌트 설정 문서 참고
    • Duration 은 1 로 설정한다.
      • 파티클 시스템이 실행되는 지속 시간
    • Looping 은 체크 해제 한다. 반복 재생 X
    • Play On Awake 해제 한다.
      • 이걸 체크하면 게임이 시작되자마자 해당 파티클 시스템이 재생된다.
    • Start Lifetime 을 0.15 로 설정한다.
      • 파티클의 초기 수명.
      • 0.15 로 크게 낮춰서 파티클 입자들이 눈깜짝할 새에 사라지는 것을 볼 수 있다. 역시 총 발사 이펙트 효과에 적합할 것 같다!
    • Start Speed 를 25 으로 설정한다.
      • 해당 방향으로 작용하는 파티클 초기 속도
    • Emissions 모듈 👉 한 순간에 빵 터지듯이 동시에 방출될 입자 수. 한발에 동시에 총알 여러발 나가는 총의 이펙트 효과를 구현할 때 좋을 것 같다.
      • Rate over Time 은 0 으로 설정한다.
        • 시간 단위 당 방출되는 파티클 수
      • Bursts 👉 파티클을 생성하는 이벤트
        • Count는 15로 설정한다.
          • 동시에 한번에 방출되는 파티클 수
    • Shape 모듈 👉 파티클 입자들이 방출되는 모양을 결정한다. 구라면 구 모양으로 파티클 입자들이 방출되는 식이다.
      • Shape를 Sphere 로 설정한다.
        • 파티클 입자들이 구 모양을 그리며 모여서 방출된다. 사방으로 발사되긴 하는데 그게 구 형태로 발사되는 식이다. 이 모양 내에서 랜덤한 위치에서 파티클 입자들이 방출된다. 총구는 동그랗기 때문에 역시 총 발사 이펙트 효과에 적합할 것 같다!
      • Radius 는 0.01 로 최소로 설정한다. 위에서 설정한 Shpae의 반지름이 된다. 이 반경 내에서 입자들이 태어난다.
        • 총구 에서만 파티클 효과를 줄 것이기 때문에
    • Color Over Lifetime 모듈 👉 체크. 수명 주기에 따라 파티클의 색깔과 투명도가 어떻게 변하는지를 설정할 수 있다. image
      • 저 직사각형 가로 길이가 파티클 입자의 전체 수명 주기이다.
      • 우클로 섹션을 추가할 수 있는데, 이 섹션들을 누르면 섹션이 위치한 주기의 구간 별로 색상 혹은 투명도를 다르게 지정할 수 있다.
        • 위의 섹션 👉 투명도
        • 아래 섹션 👉 색상
      • 파티클이 점점 노래지다가 중간즈음부터 점점 투명해져 자연스럽게 수명을 다하는 컬러를 나타냈다.
        • 1 번 섹션의 투명도를 256으로 한 후 수명 주기의 중간에 위치하게 하고 2 번 섹션의 투명도를 0 으로 한 후 수명 끝에 위치하게 하면 수명의 중간까진 불투명했다가 중간 이후부턴 점점 투명해지게 만들 수 있다.
        • 3, 4 번 섹션의 색상을 노란색으로 추가해서 수명에서 3,4 번 주변 주기 땐 노랗게 변하게 할 수 있다.
    • Size Over LifeTime 모듈 👉 체크. 수명 주기에 따라 파티클의 크기가 어떻게 변하는지도 설정할 수 있다. image
      • 사이즈가 처음엔 최대이다가 수명 주기 동안 점점 곡선을 그리는 형태로 작아지고 사라진다.
    • Renderer 모듈 👉 파티클의 이미지나 메시가 어떻게 변환되고 응영처리 되고 덮어 쓰여지는지를 결정한다. 총알 피격 효과처럼 파티클 입자들을 동그랗게 말고 날카롭게 만들어 보자
      • Render Mode 를 Stretched Billboard 로 설정한다.
        • Render Mode 는 렌더링 된 이미지가 생성되는 방식이다.
          • Stretched Billboard 로 설정하면 파티클은 항상 카메라를 향하지만 다양한 Scale 이 적용 된다.
            • Camera Scale 은 0 으로
            • Speed Scale 은 -0.2 로
              • 속도에 비례하여 파티클이 뻗어진다.
              • 그냥 0.2 양수로 설정하면 바깥에서 안으로 들어오는 방향이더라.. -0.2 음수로 설정하면 안에서 바깥으로 방향이 반전되서 더 자연스럽다.
            • Length Scale 은 0 으로 설정한다. image
    • Texture Sheet Animation 모듈 👉 파티클 입자에 텍스처 또는 애니메이션을 넣을 수 있다!
      • Grid 모드 : 일반 적인 입자
      • Sprite 모드 : 특정 Sprite 이미지로 대체
        • 인스펙터에서 파티클 입자에 입힐 이미지 파일의 Texture Type을 Sprite(2D and UI)로 바꿔준 후 이 미지 파일을 할당해 준다.
        • Sprite 에 맞게 입자의 Material도 바꿔주는게 자연스럽다. Renderer 모듈의 Material을 “Sprites-Default” 로 바꿔준다. (스프라이트에 맞는 Material)


파티클 시스템 2 : 화염 이펙트 효과

편리하게 위의 Particle System을 복사하여 만든다. 번쩍 하는 화염 이펙트 효과를 구현함. 이름은 “MuzzleFlash_inner”

image

  • 트랜스폼 Scale 은 (1, 1, 1)로 설정.
  • Start Speed 를 0 으로 설정.
  • Emissions - Bursts - Count 를 1 로 설정.
    • 입자 하나만 생성 및 방출
  • Textrue Sheet Animation 체크 해제
  • Renderer - Render Mode - Billboard 설정
    • Renderer - Material 은 다시 “Default-Particle” 기본 머테리얼로.
  • 나머지는 “MuzzleFlash”과 동일

image

image

“MuzzleFlash_inner”를 “MuzzleFlash” 의 자식으로 넣어주니 다음과 같은 이펙트 효과가 완성 되었다.


파티클 시스템 배치

image

image

MuzzleFlash - MuzzleFlash_inner 파티클 시스템을 SubMachineGun의 자식인 Amature의 자식인 Gun 자식으로 넣어준다. 이제 총을 따라다니게 된다. 파티클 시스템을 총구 쪽에 위치시켜 준다. 마찬가지로 총의 자식이 되었으니 Layer를 “Weapon”으로 해준다.


총을 발사할 때마다 파티클 시스템 재생

image

우선 📜Gun.cs 에 만들어 둔 MuzzleFlash 파티클 시스템을 muzzleflash 변수에 할당한다.

📜GunController.cs

    private void Shoot()  
    {
        currentGun.muzzleFlash.Play();  // ✨✨
        Debug.Log("총알 발사 함");
    }


🚖 총 발사 소리 재생

📜GunController.cs

    void Start()
    {
        audioSource = GetComponent<AudioSource>();
    }

    private void Shoot()  // 실제 발사 되는 과정
    {
        PlaySE(currentGun.fire_Sound);
        currentGun.muzzleFlash.Play();
        Debug.Log("총알 발사 함");
    }

    private void PlaySE(AudioClip _clip)  // 발사 소리 재생
    {
        audioSource.clip = _clip;  // 오디오 클립 할당
        audioSource.Play();       // 오디오 재생
    }

위와 같이 함수를 추가해주고 이 스크립트가 붙어 있는 Holder에 Audio Source 컴포넌트를 추가한다.

image

SubMachineGun1의 📜Gun.cs 의 fire_Sound에 발사 소리로 쓸 오디오 클립을 할당한다.

image

게임 실행 후 발사하면 알아서 Audio Source 컴포넌트의 AudioClip 에 fire_Sound에 할당했던 오디오 클립이 들어오고 재생이 될 것이다.


🚖 여기까지 스크립트 정리

📜Gun.cs

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

public class Gun : MonoBehaviour
{
    /* 모든 종류의 총들이 공통적으로 갖고 있는 속성들(멤버 변수들) */

    public string gunName;  // 총의 이름. 총의 종류가 여러개이기 때문에.
    public float range;     // 총의 사정 거리. 총마다 사정 거리 다름. 총알이 너무 멀리까지 영원히 날아가면 안되니까.
    public float accuracy;  // 총의 정확도. 총의 종류마다 정확도가 다름.
    public float fireRate;  // 연사 속도. 즉 한발과 한발간의 시간 텀. 높으면 높을 수록 연사가 느려짐. 총의 종류마다 다름.
    public float reloadTime;// 재장전 속도. 총의 종류마다 다름.

    public int damage;      // 총의 공격력. 총의 종류마다 다름.

    public int reloadBulletCount;   // 총의 재장전 개수. 재장전 할 때 몇 발씩 될지. 총의 종류마다 다름.
    public int currentBulletCount;  // 현재 탄창에 남아있는 총알의 개수.
    public int maxBulletCount;      // 총알을 최대 몇 개까지 소유할 수 있는지. 
    public int carryBulletCount;    // 현재 소유하고 있는 총알의 총 개수.

    public float retroActionForce;  // 반동 세기. 총의 종류마다 다름.
    public float retroActionFineSightForce; // 정조준시 반동 세기. 총의 종류마다 다름.

    public Vector3 findSightOriginPos;  // 정조준시 총이 향할 위치. 정조준 할 때 총의 위치가 변하니까 그 때의 위치!

    public Animator anim;   // 총의 애니메이션을 재생할 애니메이터 컴포넌트
    public ParticleSystem muzzleFlash;  // 화염구 이펙트 재생을 담당할 파티클 시스템 컴포넌트
    public AudioClip fire_Sound;    // 총 발사 소리 오디오 클립

}

📜GunController.cs

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

public class GunController : MonoBehaviour
{
    [SerializeField]
    private Gun currentGun; // 현재 들고 있는 총. 📜Gun.cs 가 할당 됨.

    private float currentFireRate; // 이 값이 0 보다 큰 동안에는 총알이 발사 되지 않는다. 초기값은 연사 속도인 📜Gun.cs의 fireRate 

    private AudioSource audioSource;  // 발사 소리 재생기

    void Start()
    {
        audioSource = GetComponent<AudioSource>();
    }

    void Update()
    {
        GunFireRateCalc();
        TryFire();
    }

    private void GunFireRateCalc()
    {
        if (currentFireRate > 0)
            currentFireRate -= Time.deltaTime;  // 즉, 1 초에 1 씩 감소시킨다.
    }

    private void TryFire()  // 발사 입력을 받음
    {
        if(Input.GetButton("Fire1") && currentFireRate <= 0)
        {
            Fire();
        }
    }

    private void Fire()  // 발사를 위한 과정
    {
        currentFireRate = currentGun.fireRate;
        Shoot();
    }

    private void Shoot()  // 실제 발사 되는 과정
    {
        PlaySE(currentGun.fire_Sound);
        currentGun.muzzleFlash.Play();
        Debug.Log("총알 발사 함");
    }

    private void PlaySE(AudioClip _clip)  // 발사 소리 재생
    {
        audioSource.clip = _clip;
        audioSource.Play();
    }
}



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

댓글남기기