본문 바로가기
디지털노마드

[챗GPT게임개발44] GPT-4, GPT-4o 활용해서 Unity에서 애니메이션과 게임 로직 통합하기

by 인텔리노마드라이프 2024. 6. 3.
728x90

안녕하세요. 인텔리원스튜디오(IntelliOneStudio)입니다.

오늘은 GPT-4, GPT-4o 활용해서 Unity에서 애니메이션과 게임 로직을 통합해볼께요. 

[인텔리원스튜디오]

[오늘의 목표] GPT-4, GPT-4o 활용해서 Unity에서 애니메이션과 게임 로직 통합하기

 

[참고]

2024.06.03 - [디지털노마드] - [챗GPT게임개발43] GPT-4, GPT-4o 활용해서 Unity에서 애니메이션과 게임 로직 통합을 위한 개념 이해

 

[챗GPT게임개발43] GPT-4, GPT-4o 활용해서 Unity에서 애니메이션과 게임 로직 통합을 위한 개념 이해

안녕하세요. 인텔리원스튜디오(IntelliOneStudio)입니다.오늘은 GPT-4, GPT-4o 활용해서 Unity에서 애니메이션과 게임 로직 통합하는 개념을 알아볼께요.[오늘의 목표] GPT-4, GPT-4o 활용해서 Unity에서 애니

intellinomadlife.tistory.com

 

 ChatGPT 답변 내용 중 제가 별도로 표시한 파란색 내용을 기준으로 읽으시면 좀 더 이해가 쉽게 될거예요.

이 글은 Unity 에서 제공한 2D Game Kit을 활용했어요.

 

상태와 동작을 연결하는 스크립트 살펴보기

먼저 애니메이션 상태와 그 상태에 맞게 동작을 연결하는 스크립트를 살펴볼께요.

using UnityEngine;
using UnityEngine.Animations;

namespace Gamekit2D
{
    public class SceneLinkedSMB<TMonoBehaviour> : SealedSMB 
        where TMonoBehaviour : MonoBehaviour
    {
        protected TMonoBehaviour m_MonoBehaviour;
    
        bool m_FirstFrameHappened;
        bool m_LastFrameHappened;

        public static void Initialise (Animator animator, TMonoBehaviour monoBehaviour)
        {
            SceneLinkedSMB<TMonoBehaviour>[] sceneLinkedSMBs = animator.GetBehaviours<SceneLinkedSMB<TMonoBehaviour>>();

            for (int i = 0; i < sceneLinkedSMBs.Length; i++)
            {
                sceneLinkedSMBs[i].InternalInitialise(animator, monoBehaviour);
            }
        }

        protected void InternalInitialise (Animator animator, TMonoBehaviour monoBehaviour)
        {
            m_MonoBehaviour = monoBehaviour;
            OnStart (animator);
        }

        public sealed override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
        {
            Debug.Log("OnStateEnter called");

            m_FirstFrameHappened = false;

            OnSLStateEnter(animator, stateInfo, layerIndex);
            OnSLStateEnter (animator, stateInfo, layerIndex, controller);
        }

        public sealed override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
        {
            if(!animator.gameObject.activeSelf)
                return;
        
            if (animator.IsInTransition(layerIndex) && animator.GetNextAnimatorStateInfo(layerIndex).fullPathHash == stateInfo.fullPathHash)
            {
                OnSLTransitionToStateUpdate(animator, stateInfo, layerIndex);
                OnSLTransitionToStateUpdate(animator, stateInfo, layerIndex, controller);
            }

            if (!animator.IsInTransition(layerIndex) && m_FirstFrameHappened)
            {
                OnSLStateNoTransitionUpdate(animator, stateInfo, layerIndex);
                OnSLStateNoTransitionUpdate(animator, stateInfo, layerIndex, controller);
            }
        
            if (animator.IsInTransition(layerIndex) && !m_LastFrameHappened && m_FirstFrameHappened)
            {
                m_LastFrameHappened = true;
            
                OnSLStatePreExit(animator, stateInfo, layerIndex);
                OnSLStatePreExit(animator, stateInfo, layerIndex, controller);
            }

            if (!animator.IsInTransition(layerIndex) && !m_FirstFrameHappened)
            {
                m_FirstFrameHappened = true;

                OnSLStatePostEnter(animator, stateInfo, layerIndex);
                OnSLStatePostEnter(animator, stateInfo, layerIndex, controller);
            }

            if (animator.IsInTransition(layerIndex) && animator.GetCurrentAnimatorStateInfo(layerIndex).fullPathHash == stateInfo.fullPathHash)
            {
                OnSLTransitionFromStateUpdate(animator, stateInfo, layerIndex);
                OnSLTransitionFromStateUpdate(animator, stateInfo, layerIndex, controller);
            }
        }

        public sealed override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
        {
            m_LastFrameHappened = false;

            OnSLStateExit(animator, stateInfo, layerIndex);
            OnSLStateExit(animator, stateInfo, layerIndex, controller);
        }

        /// <summary>
        /// Called by a MonoBehaviour in the scene during its Start function.
        /// </summary>
        public virtual void OnStart(Animator animator) { }

        /// <summary>
        /// Called before Updates when execution of the state first starts (on transition to the state).
        /// </summary>
        public virtual void OnSLStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
    
        /// <summary>
        /// Called after OnSLStateEnter every frame during transition to the state.
        /// </summary>
        public virtual void OnSLTransitionToStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }

        /// <summary>
        /// Called on the first frame after the transition to the state has finished.
        /// </summary>
        public virtual void OnSLStatePostEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }

        /// <summary>
        /// Called every frame after PostEnter when the state is not being transitioned to or from.
        /// </summary>
        public virtual void OnSLStateNoTransitionUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }

        /// <summary>
        /// Called on the first frame after the transition from the state has started.  Note that if the transition has a duration of less than a frame, this will not be called.
        /// </summary>
        public virtual void OnSLStatePreExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }

        /// <summary>
        /// Called after OnSLStatePreExit every frame during transition to the state.
        /// </summary>
        public virtual void OnSLTransitionFromStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }

        /// <summary>
        /// Called after Updates when execution of the state first finshes (after transition from the state).
        /// </summary>
        public virtual void OnSLStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }

        /// <summary>
        /// Called before Updates when execution of the state first starts (on transition to the state).
        /// </summary>
        public virtual void OnSLStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }

        /// <summary>
        /// Called after OnSLStateEnter every frame during transition to the state.
        /// </summary>
        public virtual void OnSLTransitionToStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }

        /// <summary>
        /// Called on the first frame after the transition to the state has finished.
        /// </summary>
        public virtual void OnSLStatePostEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }

        /// <summary>
        /// Called every frame when the state is not being transitioned to or from.
        /// </summary>
        public virtual void OnSLStateNoTransitionUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }

        /// <summary>
        /// Called on the first frame after the transition from the state has started.  Note that if the transition has a duration of less than a frame, this will not be called.
        /// </summary>
        public virtual void OnSLStatePreExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }

        /// <summary>
        /// Called after OnSLStatePreExit every frame during transition to the state.
        /// </summary>
        public virtual void OnSLTransitionFromStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }

        /// <summary>
        /// Called after Updates when execution of the state first finshes (after transition from the state).
        /// </summary>
        public virtual void OnSLStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }
    }

    //This class repalce normal StateMachineBehaviour. It add the possibility of having direct reference to the object
    //the state is running on, avoiding the cost of retrienving it through a GetComponent every time.
    //c.f. Documentation for more in depth explainations.
    public abstract class SealedSMB : StateMachineBehaviour
    {
        public sealed override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }

        public sealed override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }

        public sealed override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
    }
}

 

[쉽게 설명하기 : GPT-4, GPT-4o]


이 코드는 Unity의 애니메이션 시스템에서 상태를 관리하고 특정 게임 오브젝트의 동작과 애니메이션 상태를 연결하는 패턴을 구현한 것입니다. 쉽게 말해, 이 패턴을 통해 특정 MonoBehaviour(게임 오브젝트의 동작을 정의하는 클래스)와 애니메이터 상태를 효율적으로 결합하여 다양한 동작을 구현할 수 있습니다.

쉽게 설명하면

  1. SceneLinkedSMB<TMonoBehaviour>는 특정 상태에서 실행할 코드를 MonoBehaviour와 연결하는데 사용됩니다.
  2. Initialise 메서드는 애니메이터와 MonoBehaviour를 연결합니다.
  3. OnStateEnter, OnStateUpdate, OnStateExit 메서드는 상태 전환 시 호출되어 특정 동작을 수행합니다.
  4. 하위 클래스는 이 메서드들을 재정의하여 상태별로 다른 동작을 정의할 수 있습니다.

이 패턴을 통해 상태별로 구체적인 동작을 정의하고, 이 동작을 애니메이터와 게임 오브젝트의 동작에 쉽게 연결할 수 있습니다.

 

이 스크립트는 플레이어의 상태별로 구체적인 동작을 정의할 수 있다는 내용만 기억하면 되요.

이제 어떻게 사용되는 지 볼께요.

게임 시작 시 플레이어 동작 정의하기

위 스크립트를 활용해서 아래 기능이 어떻게 동작하는 지 살펴볼께요.

게임 시작 시 PlayerCharacter의 이동 벡터를(0, 0)으로 설정하여 플레이어를 정지 시키기

 

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

namespace Gamekit2D
{
    public class RespawnSMB : SceneLinkedSMB<PlayerCharacter>
    {
        public override void OnSLStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            base.OnSLStateEnter(animator, stateInfo, layerIndex);

            m_MonoBehaviour.SetMoveVector(Vector2.zero);
        }
    }
}

 

  • RespawnSMB 클래스는 SceneLinkedSMB<PlayerCharacter>를 상속받아요.
  • OnSLStateEnter 메서드는 리스폰 상태에 들어갈 때 호출되며, PlayerCharacter의 이동 벡터를 (0, 0)으로 설정하여 플레이어를 정지시켜요.

위 스크립트를 저장하고 아래 영상과 같이 설정 후 실행하면 되요.

 

영상에서와 같이 게임 시작 시 Spawn 상태가 실행되고 그 상태에서 원하는 동작이 되는 것을 볼 수 있어요. 

조금 어려울 수 있지만 조금 천천히 살펴보면 이해가 될거예요.

 

오늘은 GPT-4, GPT-4o 활용해서 Unity에서 애니메이션과 게임 로직을 통합해봤어요.

 

지금까지,

언제나 성장하는 인텔리원스튜디오(IntelliOneStudio)입니다.

감사합니다.

 

 

 

728x90