본문 바로가기
유니티

[유니티]유니티에서 SO로 스킬 관리 시스템을 만들어 보자.

by chunma1126 2025. 4. 1.

대부분의 게임에서는 스킬들이 등장한다.

로그라이크 부터 RPG, FPS 등등 다양한 게임에서 스킬 시스템들이 존재 한다.

 

현재 내가 만들고 있는 게임도 로그라이크류 게임인데 스킬 시스템이 필요했다.

근데 로그라이크 게임이기 때문에 스킬들의 탈부착이 자유롭고, 수치들을 자유롭게 조절하기 위해 스킬들을 SO로 만들기로 했다.

구조

 

구조를 간단하게 그림으로 표현해보았다.

 

스킬들은 각 행동이 트리거가 되서 발동하는 구조로 이루어져 있다.

그리고 패시브 스킬 같은것들을 만들기 위해 update()라는 함수가 있다.

 

자세하게 글로 설명해 보도록 하겠다.

먼저 Player Skill Controller라는 클래스가 있다.

 

여기서 각 행동노드에 트리거가 될 액션 묶음이 있고 이 액션 묶음들을 딕셔너리로 관리하고 있다.

key는 행동이고 value는 스킬이다.

 

코드로 보여주겠다.

public event Action<Player,Transform[]> OnAttackEventSkill;
public event Action<Player,Transform[]> OnRollingEventSkill;
public event Action<Player,Transform[]> OnParryEventSkill;
public event Action<Player,Transform[]> OnHitEventSkill;
public event Action<Player,Transform[]> OnDeadEventSkill;
 private Dictionary<SkillType, Action<Player,Transform[]>> skillEvents;
  private void Awake()
{
    skillEvents = new Dictionary<SkillType, Action<Player,Transform[]>>()
    {
        { SkillType.Attack, OnAttackEventSkill },
        { SkillType.Rolling, OnRollingEventSkill },
        { SkillType.Parry, OnParryEventSkill },
        { SkillType.Hit, OnHitEventSkill },
        { SkillType.Dead, OnDeadEventSkill }
    };
}

 

이렇게 딕셔너리로 관리를 하는것이다.

때문에 각각의 행동의 트리거가 될곳에서 skill event를 불러와서 실행만 시켜주면 된다.

 

행동이 트리거가 되지 않는 패시브 스킬들은 따로 update함수 안에서 실행 시켜준다.

 private void Update()
 {
     foreach (var item in currentSkillList)
     {
         item.SkillUpdate(_player);
     }
 }

각 스킬들이 다 SO로 이루어져 있기 때문에 update를 실행할수 없다.

때문에 이렇게 skillController가 대신해서 현재 skill들을 저장했다가 update들을 실행시켜주어야 한다.

 

각각의 skill들은 skillData라는 abstractClass를 상속받고 있는데 반드시 useSkill을 구현시켜야한다.

namespace Swift_Blade.Skill
{
    public abstract class SkillData : ScriptableObject
    {
        public string skillName;
        public Sprite skillIcon;
        public SkillType SkillType;
        public StatType StatType;
        public ColorType colorType;
        
        [Range(1,100)] public int random;
        
        [TextArea] public string skillDescription;
        
        [Space(40)]
        public PoolPrefabMonoBehaviourSO skillParticle;
        
        public virtual void Initialize(){}

        public virtual void SkillUpdate(Player player, List<Transform> targets = null){}
        
        public abstract void UseSkill(Player player, Transform[] targets = null);
        
        public bool CheckSkill()
        {
            return Random.Range(0, 100) > random;
        }
        
    }
}

 

이렇게 이루어져 있다.

 

모든 스킬들이 다 확정적으로 실행되는것이 아니라 일정 확률로 실행될수도 있기 때문에 확인해주는 부분이 있다.

SkillType은 어떤 행동이 트리거가 될지 정하는거고 , StatType은 어떤 Stat과 관련이 있는지 정하는것이다.

 

namespace Swift_Blade.Skill
{
    [CreateAssetMenu(fileName = "AttackThunderSkill", menuName = "SO/Skill/Yellow/Thunder")]
    public class AttackThunderSkill : SkillData
    {
        [SerializeField] private ushort attackCount = 3;
        private ushort attackCounter = 0;
       
        [SerializeField] private int skillDamage = 0;

        public override void Initialize()
        {
            attackCounter = 0;
            MonoGenericPool<ThunderParticle>.Initialize(skillParticle);
        }

        public override void UseSkill(Player player,Transform[] targets = null)
        {
            ++attackCounter;
            
            if (attackCounter >= attackCount)
            {
                if (CheckSkill())
                {
                    foreach (var item in targets)
                    {
                        if(item.TryGetComponent(out BaseEnemyHealth health))
                        {
                            ActionData actionData= new ActionData();
                            actionData.damageAmount = skillDamage;
                            actionData.stun = true;
                        
                            health.TakeDamage(actionData);
                        
                            ThunderParticle th = MonoGenericPool<ThunderParticle>.Pop();
                            th.transform.position = item.transform.position + new Vector3(0,1,0);
                        }
                    }
                }
                
                attackCounter = 0;
                
            }
            
        }
        
    }
}

 

실제로 구현된 Thunder skill 이다.

 

이 스킬은 n번 적을 공격하면 주변적들에게 벼락을 내리쳐서 기절을 시키는 스킬인데 다음과 같이 구현되어 있다.

번개

실제로 적용된 모습이다.

 

SO로 스킬을 만들면 좋은점이 데이터 관리에 굉장히 용이하다.

스킬 SO

값을 이렇게 손쉽게 바꿔줄수 있기 때문에 밸런싱 작업에도 용이하다.

 

이렇게 처음부터 각잡고 구조짠게 오랜만이라 뭔가 낯설면서도 재밌었다.

확실히 계획을 세워놓고 하면 개발할때 편한거 같다.