Enemy.cs
6.52 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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.AI;
public class Enemy : LivingEntity
{
public LayerMask explosionTarget;
public float explosionForce = 1000f; //폭심지에서 발생하는 힘
public enum State { Idle, Chasing, Attacking};
State currentState; // 현재 AI 상태
public ParticleSystem deathEffect; // 적이 죽었을때 재생할 이펙트
public ParticleSystem hitEffect; // 적이 공격받았을때 재생할 이펙트
public float damage = 50f; // 적이 폭발하면서 줄 데미지
public float explosionDistance = 5; // 폭발 반경
public float explosionSpeed = 1.0f; // 폭발하는 속도
AudioSource audioPlayer; // 사운드 재생기
public AudioClip hitSound; // 맞았을때 소리
NavMeshAgent pathFinder; // 경로 AI
Transform target; // 추적 대상
LivingEntity targetEntity; // 추적 대상으로부터 가져온 '생명' 정보
Material skinMaterial; // 표면의 색깔을 결정하는 애셋
public Color attackColor = Color.red; // 공격 컬러
private Color originColor; // 원래 머티리얼의 컬러
float attackDistanceThreshold = 3.5f; // 공격을 시도하는 거리
bool hasTarget; // 추적할 대상이 있는가?
private void Awake()
{
audioPlayer = GetComponent<AudioSource>();
pathFinder = GetComponent<NavMeshAgent>();
if(GameObject.FindGameObjectWithTag("Player") != null)
{
hasTarget = true;
target = GameObject.FindGameObjectWithTag("Player").transform;
targetEntity = target.GetComponent<LivingEntity>();
targetEntity.OnDeath += OnTargetDeath;
}
}
protected override void Start()
{
base.Start();
if(hasTarget)
{
currentState = State.Chasing;
StartCoroutine("UpdatePath"); //추적 루틴 시작
}
}
// 타겟이 죽었을때 체인으로 실행됨
void OnTargetDeath()
{
// 타겟이 죽었으니 더이상 추적안함
hasTarget = false;
currentState = State.Idle;
}
private void Update()
{
if(hasTarget)
{
float distance = Vector3.Distance(transform.position,target.position);
if(distance < attackDistanceThreshold && currentState != State.Attacking)
{
StartCoroutine("Attack");
}
}
}
// 외부에서 Enemy를 만들경우, 이것을 통해 디테일을 설정함
public void Setup(float moveSpeed, float newDamage,
float newHeath,Color skinColor)
{
pathFinder.speed = moveSpeed;
damage = newDamage;
startingHealth = newHeath;
// 이펙트와 표면의 색깔을 설정
deathEffect.startColor = skinColor;
skinMaterial = GetComponent<Renderer>().material;
skinMaterial.color = skinColor;
originColor = skinMaterial.color;
}
public override void OnDamage(float damage, Vector3 hitPoint, Vector3 hitDirection)
{
audioPlayer.clip = hitSound; // 클립을 교체하고
audioPlayer.Play(); // 데미지 입는 소리를 재생
var effectInstance = Instantiate(hitEffect, hitPoint,
Quaternion.FromToRotation(Vector3.forward, hitDirection));
effectInstance.startColor = skinMaterial.color;
effectInstance.Play();
//아래 코드는 파티클 시스템 컴포넌트 측의 StopAction을 Destroy를 사용함으로써 대체할수도 있음
Destroy(effectInstance.gameObject , effectInstance.duration);
base.OnDamage(damage, hitPoint, hitDirection);
}
public override void Die()
{
var effectInstance
= Instantiate(deathEffect, transform.position, transform.rotation);
var effectAudio = effectInstance.GetComponent<AudioSource>();
if(effectAudio != null)
{
effectAudio.Play();
}
effectInstance.Play();
Destroy(effectInstance.gameObject, effectInstance.duration);
base.Die();
}
IEnumerator UpdatePath()
{
float refreshRate = 0.25f;
// 추적할 대상이 존재하는 동안 경로 갱신을 무한루프
while(hasTarget)
{
if(currentState == State.Chasing)
{
if(!dead)
{
pathFinder.SetDestination(target.position);
}
}
yield return new WaitForSeconds(refreshRate);
}
}
IEnumerator Attack()
{
currentState = State.Attacking; // 공격하는 상태로 전환
pathFinder.isStopped = true; // AI 경로 추적을 중지
Color startColor = skinMaterial.color; // 시작
Color targetColor = attackColor; // 변환 완료로 될 컬러
float percent = 0f; // 공격 진행도
while(percent <= 1f)
{
percent += Time.deltaTime * explosionSpeed;
skinMaterial.color = Color.Lerp(startColor, targetColor, percent);
yield return null;
}
Debug.Log("공격을 했다! " + damage);
// transform.positino을 중심으로 구를 그려서
// 겹치는 모든 콜라이더를 가져옴
Collider[] attackTargets
= Physics.OverlapSphere(transform.position,
explosionDistance,explosionTarget);
for(int i = 0; i<attackTargets.Length; i++)
{
LivingEntity targetLivingEntity
= attackTargets[i].GetComponent<LivingEntity>();
if(targetLivingEntity != null)
{
Transform targetTransform = targetLivingEntity.transform;
targetLivingEntity.OnDamage(damage,
targetTransform.position, transform.forward);
Debug.Log("공격당한 상대방 체력: "
+ targetLivingEntity.health);
}
Rigidbody targetRigidbody = attackTargets[i].GetComponent<Rigidbody>();
if(targetRigidbody != null)
{
targetRigidbody.AddExplosionForce(explosionForce,
transform.position, explosionDistance);
}
}
Die(); // 자폭이라서 공격하면 스스로 죽음
}
}