ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Ground Monk
    게임 클라이언트 개발/MakeDNF 2024. 4. 10. 00:07

    이번 글에선 지금까지 구현한 Ground Monk 캐릭터의 스킬을 작성할 것이다. Ground Monk는 던전 앤 파이터의 남 프리스트 인파이터를 모티브로 스킬을 구현했다.

    Ground Monk의 전투 영상

     

    기본 공격

    기본 공격은 총 5타로 이루어져 있다. 기본 공격 버튼(Default로 X키 지정)을 입력할 경우 연속적으로 기본 공격을 사용한다. 3타와 4타의 경우 방향키를 입력하지 않을 경우 약간 앞으로 전진하며, 캐릭터가 바라보는 방향의 반대 방향의 방향키를 입력할 경우, 움직이지 않고 사용 가능하다. (제자리 평타, 흔히 제평이라 부르는 기능)

    기본 공격 1타, 2타, 3타, 4타, 5타

     

    이전에 구현했던 Fire Knight 캐릭터와 마찬가지로 추가 키 입력을 HitboxActive와 MotionInProgress 두 상태에서만 가능하도록 구현하려 했으나, Ground Monk의 애니메이션의 프레임 수가 짧아서 MotionInProgress 상태를 제거했다. 또한 후딜레이 모션이 없어서, 모든 상태에서 유저의  추가 키 입력을 허용하도록 구현했다.

    // 유저가 스킬 키를 추가적으로 입력했을 때 호출되는 이벤트 함수
    public void OnSkillButtonPressed()
    {
        isContinue = true;
        character.Animator.SetBool(continueHash, true);
    }

     

    Ground Monk의 기본 공격

     

    개틀링 펀치 Gattling Punch

    개틀링 펀치 스킬은 기본 공격을 캔슬하고 사용할 수 있는 액티브 스킬이다. 총 7번의 연타 공격 후 마지막 피니쉬 펀치를 날려 적을 공격한다. 스킬 사용 시 슈퍼 아머 상태가 되도록 구현했다. 

    인파이터의 게틀링 펀치 연타 유무 타수 비교

     

    개틀링 펀치 스킬은 스킬 사용 중 스킬 키를 추가 입력할 경우 연타 공격 횟수 증가 및 공격 속도가 증가하는 메커니즘을 갖고 있다. 이를 위해 최대 입력 가능한 횟수를 설정하고, 스킬 키 입력 시 연타 공격 횟수 및 애니메이션 속도를 증가시키도록 로직을 작성했다.

    // 플레이어가 추가로 입력할 수 있는 최대 콤보 횟수
    private int maxAdditionalCombo = 3;
    
    // 플레이어가 추가로 스킬 키를 입력했을때 호출되는 이벤트 함수
    public void OnSkillButtonPressed()
    {
        if (maxCombo - stateController.numCombo >= maxAdditionalCombo) return;
    
        maxCombo++;
        attackSpeed += 0.3f;
    
        character.Animator.SetFloat(attackSpeedHash, attackSpeed);
    }

     

    기존에는 각 스킬의 상태를 표현하는 클래스의 부모 클래스인 SkillState 클래스에 플레이어가 추가로 스킬 키를 입력했을 때 호출되는 이벤트 함수들을 virtual 함수로 미리 선언하고, Character 클래스에서 이벤트 함수를 연결하였다. 하지만, 이렇게 구현할 경우 스킬의 추가 입력을 스킬이 등록된 키로만 가능하게 구현할 수 없다는 문제점이 발생했다. 또한, 추가 키 입력이 없는 스킬이라도 플레이어가 스킬 키를 계속해서 누르면 빈 함수를 계속해서 호출한다는 문제점이 발생했다.

    // Character.cs
    // keyName에 해당하는 키가 눌렸을 경우 호출되는 이벤트 함수.
    private void OnAttackButtonPressed(EKeyName keyName)
    {
        // AttackBehaviour 클래스의 이벤트 함수 호출
        attackBehaviour.OnSkillButtonPressed(keyName);
    
        // .. 캐릭터가 스킬을 사용할 수 있는지 체크하는 로직
    
        // keyName에 맞는 스킬을 사용
        attackBehaviour.Attack(keyName);
    }
    
    // AttackBehaviour.cs
    public void OnSkillButtonPressed(EKeyName keyName)
    {
        // keyName에 스킬이 등록되있지 않은 경우
        if (!registeredSkillDictionary.ContainsKey(keyName)) return;
            
        // keyName에 등록된 Skill 클래스의 이벤트 함수 호출
        registeredSkillDictionary[keyName].OnSkillButtonPressed();
    }
    
    // Skill.cs
    public void OnSkillButtonPressed()
    {
        if (curState == null) return;
        
        // 현재 상태의 SkillState 클래스의 이벤트 함수 호출
        curState.OnSkillButtonPressed();
    }
    
    // SkillState.cs
    // Bad : keyName에 해당하는 버튼이 눌렸을 때만 호출할 수 있음.
    // 또한 다른 키(Ex. 점프 키, 기본 공격 키 등)를 입력했을때 추가 효과는 구현할 수 없음.
    // Bad : 구현하지 않아도 함수는 계속해서 호출됨.
    public virtual void OnSkillButtonPressed() { }

     

    예를 들어 인게임의 개틀링 펀치 스킬의 경우, 공격 속도가 증가하는 추가 효과를 받기 위해 스킬 키를 입력해도 되지만, 기본 공격 키를 입력해도 공격 속도가 증가한다. 즉, 스킬 추가 키 입력은 스킬 버튼뿐만 아니라, 다른 버튼으로도 가능해야 한다는 소리이다. 또는 스킬 사용 중 점프 키를 입력하면 스킬 사용을 중단할 수 있어야 한다.

     

     

    이를 해결하기 위해, 추가 키 입력이 필요한 SkillState 클래스에서 직접 InputManager에 접근해 추가 키 입력을 이벤트로 등록하도록 수정하였다. 비록, 등록한 이벤트를 SkillState가 종료되거나 캔슬 됐을 때 해제해줘야 하는 번거로움이 있지만, 스킬을 구현함에 있어 클래스에 제약받지 않고 훨씬 자유롭게 기능을 구현할 수 있었다.

    // GattlingPunch_Combo.cs
    // SkillState 클래스를 상속받은 클래스.
    // 추가 키 입력이 필요한 클래스에 이벤트 함수를 따로 작성.
    public class Combo
    {
        // .. Other members
        
        public override void OnStart()
        {
            // .. SkillState 초기화 로직
            
            // 기본 공격 키를 입력 시, OnSkillButtonPressed 이벤트 함수 호출
            GameManager.Input.AddButtonDownDelegate(EKeyName.BASEATTACK, OnSkillButtonPressed);
            // 스킬이 등록된 키 입력 시, OnSkillButtonPressed 이벤트 함수 호출
            GameManager.Input.AddButtonDownDelegate(stateController.keyName, OnSkillButtonPressed);
        }
        
        // 플레이어가 스킬 사용 중 추가로 키를 입력할 경우 호출되는 이벤트 함수.
        public void OnSkillButtonPressed()
        {
            // .. 스킬 키를 추가로 입력했을때의 기능 로직 작성
        }
    }

     

    Ground Monk의 개틀링 펀치 스킬

     

    스웨이 Sway

    스웨이 스킬은 기본 공격을 캔슬하고 사용할 수 있는 액티브 스킬이다. 후방으로 대쉬하며, 시전 직후 잠깐동안 무적이 되는 스킬이다. 스웨이 스킬 사용 중에 스트레이트 펀치 스킬을 사용할 수 있다. Ease 함수를 통해 대쉬 가속도를 조절하여 회피하는 느낌을 살렸다.

     

    Ground Monk의 스웨이 스킬

     

    스트레이트 펀치 Straight Punch

    스트레이트 펀치 스킬은 스웨이 스킬 사용 중에만 사용할 수 있는 액티브 스킬이다. 스웨이 스킬을 캔슬하며 전방으로 주먹을 내질러 적을 공격한다. 스킬 사용 중에는 캐릭터가 슈퍼 아머 상태가 된다.

     

    이전에 Fire Knight의 회피 스킬을 구현할 때와 마찬가지로, 캐릭터가 현재 스웨이 스킬을 사용하고 있는지 확인하기 위해  Skill 클래스의 int형의 해쉬 코드로 비교 연산을 수행한다. 

    // StraightPunch.cs
    // 스킬을 사용할 수 있는지 검사하는 함수
    public override bool CheckCanUseSkill(ActiveSkill curSkill)
    {
        if (curSkill == null) return false;
    
        // 현재 캐릭터가 스웨이 스킬을 사용하는 중에만 스트레이트 펀치 스킬을 사용할 수 있음
        return curSkill.SkillCode == typeof(Sway).GetHashCode();
    }

     

    Ground Monk의 스트레이트 펀치 스킬

     

    핵펀치 Nuclear Punch

    핵펀치 스킬은 기본 공격을 캔슬하고 사용할 수 있는 액티브 스킬이다. 일정 반경에 적이 존재할 경우, 캐릭터의 전방으로 적을 끌어모은 뒤, 펀치를 내지른다. 이때, 펀치의 공격 범위는 매우 좁으나, 펀치에 피격된 적이 존재할 경우, 충격파를 생성하여 적을 공격한다.

     

    적을 캐릭터의 전방으로 모으는 로직을 작성할 때 유의했던 점은 거리 비교 연산이였다. 적을 끌어당기는 로직은 물리 연산이기 때문에 Unity의 FixedUpdate 시점에서 수행했다. 또한 적과의 거리 값이 중요한 것이 아닌, 적이 일정 거리 내에 존재하는지에 대한 여부만 검사하면 되기에, 제곱근 연산으로 인한 비싼 비용의 magnitude가 아닌 sqrMagnitude를 사용했고, float 타입의 변수는 0을 정확하게 표현할 수 없기 때문에, 입실론 테스트를 통해 적이 이미 타점에 위치했다면 끌어당기는 로직을 수행하지 않도록 예외처리하였다.

    public override void OnFixedUpdate()
    {
        // .. 몬스터를 모을 타점을 계산하기 위한 변수 초기화 로직
        
        foreach (IDamagable monster in monsters)
        {
            Vector3 monsterPos = monster.DefenderDNFTransform.Position;
            Vector3 gatheringVector = gatheringPoint - new Vector3(monsterPos.x, 0f, monsterPos.z);
    
            // 적이 일정 거리 내에 존재하거나, 타점에 이미 위치해있는 경우
            if (gatheringVector.sqrMagnitude > gatheringRange * gatheringRange 
                || gatheringVector.sqrMagnitude < GlobalDefine.EPSILON) continue;
    
            // .. 적을 계산한 방향으로 끌어당기는 로직
        }
    }

     

    Ground Monk의 핵펀치 스킬

     

    드라이 아웃 Dry Out

    드라이 아웃 스킬은 Ground Monk가 스웨이를 제외한 전직 계열 스킬을 사용 중이라면, 해당 스킬을 캔슬하고 다른 전직 계열의 스킬을 사용할 수 있는 패시브 스킬이다.

     

    기존엔 스킬을 구분짓기 위한 ESkillType 열거형의 요소로 Active와 Passive로만 구분지었으나, 기본 공격을 가리키는 BaseAttack, 캐릭터 공통 스킬인 Common, 그리고 전직 계열 스킬인 ClassSpecify를 추가하였다. 또한 Flags 속성을 추가하여 스킬은 Active, Passive 중 한 가지 타입과 BaseAttack, Common, ClassSpecify 중 한 가지 타입을 갖도록 구현했다.

    // 스킬 타입을 구분짓기 위한 열거형.
    // 스킬은 ACTIVE, PASSIVE 중 한 가지 속성을 가져야하며, 
    // BASEATTACK, COMMON, CLASSSPECIFIC 중 한 가지 속성을 가져야 함.
    [Flags]
    public enum ESkillType 
    {
        NONE = 0,
        
        ACTIVE = 1 << 0,
        PASSIVE = 1 << 1,
        
        BASEATTACK = 1 << 2,
        COMMON = 1 << 3,
        CLASSSPECIFIC = 1 << 4
    }

     

    Ground Monk의 드라이 아웃 스킬

     

Designed by Tistory.