안녕하세요

프로그램 과정에서 막혔던 문제들에 대한 해결책 정리


페이지 목록

레이블이 MVP인 게시물을 표시합니다. 모든 게시물 표시
레이블이 MVP인 게시물을 표시합니다. 모든 게시물 표시

2025년 11월 26일 수요일

[유니티][Unity] MVP를 이용해 주인공 움직임 애니메이션을 사용하기

 Model 

// Assets/Scripts/Model/CharacterStateModel.cs
using System;

public enum CharacterState
{
    Idle,
    Walking,
    Attacking,
    Skilling,
    Dying
}

public class CharacterStateModel
{
    public event Action<CharacterState> OnStateChanged;
    private CharacterState currentState;

    public void ChangeState(CharacterState newState)
    {
        if (currentState != newState)
        {
            currentState = newState;
            OnStateChanged?.Invoke(currentState); // 상태 변경 시 이벤트 발생
        }
    }
}

CharacterState 를 enum으로 저장한다.

OnStateChanged 액션으로 액션 변경을 알리도록 하고

CharacterState currentState로 현재 상태를 저장한다.


View

// Assets/Scripts/View/ICharacterStateView.cs
public interface ICharacterStateView
{
    void PlayIdleAnimation();
    void PlayWalkAnimation();
    void PlayAttackAnimation();
    void PlaySkillAnimation();
    void PlayDieAnimation();
}

CharacterStateView에서 재생해야 하는 Animation을 Interface로 만들어 놓는다.

Idle,Walk,Attack,Skill,Die에 대한 애니메이션이다.


// Assets/Scripts/View/CharacterStateView.cs
using UnityEngine;

public class CharacterStateView : MonoBehaviour, ICharacterStateView
{
    [SerializeField] private Animator animator;
    // 코드와 Animator 파라미터 이름을 일치시킵니다.
    private const string IdleTrigger = "Idle";
    private const string WalkTrigger = "Walk";
    private const string AttackTrigger = "Attack";
    private const string SkillTrigger = "Skill";
    private const string DieTrigger = "Die";

    void Awake()
    {
        if (animator == null)
        {
            animator = GetComponent<Animator>();
        }
    }

    // Presenter의 지시에 따라 Animator의 트리거를 설정합니다.
    public void PlayIdleAnimation() => animator.SetTrigger(IdleTrigger);
    public void PlayWalkAnimation() => animator.SetTrigger(WalkTrigger);
    public void PlayAttackAnimation() => animator.SetTrigger(AttackTrigger);
    public void PlaySkillAnimation() => animator.SetTrigger(SkillTrigger);
    public void PlayDieAnimation() => animator.SetTrigger(DieTrigger);
}

실행시킬 코드이다. animator.SetTrigger를 이용해서 Trigger를 활성화시켜 애니메이션을 재생한다.


Presenter

// Assets/Scripts/Presenter/CharacterPresenter.cs
using UnityEngine;

public class CharacterPresenter : MonoBehaviour
{
    [SerializeField] private CharacterStateView characterStateView;

    private CharacterStateModel model;
    private ICharacterStateView characterStateViewInterface;

    void Start()
    {
        model = new CharacterStateModel();
        characterStateViewInterface = characterStateView;
model.OnStateChanged += OnStateChanged;
// Model 이벤트 구독 model.ChangeState(CharacterState.Idle); // 초기 상태 설정 } private void OnStateChanged(CharacterState newState) { // Model의 상태에 따라 View에게 재생할 애니메이션 지시 switch (newState) { case CharacterState.Idle: viewInterface.PlayIdleAnimation(); break; case CharacterState.Walking: viewInterface.PlayWalkAnimation(); break; case CharacterState.Attacking: viewInterface.PlayAttackAnimation(); break; case CharacterState.Skilling: viewInterface.PlaySkillAnimation(); break; case CharacterState.Dying: viewInterface.PlayDieAnimation(); break; } } // 테스트용 사용자 입력 처리 void Update() { if (Input.GetKeyDown(KeyCode.Alpha1)) model.ChangeState(CharacterState.Idle); if (Input.GetKeyDown(KeyCode.Alpha2)) model.ChangeState(CharacterState.Walking); if (Input.GetKeyDown(KeyCode.Alpha3)) model.ChangeState(CharacterState.Attacking); if (Input.GetKeyDown(KeyCode.Alpha4)) model.ChangeState(CharacterState.Skilling); if (Input.GetKeyDown(KeyCode.Alpha5)) model.ChangeState(CharacterState.Dying); } }

OnStateChanged 때 Statte가 변경되었으면 해당 Animation을 재생하게 된다.

애니메이션을 재생할 애니메이터도 설정해야 한다.

 1. Animator Controller 생성 : 프로젝트 창에서 마우스 우클릭 -> Create -> Animator Controller 선택 이름은 characterAnimController로 지정

2. 캐릭터에 할당: 씬의 캐릭터 모델을 선택하고, Animator 컴포넌트의 Controller 필드에 생성한 characterAnimController를 드래그해서 할당한다.  

View의 SerializeField의 Animator에도 할당한다.

3. 애니메이션 클립 추가: Animator 창을 열어 프로젝트에 있는 Idle,Walk,Attack,Skill, Die 애니메이션 클립들을 드래그하여 상태(State)로 추가한다.

4. 기본 상태 설정: Idle 상태를 마우스 우클릭 -> Set as Layer Default State를 선택

5. 파라미터 추가: Animator 창의 Parameters 탭에서 + 버튼을 눌러, 코드와 동일한 이름의 Trigger 파라미터 5개를 추가함 (대소문자 일치 할 것)

 Idle, Walk, Attack, Skill, Die

6. 트랜지션 설정: Any State에서 각 애니메이션 상태로 트랜지션을 만든다.

 Any State에서 Idle로 드래그하여 트랜지션 생성

해당 트랜지션을 선택하여 인스펙터의 Conditions에 Idle 트리거를 추가하고 Has Exit Time 체크해제 

다른 상태(State)도 동일하게 Any State에서 연결하고 해당 트리거 조건에 맞게 설정한다.

테스트는 숫자 1-5를 눌러서 할 수 있다.

구글 AI의 도움을 받아 작성하였습니다.

[유니티][Unity] MVC와 MVP 소프트웨어 디자인 패턴 및 MVP가 유니티개발에 유리한 이유

 사용자 인터페이스(UI)를 구성할 때 사용되는 아키텍쳐 패턴으로 소프트웨어 디자인패턴이다.
 세가지 핵심 역할로 분리해서 애플리케이션의 로직을 만드는데 이렇게 함으로써 코드의 유지 보수성과 확장성을 높이게 된다.
 
 UI 패턴 또는 애플리케이션 설계 패턴으로 불린다.

 1. MVC ( Model-View-Controller) 모델
  
    Model (모델): 비즈니스 로직과 데이터를 관리함
    View(뷰) : UI(화면)을 담당
    Controller(컨트롤러): Model이나 View를 업데이트하는 사용자의 입력(마우스 클릭,키보드 입력)을 받음

 2. MVP (Model-View-Presenter) 모델
   MVC에서 파생된 패턴으로 Unity와 같은 환경에서 더 선호하는 경향이 있다.
 
  Model(모델) : 비즈니스 로직과 데이터 관리 (1번 모델과 동일)
  View (뷰) : 화면(UI)를 담당하여 사용자에게 보여지는 부분이다, Presenter에 사용자 입력을 전달, 자체 로직은 거의 없음
  Presenter (프리젠터): 중개자 역할을 함 (View와 Model 사이), View에게 받은 입력으로 Model을 업데이트하고, Model의 변경사항을 감지하여 View를 업데이트 하도록 지시함, View와 Model을 강력하게 연결함

 MVP를 선호하는 이유
 유니티의 컴포넌트 기반 아키텍쳐(Component-based Architecture)와의 테스트 용이성 및 호환성이 좋기 때문이다.

 MVP는 MVC에서 파생되었지만 유니티 환경에서 몇 가지 핵심적인 차이가 있고 이 부분에서 MVP가 장점을 가진다.

 1.View와 Presenter 간의 명확한 분리 (느슨한 결합)

    MVC의 Cotroller: Controller와 View가 강하게 결합되는 경향이 있음 Cotroller가 입력을 처리하고 View를 직접 업데이트 할 수 있음

    MVP의 Presenter: Presenter는 중개자 역할을 함 (View와 Model 사이), View는 Presenter의 지시를 따르기만 함. View가 수동적인 역할을 하게 되고 자체 로직이 최소화 됨

    유니티에서의 장점: 유니티의 MonoBehaviour 스크립트는 필연적으로 Transform,Animator, GameObject 등 유니티 엔진 요소에 종속됨. 
    MVP에서는 이런 종속적인 코드를 View에 집중시키고, 제어 흐름과 핵심 게임로직을 담당하는 Presenter는 유니티 의존성을 줄일 수 있게 됨

2. 테스트 용이성 (Testability)
 테스트는 유니티 개발에서 중요한 이슈임
  
 MVP의 Presenter및 Model: 순수 C# 클래스(Plain C# Objects)로 MonoBehaviour를 상속받지 않을 수 있음. 이렇게 되면 유니티 엔진 없이 단위 테스트(Unit Test) 프레임 워크를 사용하여 쉽게 테스트가 가능함
 
 MVC의 Controller/View: 유니티 컴포넌트에 강하게 의존하는 경우, 전체 유니티 씬과 엔진 환경을 구동해야 테스트가 되어 테스트가 복잡하고 느려짐

 유니티에서의 장점: 유니티와 독립적으로 빠르게 테스트 가능 (핵심 게임로직(체력 계산, 사망 판정 등))

 3. 유니티 컴포넌트 시스템과의 조화
 
 MVP 구조: Presenter는 메인 Gameobject(예:보스)에 부착하고, View는 UI Canvas에 부착하는 등 유니티의 컴포넌트 기반 아키텍처에 자연스러운 매핑이 가능함

 MVC 구조: 전통적인 MVC 구조는 UI 프레임워크(예: 웹, 모바일)에 더 적합하게 설계되어서 유니티의 GameObject 계층 구조에 완벽하게 맞지 않을 수가 있음
 

 구글 AI의 도움을 받아 작성되었습니다.