티스토리 뷰
금일 한 것
▶ 에너미 시스템 추가
- 이동 로직(플레이어 추적) 구현
- 공격 기능 구현
- 피격과 이로 인한 사망 기능 구현
- 자동 생성 기능 추가
- 플레이어 근처에서는 생성되지 않음.
- 등급(미니언, 엘리트, 챔피언) 구현
- 엘리트: 미니언의 강화 버전으로 작성. 구간 별 보스 역할.
- 챔피언: 별개 패턴을 가진 유닛으로 구성. 최종 보스 역할.
※ 몬스터 및 플레이어의 스텟에 따른 차이점은 현재 생명력(Life) 이외에는 전무합니다.
┗ 추후 스킬 및 공격 관련 요소 모듈화 작업을 진행할 때에 스텟의 효과를 받도록 구성할 예정입니다.
┗ 또한, 같은 시기에 플레이어의 다른 능력치도 효력이 있게끔 구성할 예정입니다.
결과물(영상)
코드(일부)
에너미 - 부모 클래스 [겸 임시 챔피언 클래스]
더보기
[CreateAssetMenu(menuName = "Scriptables/Enemy/Base")]
public class Enemy_Base : ScriptableObject
{
public int Life_Max;
public int Atk;
public int Dec;
// 내성 [상흔 / 중독 / 심약]
public int[] Sta_Imm = new int[3];
// 상세 능력치
// ※ [피해량 = {공격력 * (100 + Atk_Mul) / (100 + Res_Mul) } - Dec]로 판정된다.
public int[] Res_Mul = new int[5]; // 피해 감소 보정치. [물리 / 화염 / 번개 / 신성 / 신비]
public float velocity = 0.04f;
protected float refeat = 0.05f;
public GameObject Projectile_Pref;
public GameObject Pulse_Pref;
public GameObject Orb_Pref;
public virtual void Patton(ref float timer, ref int count, GameObject Self_Unit)
{
GameObject Target_Unit = GameManager.Instance.Player_Unit;
int[] Damage = { 4, 0, 0, 0, 0 };
if (timer >= refeat)
{
if (count % 5 == 0)
Emit_Pulse(Self_Unit.transform.position, 5.0f, 2.5f, Damage);
if (count % 20 < 8)
{
Shoot_Proj(Self_Unit.transform.position, count * 15.0f, 0.05f, 1.5f, Damage);
Shoot_Proj(Self_Unit.transform.position, 120.0f + (count * 15.0f), 0.05f, 1.5f, Damage);
Shoot_Proj(Self_Unit.transform.position, -120.0f + (count * 15.0f), 0.05f, 1.5f, Damage);
}
if(count % 35 < 5 && count % 2 == 0)
{
float direction = Mathf.Atan2(Target_Unit.transform.position.y - Self_Unit.transform.position.y, Target_Unit.transform.position.x - Self_Unit.transform.position.x) * Mathf.Rad2Deg;
float distance = Vector3.Distance(Self_Unit.transform.position, Target_Unit.transform.position);
if (distance > 3.0f)
Shoot_Proj(Self_Unit.transform.position, direction, 0.1f, 3.5f, Damage);
}
timer -= refeat;
if (count >= 20)
count = 0;
else
count++;
}
}
public virtual void Move_Chara(float _velocity, GameObject Self_Unit)
{
GameObject Target_Unit = GameManager.Instance.Player_Unit;
// 탄젠트 값을 이용하여 Radian 각도를 구한 뒤 이를 Degree 각도로 치환하는 공식.
float Target_Degree = Mathf.Atan2(Target_Unit.transform.position.y - Self_Unit.transform.position.y, Target_Unit.transform.position.x - Self_Unit.transform.position.x) * Mathf.Rad2Deg;
// 두 점 간의 거리를 구하는 함수.
float distance = Vector3.Distance(Self_Unit.transform.position, Target_Unit.transform.position);
Self_Unit.transform.position += Quaternion.Euler(0.0f, 0.0f, Target_Degree) * Vector3.right * _velocity;
}
public void Shoot_Proj(Vector3 _Position, float _rotation, float _velocity, float _Size, int[] _Damage)
{
GameObject temp = Instantiate(Projectile_Pref, _Position, Quaternion.identity);
temp.SetActive(false);
Projectile temp_Proj = temp.GetComponent<Projectile>();
temp_Proj.is_friendly = false;
temp_Proj.velocity = _velocity;
temp_Proj.rotate = _rotation;
temp_Proj.Fork.Add(3);
temp_Proj.Damage = _Damage;
temp_Proj.Size = new Vector3(_Size, _Size, 1.0f);
temp.SetActive(true);
}
public void Emit_Pulse(Vector3 _Position, float E_Speed, float _Size, int[] _Damage)
{
GameObject temp = Instantiate(Pulse_Pref, _Position, Quaternion.identity);
temp.SetActive(false);
Pulse temp_Pul = temp.GetComponent<Pulse>();
temp_Pul.is_friendly = false;
temp_Pul.speed = E_Speed;
temp_Pul.Damage = _Damage;
temp_Pul.Size = new Vector3(_Size, _Size, 1.0f);
temp.SetActive(true);
}
public void Create_Orb(Vector3 _Position, float _Time_Max, float _Time_Term, float _Size, float _velocity, float _Direction, int[] _Damage)
{
GameObject temp = Instantiate(Orb_Pref, _Position, Quaternion.identity);
temp.SetActive(false);
Orb temp_Orb = temp.GetComponent<Orb>();
temp_Orb.is_friendly = false;
temp_Orb.Time_Max = _Time_Max;
temp_Orb.TIme_Term = _Time_Term;
temp_Orb.velocity = _velocity;
temp_Orb.Direction = _Direction;
temp_Orb.Damage = _Damage;
temp_Orb.Size = new Vector3(_Size, _Size, 1.0f);
temp.SetActive(true);
}
}
미니언 & 엘리트
더보기
[CreateAssetMenu(menuName = "Scriptables/Enemy/Minion_1")]
public class Minion_1 : Enemy_Base
{
public override void Patton(ref float timer, ref int count, GameObject Self_Unit)
{
GameObject Target_Unit = GameManager.Instance.Player_Unit;
int[] Damage = { 4, 0, 0, 0, 0 };
float direction = Mathf.Atan2(Target_Unit.transform.position.y - Self_Unit.transform.position.y, Target_Unit.transform.position.x - Self_Unit.transform.position.x) * Mathf.Rad2Deg;
float distance = Vector3.Distance(Self_Unit.transform.position, Target_Unit.transform.position);
if (timer >= refeat)
{
if (count >= 50)
count = 0;
if (distance < 1.0f)
{
if (count == 0)
Emit_Pulse(Self_Unit.transform.position, 2.5f, 2.5f, Damage);
count++;
}
else if (count != 0)
count++;
}
}
public override void Move_Chara(float _velocity, GameObject Self_Unit)
{
GameObject Target_Unit = GameManager.Instance.Player_Unit;
// 탄젠트 값을 이용하여 Radian 각도를 구한 뒤 이를 Degree 각도로 치환하는 공식.
float Target_Degree = Mathf.Atan2(Target_Unit.transform.position.y - Self_Unit.transform.position.y, Target_Unit.transform.position.x - Self_Unit.transform.position.x) * Mathf.Rad2Deg;
// 두 점 간의 거리를 구하는 함수.
float distance = Vector3.Distance(Self_Unit.transform.position, Target_Unit.transform.position);
Self_Unit.transform.position += Quaternion.Euler(0.0f, 0.0f, Target_Degree) * Vector3.right * _velocity;
}
}
자동 생성 코드
더보기
void Update()
{
for (int i = 0; i < Timer.Length; i++)
{
Timer[i] += Time.deltaTime;
}
if (Timer[1] >= 1.0f)
{
Create_Minion();
Timer[1] = 0.0f;
}
if (Timer[2] >= 5.0f) // 시간은 원래 180.0초로 둬야 하나, 테스트용이라 5초로 설정.
{
if (count < 2)
{
Create_Champion();
Timer[2] = 0.0f;
count++;
}
else if (count == 2)
{
Create_Chieftain();
Timer[2] = 0.0f;
count++;
}
}
}
void Create_Minion()
{
float Rand_x_Pos = Player_Unit.transform.position.x;
float Rand_y_Pos = Player_Unit.transform.position.y;
if (Random.Range(0.0f, 1.0f) > 0.5f)
Rand_x_Pos += Random.Range(2.5f, 8.0f);
else
Rand_x_Pos -= Random.Range(2.5f, 8.0f);
if (Random.Range(0.0f, 1.0f) > 0.5f)
Rand_y_Pos += Random.Range(2.5f, 8.0f);
else
Rand_y_Pos -= Random.Range(2.5f, 8.0f);
Instantiate(Minion_List[Random.Range(0, Minion_List.Count)], new Vector3(Rand_x_Pos, Rand_y_Pos, 0.0f), Quaternion.identity);
}
void Create_Champion()
{
float Rand_x_Pos = Player_Unit.transform.position.x;
float Rand_y_Pos = Player_Unit.transform.position.y;
if (Random.Range(0.0f, 1.0f) > 0.5f)
Rand_x_Pos += Random.Range(2.5f, 8.0f);
else
Rand_x_Pos -= Random.Range(2.5f, 8.0f);
if (Random.Range(0.0f, 1.0f) > 0.5f)
Rand_y_Pos += Random.Range(2.5f, 8.0f);
else
Rand_y_Pos -= Random.Range(2.5f, 8.0f);
GameObject Unit = Instantiate(Minion_List[Random.Range(0, Minion_List.Count)], new Vector3(Rand_x_Pos, Rand_y_Pos, 0.0f), Quaternion.identity);
Unit.SetActive(false);
Unit.GetComponent<Enemy_Cell>().is_Elite = true;
Unit.SetActive(true);
}
void Create_Chieftain()
{
float Rand_x_Pos = Player_Unit.transform.position.x;
float Rand_y_Pos = Player_Unit.transform.position.y;
if (Random.Range(0.0f, 1.0f) > 0.5f)
Rand_x_Pos += Random.Range(2.5f, 8.0f);
else
Rand_x_Pos -= Random.Range(2.5f, 8.0f);
if (Random.Range(0.0f, 1.0f) > 0.5f)
Rand_y_Pos += Random.Range(2.5f, 8.0f);
else
Rand_y_Pos -= Random.Range(2.5f, 8.0f);
GameObject Unit = Instantiate(Champion_List[Random.Range(0, Minion_List.Count)], new Vector3(Rand_x_Pos, Rand_y_Pos, 0.0f), Quaternion.identity);
Unit.SetActive(false);
Unit.GetComponent<Enemy_Cell>().is_Elite = true;
Unit.GetComponent<Enemy_Cell>().is_Champion = true;
Unit.SetActive(true);
}
마무리하며
전체적으로 클래스 간의 부모-자식 관계를 어떻게 구성할지를 고민하느라 시간이 오래 걸린 파트입니다.
결과적으로는 부모 쪽에서 거의 모든 요소를 포함하되, 공격과 이동 함수는 몬스터의 종류에 따라서 변화할 수 있도록 virtual - override 구성으로 작성하는 식으로 제작했습니다.
일단 적 사망 시에는 아무런 효과 없이 뿅 하고 없어진다는 점이나, 가시성을 위해서 충돌이 아니라 파동으로 공격하도록 구성했거나 하는 등, 추후 추가 작업을 해야하는 요소들이 이래저래 존재하긴 하지만, 일단 최소 기능으로는 완성했다고 봐도 될 거 같아요.
이번 작업을 하면서 "생각해 보니 데미지 숫자 표시 기능을 안넣었구나..." 싶었습니다.
일단 아마 간단하게 끝날 거 같긴 해서, 조만간 간단하게 작업해야겠네요.
'스파르타 내일배움캠프 > Today I Learned' 카테고리의 다른 글
(사전 캠프) Day 17 - 멍뭉이! 멍멍뭉! ~ 귀신 잡는 개멍대! (0) | 2024.09.04 |
---|---|
(사전 캠프) Day 16 - 멍뭉이! 멍멍뭉! ~ 귀신 잡는 개멍대! (4) | 2024.09.03 |
(사전 캠프) Day 14 - 멍뭉이! 멍멍뭉! ~ 귀신 잡는 개멍대! (0) | 2024.08.30 |
(사전 캠프) Day 13 - 간단한 역 기획 발표하기 (0) | 2024.08.29 |
(사전 캠프) Day 12 [멍뭉이! 멍멍뭉! - 귀신 잡는 개멍대!] (0) | 2024.08.28 |