본문 바로가기
TIL

[멋쟁이사자처럼 부트캠프 TIL 회고] 유니티 게임개발 3기 32일차 디자인패턴 몬스터 제작

by HYttt 2025. 1. 7.

디자인 패턴을 이용해 몬스터 제작

  • Entity를 Player, Monster에 상속시키켜 컴포넌트 관리
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[
    RequireComponent(typeof(Rigidbody)), 
    RequireComponent(typeof(CapsuleCollider)),
    RequireComponent(typeof(StateMachine)),
    RequireComponent(typeof(Animator)),
    RequireComponent(typeof(CustomTag)),
    RequireComponent(typeof(SkillControllerJ)),
    RequireComponent(typeof(SkillControllerJ_V2))
]
public abstract class EntityJ : MonoBehaviour
{
    protected StateMachine stateMachine;
    protected Rigidbody rigidbody;
    
    protected virtual StaterType EnityStaterType => StaterType.None;

    void Awake()
    {
        stateMachine = GetComponent<StateMachine>();
        rigidbody = GetComponent<Rigidbody>();
        rigidbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
    }

    protected virtual void Start()
    {
        stateMachine.Run(EnityStaterType);
    }

    // Update is called once per frame
    void Update()
    {
        stateMachine?.UpdateState();
    }
}
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

[
    RequireComponent(typeof(Blackboard_Monster))
]
public class MonsterJ : EntityJ
{
    Blackboard_Monster monster;
    protected override StaterType EnityStaterType => StaterType.Monster;

    protected override void Start()
    {
        base.Start();
        monster = GetComponent<Blackboard_Monster>();
    }
    
    private void OnEnable()
    {
        MonsterManagerJ.Instance.AddMonster(this);
    }

    private void OnDisable()
    {
        if(!MonsterManagerJ.Instance.IsUnityNull())
            MonsterManagerJ.Instance.RemoveMonster(this);
    }

    public void OnDetectPlayer(PlayerJ player)
    {
        Debug.Log(player);
        monster.target = player;
    }
}
  • 몬스터 블랙보드 생성
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Blackboard_Monster : MonoBehaviour, IBlackboardBase
{
    public float moveSpeed = 3.0f;
    public float attackRange = 6.0f;
   
    [NonSerialized] public Animator animator;
    [NonSerialized] public Rigidbody rigidbody;
    [NonSerialized] public SkillControllerJ SkillController;

    [NonSerialized] public EntityJ target;
        
    public new void InitBlackboard()
    {
        animator = GetComponent<Animator>();
        rigidbody = GetComponent<Rigidbody>();
        SkillController = GetComponent<SkillControllerJ>();
    }   
}
  • 몬스터의 CommonState
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class CommonState_MonsterJ : MonoBehaviour , IState
{
    public StateMachine Fsm { get; set; }
    public Blackboard_Monster Blackboard { get; set; }
    public void InitState(IBlackboardBase blackboard)
    {
        Blackboard = blackboard as Blackboard_Monster;
    }

    public virtual void Enter()
    {
        throw new System.NotImplementedException();
    }

    public virtual void UpdateState(float deltaTime)
    {
        throw new System.NotImplementedException();
    }

    public virtual void Exit()
    {
        throw new System.NotImplementedException();
    }
}
  • 몬스터의 State : Idle, Chase, Skill
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[State("IdleState")]
public class IdleState_MonsterJ : CommonState_MonsterJ
{
    public override void Enter()
    {
        Blackboard.animator.Play("Idles");
        Blackboard.animator.SetFloat("Speed", 0.0f);
    }

    public override void UpdateState(float deltaTime)
    {
        if (Blackboard.target != null)
        {
            Fsm.ChangeState(StateTypesClasses.StateTypes.ChaseState);
        }
    }

    public override void Exit()
    {
        
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[State("ChaseState")]
public class ChaseState_MonsterJ : CommonState_MonsterJ
{
    public override void Enter()
    {
        Blackboard.animator.Play("Idles");
        Blackboard.animator.SetFloat("Speed", 1.0f);
    }

    public override void UpdateState(float deltaTime)
    {
        if (Blackboard.target == null)
        {
            Fsm.ChangeState(StateTypesClasses.StateTypes.IdleState);
            return;
        }
        

        var (skillDistance, skillIndex) = Blackboard.SkillController.GetNearSkillDistanceAndIndex();
        if (0 > skillIndex)
        {
            Fsm.ChangeState(StateTypesClasses.StateTypes.IdleState);
            return;
        }
            
        float attackRnageSqr = skillDistance * skillDistance;
        if (Vector3.SqrMagnitude(Blackboard.target.transform.position - Fsm.transform.position) > attackRnageSqr)
        {
            Vector3 newPos = Vector3.MoveTowards(
                Fsm.transform.position, 
                Blackboard.target.transform.position, 
                Blackboard.moveSpeed * deltaTime
                );
                
            Blackboard.rigidbody.MovePosition(newPos);
            return;
        }

        Fsm.ChangeState(StateTypesClasses.StateTypes.SkillState);
        
        Debug.Log("ChaseState_MonsterJ.Enter"); 
    }

    public override void Exit()
    {
        
    }
}
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEditor.Experimental.GraphView;
using UnityEngine;

[State("SkillState")]
public class SkillState_MonsterJ : CommonState_MonsterJ
{
    public override void Enter()
    {
        Debug.Log("SkillState_MonsterJ.Enter");
        FireSkill();
    }

    public override void UpdateState(float deltaTime)
    {
    }

    public override void Exit()
    {
        Debug.Log("SkillState_MonsterJ.Exit");
    }

    async void FireSkill()
    {
        var (distance, skillIndex) = Blackboard.SkillController.GetNearSkillDistanceAndIndex();

        var skillData = Blackboard.SkillController.FireSkillByIndex(skillIndex);
        Blackboard.animator.Play(skillData.skillAnimation);
            
        await UniTask.Delay((int)(skillData.skillDuration * 1000));
        Debug.Log("OnFireSkill 2");

        Fsm.ChangeState(StateTypesClasses.StateTypes.IdleState);

    }
}