사용 패턴 및 구조
- 템플릿 매서드 패턴 및 상속 계층 구조
- ScriptableObject로 메모리 절약 유도
구조 설명
1. 추상 클래스로 공통적으로 사용할 것들을 모우는 BaseNPC 제작
2. 몬스터에게도 여러 종류가 존재할 것이기 때문에 Monster_Base에 BaseNPC를 상속
3. 각 종류별 몬스터에게 Monster_Base를 상속시켜 필요한 부분 제작
구조 제작 의도
- 상속을 이용해 중복 코드 최소화 및 확장성을 고려한 작업 진행
애니메이션 리소스 : 바로가기
Enemy Galore 1 - Pixel Art | 2D 캐릭터 | Unity Asset Store
Elevate your workflow with the Enemy Galore 1 - Pixel Art asset from Admurin. Find this & more 캐릭터 on the Unity Asset Store.
assetstore.unity.com
- 하나를 지정해서 애니메이션 적용
- hp표기용 hpbar 이미지 추가, Canvas Scaler을 화면 크기에 맞게 조정
HP바 추가
using UnityEngine;
public class UI_Follows_Object : MonoBehaviour
{
[SerializeField] RectTransform _myui;
[SerializeField] Transform _targetobject;
[SerializeField] Vector3 _offset;
Camera _maincamera;
void Start()
{
_maincamera = Camera.main;
}
void LateUpdate()
{
if (_targetobject == null || _maincamera == null)
{
return;
}
_myui.position = _maincamera.WorldToScreenPoint(_targetobject.position) + _offset;
}
}
- UI가 오브젝트를 따라다닐 수 있게 컴퍼논트 추가
공용으로 사용할 Scriptable Object추가
using UnityEngine;
[CreateAssetMenu(fileName = "SO_NPC", menuName = "SO_NPC", order = 0)]
public class SO_NPC : ScriptableObject
{
public int _Hp;
public int _Damge;
public int _Armor;
public float _Critical;// 0~1
public float _Critical_damage; // 0~1
public int TotalDamage()
{
float damage = _Damge;
var critical_random_value = UnityEngine.Random.Range(0f, 1f);
if (critical_random_value <= _Critical)
{
damage = damage * (_Critical_damage + 1);
}
return Mathf.FloorToInt(damage);
}
}
- 기본적으로 사용될 hp, damage, armor, critical, critical_damage 맴버변수 정의
- 모든 npc가 동일하게 사용할 데미지 불러오기를 ScriptableObject에 정의
공용 NPC 클래스 추가
using System;
using UnityEngine;
public abstract class BaseNPC : MonoBehaviour
{
//NPC 별 데이터
[SerializeField] SO_NPC _so_npc;
[SerializeField] AnimationController _animationController;
//기본 맴버변수
protected int _current_hp;
//이벤트 변수들
public event Action _die_event;
public event Action _hit_event;
public event Action _attack_event;
//함수
protected virtual void Start()
{
ReSetting();
_die_event += () => PlayAnimation(EANIMATION.DIE);
_hit_event += () => PlayAnimation(EANIMATION.HIT);
_attack_event += () => PlayAnimation(EANIMATION.ATTACK);
}
protected virtual void ReSetting()
{
_current_hp = _so_npc._Hp;
}
protected virtual void Target_To_Attack(BaseNPC target_npc)
{
var my_damage = _so_npc.TotalDamage();
target_npc.Hp_Update(my_damage);
_attack_event?.Invoke();
}
protected virtual void Hp_Update(int target_damage)
{
_current_hp -= target_damage;
_hit_event?.Invoke();
if (_current_hp <= 0)
{
NPC_Die();
}
}
protected virtual void NPC_Die()
{
_die_event?.Invoke();
}
protected virtual void PlayAnimation(EANIMATION eanimation)
{
_animationController.PlayAnimation(eanimation);
}
}
- event action을 이용해 캡슐화 및 확장성 있도록 구현
- 각 함수들에 virtual로 중복코드 최소화
공용 몬스터 클래스 추가
using System;
using UnityEngine;
public class Monster_Base : BaseNPC
{
[SerializeField] MoveController _moveController;
protected override void Start()
{
_moveController._move_event += () => PlayAnimation(EANIMATION.RUN);
}
public void OnRelease()
{
this.gameObject.SetActive(false);
_moveController.ReSetting();
}
public void OnSpawn()
{
this.gameObject.SetActive(true);
}
}
- 풀링을 이용해 몬스터를 생성할 것이기 때문에 OnSpanw, OnRelease, Create를 이용해 기본적으로 사용될 몬스터 코드 정의
기본 몬스터 클래스 추가
using UnityEngine;
public class Monster_Normal : Monster_Base
{
}
- 각 몬스터 종류별로 처리가 다를 것이기 때문에 Monster_Base를 상속한 클래스 추가
- 별도 처리가 필요하면 추가 예정
이동 클래스 추가
using System;
using UnityEngine;
public class MoveController : MonoBehaviour
{
public event Action _move_event;
[SerializeField] float _speed;
Vector2? _targetPosition = null;
void FixedUpdate()
{
if (!_targetPosition.HasValue)
{
return;
}
MoveToUpdate();
}
public virtual void MoveToTarget(Vector2 target)
{
_targetPosition = target;
_move_event?.Invoke();
}
public void ReSetting()
{
_targetPosition = null;
}
void MoveToUpdate()
{
Vector2 currentPosition = transform.position;
Vector2 target = _targetPosition.Value;
if (Vector2.Distance(currentPosition, target) > 0.01f)
{
Vector2 newPosition = Vector2.MoveTowards(currentPosition, target, _speed * Time.fixedDeltaTime);
transform.position = new Vector3(newPosition.x, newPosition.y, transform.position.z);
}
else
{
transform.position = new Vector3(target.x, target.y, transform.position.z);
_targetPosition = null;
}
}
}
- 이동하는 NPC에게만 달아주기 위한 MoveController추가
애니메이션 클래스 추가
using UnityEngine;
public class AnimationController : MonoBehaviour
{
[SerializeField] Animator _animator;
public void PlayAnimation(EANIMATION eanimationkind)
{
_animator.SetTrigger(eanimationkind.ToString());
}
}
public enum EANIMATION
{
IDLE,
RUN,
HIT,
DIE,
ATTACK
}
- 애니메이션 실행용 AnimationController추가
몬스터 프리팹 설정
- 몬스터 컴포넌트를 비롯해 Move, Hpbar, Animation등 각종 필요한 컴포넌트 추가
- Monster Layer추가 및 BoxCollder2D, RigidBody2D 추가
Move애니메이션에서만 움직이도록 이벤트 추가
public class MoveController : MonoBehaviour
{
...이하생략
public event Action _move_end_check;
public event Func<bool> _move_check;
...이하생략
void FixedUpdate()
{
if (!_targetPosition.HasValue)
{
return;
}
if (_move_check == null ? true : _move_check.Invoke())
{
return;
}
MoveToUpdate();
}
...이하생략
void MoveToUpdate()
{
Vector2 currentPosition = transform.position;
Vector2 target = _targetPosition.Value;
if (Vector2.Distance(currentPosition, target) > 0.01f)
{
Vector2 newPosition = Vector2.MoveTowards(currentPosition, target, _speed * Time.fixedDeltaTime);
transform.position = new Vector3(newPosition.x, newPosition.y, transform.position.z);
}
else
{
transform.position = new Vector3(target.x, target.y, transform.position.z);
_targetPosition = null;
_move_end_check?.Invoke();
}
}
}
- public event Action _move_end_check 를 추가하여 움직임이 멈췄을때 이벤트 추가
- public event Func<bool> _move_check 를 추가하여 움직임 여부 이벤트 추가
using System;
using UnityEngine;
public class Monster_Base : BaseNPC
{
[SerializeField] MoveController _moveController;
protected override void Start()
{
PlayAnimation(EANIMATION.IDLE);
_moveController._move_event += () => PlayAnimation(EANIMATION.MOVE, true);
_moveController._move_end_check += () => PlayAnimation(EANIMATION.MOVE, false);
_moveController._move_check += () => _animationController.CheckRunAnimation();
}
public void OnRelease()
{
this.gameObject.SetActive(false);
_moveController.ReSetting();
}
public void OnSpawn(Vector2 target)
{
this.gameObject.SetActive(true);
_moveController.MoveToTarget(target);
}
}
- MonsterBase 클래스에서 이동종료, 이동 체크 이벤트 추가
- OnSpawn할때 이동 타겟 바로 지정해주기
public class AnimationController : MonoBehaviour
{
[SerializeField] Animator _animator;
EANIMATION _eanimation;
public void PlayAnimation(EANIMATION eanimation)
{
_animator.SetTrigger(eanimation.ToString());
}
public void PlayAnimation(EANIMATION eanimation, bool isaction)
{
_animator.SetBool(eanimation.ToString(), isaction);
}
public bool CheckRunAnimation()
{
return _eanimation == EANIMATION.MOVE;
}
/// <summary>
/// 애니메이션 이벤트
/// </summary>
public void SetAnimatorEvent(EANIMATION eanimation)
{
_eanimation = eanimation;
}
}
- MoveController에 _move_check이벤트 추가 및 FixedUpdate에 체크하기
- SetAnimatorEvent를 이용해 애니메이션 재생 시 현재 애니메이션 상태를 저장한다.
using UnityEngine;
public class AnimationEvent : StateMachineBehaviour
{
[SerializeField] EANIMATION _eanimation;
// AnimationController 캐싱용
private AnimationController _controller;
// 상태 진입 시 호출
public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
// 첫 번째 호출 시에만 AnimationController 캐싱
if (_controller == null)
{
_controller = animator.GetComponent<AnimationController>();
}
if (_controller == null)
return;
// Inspector에서 설정한 애니메이션 타입으로 SetAnimatorEvent 호출
_controller.SetAnimatorEvent(_eanimation);
}
}
- Add Behaviour을 이용해 이벤트 추가 및 어떤 상태인지 지정
*공격과 데미지 닳는거 및 테스트는 다른 것을 만들고 난 후 추가 및 확인 예정
작업 시간
1차 - 6/10 22:40~23:20
2차 - 6/11 21:00~21:40 - BaseNPC(수정), BaseMonster(수정), MoveController(추가), AnimationController(추가)
3차 - 6/11 22:20~23:20 - Enemy Galore 1 - Pixel Art(리소스 추가), Monster_(프리팹 추가) 및 애니메이션 적용
4차 - 6/12 22:30~23:30 - UI_Follow_Ojbect(추가), Monster_ -> Monster_Bat(이름 변경 및 컴포넌트 추가), Hpbar(추가)
5차 - 6/13 21:00 ~ 21:40 / 6/14 11:20~12:40 - AnimationEvent(추가), AnimationController,MoveController,BaseMonster(수정) , Bat Animato(수정)
'유니티 > 게임 제작' 카테고리의 다른 글
[디펜스 게임 제작] 공격 처리 (0) | 2025.06.18 |
---|---|
[디펜스 게임 제작] 스킬 시스템 제작 (1) | 2025.06.15 |
[디펜스 게임 제작] 보호 오브젝트 (0) | 2025.06.15 |
[디펜스 게임 제작] 유니티 프로젝트 등록 및 Git 등록 (0) | 2025.06.10 |
[디펜스 게임 제작] 첫번째 게임 기획 (0) | 2025.06.09 |