1vdlrwnsv1 님의 블로그
Unity 클래스 구조 변경 본문
기존 Target 클래스
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Xml;
public class Target : MonoBehaviour
{
[Header("타겟 데이터")]
[SerializeField]private TargetSO data; // 프리팹에 직접 넣는 방식
private float currentHp;
[SerializeField]private Animator anim;
[SerializeField]private Text lvText;
[Header("시민일 경우 메테리얼")]
[SerializeField]private Material civilianMaterial;
[Header("사운드")]
[SerializeField]private AudioClip upSound;
[SerializeField]private AudioClip downSound;
[Header("UI")]
[SerializeField]private Image hpBar;
[SerializeField]private GameObject targetUI;
void Awake()
{
if (data == null)
{
TargetSO[] allDatas = Resources.LoadAll<TargetSO>("Data/SO/TargetSO");
if (allDatas.Length > 0)
{
data = allDatas[Random.Range(0, allDatas.Length)];
}
return;
}
}
void Start()//데이터 종류 별 메테리얼 변경
{
anim = GetComponent<Animator>();
if (data.Name == "Civilian")
{
Renderer rend = GetComponentInChildren<Renderer>();
if (rend != null)
{
rend.material = civilianMaterial;
}
}
else if (data.Name == "Airial")
{
Renderer rend = GetComponentInChildren<Renderer>();
if (rend != null)
{
rend.material.color = Color.cyan;
}
}
currentHp = data.Hp;
lvText.text = $"{data.Level}";
if(hpBar != null)
{
hpBar.fillAmount = 1f;
}
}
public void TakeDamage(float amount, Collider hitCollider)
{
if (currentHp <= 0) return;
anim.SetTrigger("Hit");
if (data.Name != "Civilian" && hitCollider != null && hitCollider.name == "Head")
{
amount = Mathf.RoundToInt(amount * data.DamageRate * 1.2f);
Debug.Log($"헤드샷 데미지: {amount}");
}
else
{
amount *= data.DamageRate;
Debug.Log($"바디샷 데미지: {amount}");
}
float realDamage = Mathf.Min(amount, currentHp);
currentHp -= realDamage;
Debug.Log($"{data.Name} 받은 데미지: {realDamage}");
hpBar.fillAmount = currentHp / data.Hp;
if (currentHp <= 0)
{
Die();
}
}
public void OnPlayerEnteredRange()
{
if (currentHp <= 0) return;
if (!anim.GetBool("Up")) // 안 올라가있을 때만
{
anim.SetBool("Up", true);
SoundManager.Instance.PlaySFX(upSound);
}
}
private void Die()
{
Destroy(targetUI);
anim.SetBool("Die", true);
SoundManager.Instance.PlaySFX(downSound);
}
}
타겟 데이터를 Scriptable Object로 부터 받아와 타겟 별 메테리얼 변경, 데미지 처리, 체력이 0 이하로 떨어지면 죽는 로직 담당, 사운드 출력 등
클래스 구조 변경 이유
상속 구조 도입: 타겟이 여러 종류(예: 시민, 적) 일 수도 있어서 공통적인 기능은 BaseTarget이라는 부모 클래스로 분리, 각 타입에 맞는 세부 구현은 BaseTarget을 상속받은 자식 클래스에서 처리하도록 변경
데미지 처리 개선: 시민 타겟은 데미지 계산 방식이 다름 TakeDamage 메서드를 BaseTarget에서 추상화하여 자식 클래스에서 오버라이드 할 수 있게 함
변경된 코드
BaseTarget 타겟의 공통 속석(HP, 애니메이션, UI등) 관리, TakeDamage메서드와 Die 메서드를 추상화하여 자식 클래스에서 오버라이드 가능하게
TakeDamage 메서드는 데미지 계산, 애니메이션 처리, HP 상태 갱신등의 공통 로직을 포함하고 있으며, 자식 클래스에서 필요한 부분 오버라이드
using UnityEngine;
using UnityEngine.UI;
public abstract class BaseTarget : MonoBehaviour
{
[Header("타겟 데이터")]
[SerializeField] protected TargetSO data;
protected float currentHp;
[SerializeField] protected Animator anim;
[SerializeField] protected Text lvText;
[Header("사운드")]
[SerializeField] protected AudioClip upSound;
[SerializeField] protected AudioClip downSound;
[Header("UI")]
[SerializeField] protected Image hpBar;
[SerializeField] protected GameObject targetUI;
protected virtual void Start()
{
anim = GetComponent<Animator>();
currentHp = data.Hp;
lvText.text = $"{data.Level}";
if (hpBar != null)
{
hpBar.fillAmount = 1f;
}
}
public abstract void TakeDamage(float amount, Collider hitCollider);
public void OnPlayerEnteredRange()
{
if (currentHp <= 0) return;
if (!anim.GetBool("Up")) // 안 올라가있을 때만
{
anim.SetBool("Up", true);
SoundManager.Instance.PlaySFX(upSound);
}
}
protected virtual void Die()
{
Destroy(targetUI);
anim.SetBool("Die", true);
SoundManager.Instance.PlaySFX(downSound);
}
}
CivilianTarget 클래스
BaseTarget을 상속받아 시민 타겟에 특화된 로직을 추가 시민 타겟은 데미지를 더 받고 메테리얼을 변경하는 로직
TakeDamage 메서드를 오버라이드 하여 시민 타겟에 맞는 데미지 로직을 구현, 시민 타겟의 경우 메테리얼을 변경하는 부분도 포함
using UnityEngine;
public class CivilianTarget : BaseTarget
{
[Header("시민일 경우 메테리얼")]
[SerializeField] private Material civilianMaterial;
protected override void Start()
{
base.Start();
if (data.Name == "Civilian")
{
Renderer rend = GetComponentInChildren<Renderer>();
if (rend != null)
{
rend.material = civilianMaterial;
}
}
}
public override void TakeDamage(float amount, Collider hitCollider)
{
if (currentHp <= 0) return;
anim.SetTrigger("Hit");
amount *= data.DamageRate;
Debug.Log($"데미지: {amount}");
float realDamage = Mathf.Min(amount, currentHp);
currentHp -= realDamage;
Debug.Log($"{data.Name} 받은 데미지: {realDamage}");
hpBar.fillAmount = currentHp / data.Hp;
if (currentHp <= 0)
{
Die();
}
}
}
나머지
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LandTarget : BaseTarget
{
protected override void Start()
{
base.Start();
}
public override void TakeDamage(float amount, Collider hitCollider)
{
if (currentHp <= 0) return;
anim.SetTrigger("Hit");
if (hitCollider != null && hitCollider.name == "Head")
{
amount = Mathf.RoundToInt(amount * data.DamageRate * 1.2f);
Debug.Log($"헤드샷 데미지: {amount}");
}
else
{
amount *= data.DamageRate;
Debug.Log($"바디샷 데미지: {amount}");
}
float realDamage = Mathf.Min(amount, currentHp);
currentHp -= realDamage;
Debug.Log($"{data.Name} 받은 데미지: {realDamage}");
hpBar.fillAmount = currentHp / data.Hp;
if (currentHp <= 0)
{
Die();
}
}
}
클래스 구조를 변경하여 타겟 시스템의 확장성과 재사용성 높였음
데미지 처리로직을 공통화 하고 각 타겟 별 다르게 처리할 수 있도록 다형성 적용
타겟 처리 로직을 별도의 클래스로 분리하여 다른 타겟 타입도 쉽게 추가 ㄱㄴ
'Unity 최종' 카테고리의 다른 글
탄피, 사운드 매니저, 오브젝트 풀 매니저 리팩토링 (0) | 2025.05.07 |
---|---|
Unity3D 적 탐지 시스템 구현 (0) | 2025.04.30 |
unity 오브젝트 풀링 매니저 (0) | 2025.04.25 |
유니티 PlayerPref를 이용한 사운드 값, 마우스 감도 값 저장 불러오기 (0) | 2025.04.23 |
옵션을 만들자 (0) | 2025.04.22 |