[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 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

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

+ Recent posts