Gun.cs
6.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Gun : MonoBehaviour {
public Animator m_Animator; // 총의 애니메이터
public Transform m_FireTransform; // 총구의 위치를 나타내는 트랜스폼
public ParticleSystem m_ShellEjectEffect; // 탄피 배출 효과 재생기
public ParticleSystem m_MuzzleFlashEffect; // 총구 화염 효과 재생기
public AudioSource m_GunAudioPlayer; // 총 소리 재생기
public AudioClip m_ShotClip; // 발사 소리
public AudioClip m_ReloadClip; // 재장전 소리
public LineRenderer m_BulletLineRenderer; // 총알 궤적 랜더러
public GameObject m_ImpactPrefab; // 피탄 장소에 생성할 이펙트&데칼 원본
public Text m_AmmoText; // 남은 탄환의 수를 표시할 UI Text
public int m_MaxAmmo = 13; // 탄창의 최대 탄약 수
public float m_TimeBetFire = 0.3f; // 발사와 발사 사이의 시간 간격
public float m_Damage = 25;
public float m_ReloadTime = 2.0f;
public float m_FireDistance = 100f;
public enum State { Ready, Empty, Reloading };
public State m_CurrentState {get; private set;}
private float m_LastFireTime; // 총을 마지막으로 발사한 시점
private int m_CurrentAmmo = 0; // 탄창에 남은 현재 탄약 갯수
// Use this for initialization
void Awake() {
m_CurrentState = State.Empty; // 탄약이 빈 상태로 지정
m_LastFireTime = 0; // 마지막으로 총을 쏜 시점을 초기화
m_BulletLineRenderer.positionCount = 2; // 라인랜더러가 사용할 정점을 두개로 지정
m_BulletLineRenderer.enabled = false; // 라인랜더러를 끔
UpdateUI(); // UI를 갱신
}
// 발사 처리를 시도하는 함수
public void Fire()
{
// 총이 준비된 상태고 AND 현재 시간 >= 마지막 발사 시점 + 연사 간격
if(m_CurrentState == State.Ready && Time.time >= m_LastFireTime + m_TimeBetFire )
{
m_LastFireTime = Time.time; // 마지막으로 총을 쏜 시점이 현재 시점으로 갱신
Shot();
UpdateUI();
}
}
// 실제 발사 처리를 하는 부분
private void Shot()
{
RaycastHit hit; // 레이캐스트 정보를 저장하는, 충돌 정보 컨테이너
// 총을쏴서 총알이 맞은 곳 : 처음값으로는 총구 위치 + 총구 위치로 앞쪽 방향 * 사정거리
Vector3 hitPosition = m_FireTransform.position + m_FireTransform.forward * m_FireDistance;
// 레이캐스트(시작지점, 방향, 충돌 정보 컨테이너, 사정거리)
if(Physics.Raycast(m_FireTransform.position, m_FireTransform.forward, out hit, m_FireDistance))
{
// 상대방이 IDamageable 로서 가져와진다면,
// 상대방의 OnDamage 함수를 실행시켜서 데미지를 쥐어준다
IDamageable target = hit.collider.GetComponent<IDamageable>();
if(target != null)
{
target.OnDamage(m_Damage,hit.point,hit.point - m_FireTransform.position);
}
// 충돌 위치를 가져오기
hitPosition = hit.point;
// 피탄 효과 게임 오브젝트를 복제 생성, 충돌 지점에, 충돌한 표면의 방향으로 생성
GameObject decal = Instantiate(m_ImpactPrefab, hitPosition, Quaternion.LookRotation(hit.normal));
decal.transform.SetParent(hit.collider.transform);
}
// 발사 이펙트 재생 시작
StartCoroutine(ShotEffect(hitPosition));
// 남은 탄환의 수를 -1
m_CurrentAmmo--;
if(m_CurrentAmmo <= 0)
{
m_CurrentState = State.Empty;
}
}
// 발사 이펙트를 재생하고 총알 궤적을 잠시 그렸다가 끄는 함수
private IEnumerator ShotEffect(Vector3 hitPosition)
{
m_Animator.SetTrigger("Fire"); // Fire 트리거를 당김
// 총알 궤적 랜더러를 켬
m_BulletLineRenderer.enabled = true;
// 선분의 첫번째 점은 총구의 위치
m_BulletLineRenderer.SetPosition(0, m_FireTransform.position);
// 선분의 두번째 점은 입력으로 들어온 피탄 위치
m_BulletLineRenderer.SetPosition(1, hitPosition);
// 이펙트들 재생
m_MuzzleFlashEffect.Play(); // 총구 화염 효과 재생
m_ShellEjectEffect.Play(); // 탄피 배출 효과 재생
if (m_GunAudioPlayer.clip != m_ShotClip)
{
m_GunAudioPlayer.clip = m_ShotClip; // 총 발사 소리를 장전
}
m_GunAudioPlayer.Play(); // 총격 소리 재생
yield return new WaitForSeconds(0.07f); // 처리를 '잠시' 쉬는 시간
m_BulletLineRenderer.enabled = false;
}
// 총의 탄약 UI에 남은 탄약수를 갱신해서 띄어줌
private void UpdateUI()
{
if(m_CurrentState == State.Empty)
{
m_AmmoText.text = "EMPTY";
}
else if(m_CurrentState == State.Reloading)
{
m_AmmoText.text = "RELOADING";
}
else
{
m_AmmoText.text = m_CurrentAmmo.ToString();
}
}
// 재장전을 시도
public void Reload()
{
if(m_CurrentState != State.Reloading)
{
StartCoroutine(ReloadRoutin());
}
}
// 실제 재장전 처리가 진행되는 곳
private IEnumerator ReloadRoutin()
{
m_Animator.SetTrigger("Reload"); // Reload 파라미터 트리거를 당김
m_CurrentState = State.Reloading; // 현재 상태를 재장전 상태로 전환
m_GunAudioPlayer.clip = m_ReloadClip; // 오디오 소스의 클립을 재장전 소리로 교체
m_GunAudioPlayer.Play(); // 재장전 소리 재생
UpdateUI();
yield return new WaitForSeconds(m_ReloadTime); // 재장전 시간 만큼 처리를 쉰다
m_CurrentAmmo = m_MaxAmmo; // 탄약 최대 충전
m_CurrentState = State.Ready;
UpdateUI();
}
}