TIL(2024,09,06)적 (Enemy)만들기 및 상태패턴 적용

김보근·2024년 9월 6일

Unity

목록 보기
79/113

TIL (Today I Learned)

오늘 한 일:

Enemy의 상태 패턴 구현:
State Pattern을 활용하여 적이 항상 플레이어를 추적하는 동작을 구현.
IdleState를 생략하고, 게임 시작 시 바로 ChaseState로 전환되도록 설정.
적이 플레이어를 계속해서 추적하도록 상태를 관리하여, 플레이어와 적의 상호작용을 더욱 직관적으로 구현.

주요 내용:

ChaseState: 적이 플레이어를 향해 움직이는 상태로, Execute() 메서드에서 매 프레임마다 플레이어의 위치를 계산하여 적이 플레이어를 따라가는 로직을 구현.
상태 전환: Awake() 메서드에서 바로 ChaseState로 전환하도록 구현하여, 적이 플레이어를 감지하는 범위 없이 항상 추적하는 동작을 구현함.

스크립트

Enemy

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    [SerializeField]
    private float speed; // 적의 속도

    public Rigidbody2D target; // 추적할 플레이어 또는 목표
    private Rigidbody2D rigid; // 적의 Rigidbody2D
    private SpriteRenderer sprite; // 스프라이트 제어

    private StateMachine stateMachine; // 상태 머신
    private Animator animator;
    private bool isLive = true; // 적이 살아 있는지 여부

    private void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        sprite = GetComponent<SpriteRenderer>();
        animator = GetComponent<Animator>();

        // 상태 머신 초기화
        stateMachine = new StateMachine();
        // 게임이 시작되면 바로 플레이어를 추적하는 ChaseState로 전환
        stateMachine.SetState(new ChaseState(stateMachine, animator, speed, rigid));
    }

    private void Update()
    {
        // 상태 머신 업데이트
        if (isLive)
        {
            Vector2 dirVec = target.position - rigid.position;
            stateMachine.Update(dirVec); // 플레이어 방향을 상태에 전달
        }
    }

    private void LateUpdate()
    {
        // 적의 스프라이트 방향 처리
        sprite.flipX = target.position.x > rigid.position.x;
    }

    public void ChasePlayer()
    {
        if (isLive)
        {
            // ChaseState로 전환하여 플레이어 추적 시작
            stateMachine.SetState(new ChaseState(stateMachine, animator, speed, rigid));
        }
        
    }

    public void OnHit()
    {
        // 적이 피격되었을 때 피격 상태로 전환
        //stateMachine.SetState(new HitState(stateMachine, stateMachine.CurrentState, animator));
    }
}

ChaseState

using Unity.VisualScripting;
using UnityEngine;

public class ChaseState : Istate
{
    private StateMachine stateMachine;
    private Animator animator;
    private float speed;
    private Rigidbody2D rigid;

    public ChaseState(StateMachine machine, Animator animator, float speed, Rigidbody2D rigid)
    {
        stateMachine = machine;
        this.animator = animator;
        this.speed = speed;
        this.rigid = rigid;
    }

    public void Enter()
    {
        Debug.Log("Enter Chase State");
        animator.Play("Run"); // 추적 애니메이션
    }

    public void Execute(Vector2 direction)
    {
        // 플레이어를 추적하는 로직
        Vector2 nextVec = direction.normalized * speed * Time.fixedDeltaTime;
        rigid.MovePosition(rigid.position + nextVec);
        rigid.velocity = Vector2.zero;

    }

    public void Exit()
    {
        Debug.Log("Exit Chase State");
    }
}

Reposition

using UnityEngine;

public class Reposition : MonoBehaviour
{
    Collider2D coll;
    private void Awake()
    {
        coll = GetComponent<Collider2D>();
    }
    private void OnTriggerExit2D(Collider2D collision)
    {
        if (!collision.CompareTag("Area"))
            return;

        Vector3 playerPos = GameManager.Instance.player.transform.position;
        Vector3 myPos = transform.position;
        float diffX = Mathf.Abs(playerPos.x - myPos.x);
        float diffY = Mathf.Abs(playerPos.y - myPos.y);

        Vector3 playerDir = GameManager.Instance.player.playerVector;
        float dirX = playerDir.x < 0 ? -1 : 1;
        float dirY = playerDir.y < 0 ? -1 : 1;

        switch (transform.tag)
        {
            case "Ground":
                if (diffX > diffY)
                {
                    transform.Translate(Vector3.right * dirX * 40);
                }
                else if (diffX < diffY)
                {
                    transform.Translate(Vector3.up * dirY * 40);
                }
                break;
            case "Enemy":
                if (coll.enabled)
                {
                    transform.Translate(playerDir * 20 + 
                        new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f), 0f));
                }
                break;
        }

    }

}

트러블슈팅

IdleState 필요 여부: 처음에는 적이 대기하는 IdleState에서 플레이어를 감지하는 로직을 고려했으나, 게임의 특성상 몬스터가 생성이되면 바로바로 플레이어를 쫓아가게 하기위해 필요가 없다고 판단하여 이를 통해 불필요한 상태를 제거하고 상태 패턴의 간결함을 유지.

결과

profile
게임개발자꿈나무

0개의 댓글