[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 13회차 미션 시작합니다.
어느덧 챕터 5 진입이네요.
전투 시스템에 대하여 알아보는 시간입니다.
배우게 될 전체 구성 순서는 다음과 같습니다.
1. 전투 시스템 구성 알아보기
2. 전투 시스템 구현하기
3. 근접 전투 로직 구현하기
4. 원거리 전투 로직 구현하기
5. NPC의 HP UI 구현하기
+ EnemyController는 기존에 구현하였던 플레이어 캐릭터나 적캐릭터라고 보면 됩니다. 여기서는 Idle, Attack, Move State만 가지는 Controller를 구현합니다.
+ AttackStateController는 Animator에서 공격중인지 상태를 알게해주고, 이를 확인하여 EnemyController Logic에 전달하여 상태를 확인하도록 구현합니다. 물론 EnemyController 컴포넌트에서 직접 작업하여도 되지만, AttackStateController를 재활용할 수 있도록 별도의 컴포넌트로 제작을 하는 것입니다.
+ IAttackable은 Animator에서 공격이 발생하는 시점에 호출이되는 이벤트 인터페이스입니다. IAttackable 인터페이스를 따로 구현한 이유 역시 공격가능한 GameObject에 쉽게 부착하여 구현할 수 있도록 구성한 것입니다. 정말 좋은 내용입니다.
+ IDamageable도 마찬가지이지요. 데미지를 받아 에너지가 줄어드는 GameObject인 경우 적용합니다.
인터페이스의 장점은 클래스 상속과 상관없이 Attach/Dettach가 가능하고, 식별이 용이하다는 것이 장점입니다. 단점이라면 구조가 복잡해질 수 있다는 점입니다. Interface를 컴포넌트로 구성할 수도 있지만 참조가 많이 발생하게 되어 복잡성이 높아질 수 있어서 여기서는 Interface로만 구현합니다.
+ AttackBehaviour_xx는 공격 종류라고 보면 됩니다. EnemyController에서 여러 공격의 종류 중 하나를 선택하여 공격이 발생하는 시스템입니다.
AttackStateController와 Animator를 먼저 구성해 봅니다.
StateMachine은 위와 같이 구성합니다.
AttackStateController.cs
public class AttackStateController : MonoBehaviour
{
public delegate void OnEnterAttackState();
public delegate void OnExitAttackState();
public OnEnterAttackState enterAttackStateHandler;
public OnExitAttackState exitAttackStatehandler;
public bool IsInAttackState
{
get;
private set; // 외부에서 값 설정 불가.
}
void Start()
{
enterAttackStateHandler = new OnEnterAttackState(EnterAttackState);
exitAttackStateHandler = new OnExitAttackState(ExitAttackState);
}
public void OnStartOfAttackState()
{
IsInAttackState = true;
enterAttackStateHandler();
}
public void OnEndOfAttackState()
{
IsInAttackState = false;
exitAttackStateHandler();
}
private void EnterAttackState()
{
}
private void ExitAttackState()
{
}
// Animator에서 호출할 함수
public void OnCheckAttackCollider(int attackIndex)
{
GetComponent<IAttackable>()?.OnExecuteAttack(attackIndex);
}
}
AttackStateMachineBehaviour.cs
public class AttackStateMachineBehaviour : StateMachineBehaviour
{
public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
animator.getObject.GetComponent<AttackStateController>()?.OnStartOfAttackState();
}
public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
animator.getObject.GetComponent<AttackStateController>()?.OnEndOfAttackState();
}
}
IAttackable.cs
public interface IAttackable
{
AttackBehaviour CurrentAttackBehaviour
{
get;
}
void OnExecuteAttack(int attackIndex);
}
IDamageable.cs
public interface IDamageable
{
bool IsAlive
{
get;
}
void TakeDamage(int damage, GameObject hitEffectPrefabs);
}
AttackBehaviour.cs
public abstract class AttackBehaviour : MonoBehaviour
{
// 개발자가 어떤 컴포넌트인지 확인할 수 있도록 주석 만들기.
#if UNITY_EDITOR
[Multiline]
public string developmentDescription = "";
#endif
public int animationIndex;
public int priority; // 여러 공격중 어떤 것을 선택할지에 대한 우선순위
public int damage = 10;
public float range = 3f;
[SerializedField]
protected float coolTime; // 공격후 대기 시간?
protected float calcCoolTime; = 0.0f;
public GameObject effectPrefab;
[HideInInspector]
public LayerMask targetMask;
void Start()
{
calcCoolTime = coolTime; // 바로 공격 가능 상태
}
void Update()
{
if (calcCoolTime < coolTime)
{
calcCoolTime += Time.deltaTime;
}
}
// startPoint는 발사체가 발사되는 지점용
public abstract void ExecuteAttack(GameObject target = null, Transform startPoint = null);
}
EnemyController_New.cs
public EnemyController_New : EnemyController, IAttackable, IDamageable
{
public Transform projectilePoint;
[SerializedField]
private List<AttackBehaviour> attackBehavious = new List<AttackBehaviour>();
protected override void Start()
{
base.Start();
stateMachine.AddState(new MoveState());
stateMachine.AddState(new AttackState());
stateMachine.AddState(new DeadState());
InitAttackBehaviour();
}
protected override void Update()
{
CheckAttackBehaviour();
base.Update();
}
private void InitAttackBehaviour()
{
foreach (AttackBehaviour behaviour in attackBehavious)
{
if (CurrentAttackBehaviour == null)
CurrentAttackBehaviour = behaviour;
behaviour.targetMask = TargetMask;
}
}
private void CheckAttackBehaviour()
{
if (CurrentAttackBehaviour == null || !CurrentAttackBehaviour.IsAvailable)
{
CurrentAttackBehaviour = null;
foreach (AttackBehaviour behaviour in attackBehavious)
{
if (behaviour.IsAvailable)
{
// 가장 우선순위가 높은 공격 설정
if (CurrentAttackBehaviour == null || CurrentAttackBehaviour.priority < behaviour.priority)
{
CurrentAttackBehaviour = behaviour;
}
}
}
}
}
public AttackBehaviour CurrentAttackBehaviour
{
get;
private set; // 내부에서만 set 가능하도록..
}
public void OnExecuteAttack(int attackIndex)
{
if (CurrentAttackBehaviour != null && Target != null)
{
CurrentAttackBehaviour.ExecuteAttack(Target.gameObject, projectilePoint); // Target은 공격가능 거리내에 있는 타겟중 가장 가까운 것
}
}
}
적의 Animation 구동중에 OnCheckAttackCollider() 함수를 호출하도록 구현되어 있습니다.
캐릭터의 전투 시스템 구성은 C# 코딩으로 모두 이루어진다고 할 수 있겠네요.
이젠 배우는 내용들이 많아지므로 이전에 배운 내용은 코드 재사용을 하므로 정말 빠르게 진행됩니다.
이번에 배운 중요한 부분은 IAttackable이라고 봐야겠네요. 다음시간에는 IDamageable에 대해 구현하면 마무리가 될 것 같습니다.
<위의 코드들은 제가 보면서 주요한 함수, 코드를 확인하기 위해 타이핑한 용도로, 전체 소스코드가 아님에 주의해 주세요. 전체 코드는 교육 수강을 하면 완벽하게 받으실 수가 있답니다 ^^>
패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0
'[컴퓨터] > 웹 | 앱 | 게임 개발' 카테고리의 다른 글
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 15회차 미션 (2) | 2020.11.02 |
---|---|
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 14회차 미션 (0) | 2020.11.01 |
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 12회차 미션 (0) | 2020.10.30 |
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 11회차 미션 (0) | 2020.10.29 |
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 10회차 미션 (0) | 2020.10.28 |