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

[챗GPT게임개발14] ChatGPT를 활용하여 Player 착지 시 특수 효과 추가

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

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

오늘은 ChatGPT를 활용하여 Unity 플랫포머 게임에서 플레이어 착지 시 효과를 추가해볼거예요.

 

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

플랫포머 게임에서 플레이어 착지 시 효과 추가 방법

먼저 플레이어가 착지할 때 특수 효과를 어떻게 추가하는 지 알아봐요.

[질문] 안녕 ChatGPT, Unity 플랫포머 게임에서 플레이어 착지 시 특수 효과를 넣고 싶은데 방법을 알려줘

[답변]

Unity에서 플랫포머 게임에 플레이어의 착지 효과를 추가하는 방법은 다음과 같습니다:

1. 효과용 애셋 준비

먼저, 사용할 효과(예: 먼지 튀기기, 빛 번짐 등)의 애셋을 준비하세요. 이것은 파티클 시스템이나 스프라이트 애니메이션이 될 수 있습니다.

2. 파티클 시스템 생성

Unity의 파티클 시스템을 이용하여 착지 효과를 만들 수 있습니다. ParticleSystem 컴포넌트를 사용하여 원하는 효과를 디자인하세요.

3. 스크립트 작성

플레이어가 땅에 착지했을 때 효과를 트리거하도록 스크립트를 작성합니다. 다음은 C# 스크립트의 기본 예시입니다:

using UnityEngine;

public class LandingEffect : MonoBehaviour
{
    public ParticleSystem landingEffect; // Inspector에서 할당

    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Ground") // 땅에 닿았는지 확인
        {
            landingEffect.Play(); // 효과 재생
        }
    }
}

 

4. 스크립트 및 효과 연결

  • 위의 스크립트를 플레이어 오브젝트에 붙이세요.
  • landingEffect에는 Inspector에서 파티클 시스템을 드래그하여 할당합니다.

5. 태그 설정

플레이어가 착지할 수 있는 모든 오브젝트에 "Ground" 태그를 설정하세요. 이렇게 하면 스크립트가 땅에 닿았을 때만 효과를 트리거할 수 있습니다.

6. 테스트 및 조정

게임을 실행하여 착지 효과가 의도한 대로 작동하는지 확인하고 필요에 따라 파티클 시스템의 세부 설정을 조정하세요.

이렇게 하면 Unity에서 플랫포머 게임에 플레이어의 착지 시 효과를 쉽게 추가할 수 있습니다. 게임의 시각적 매력을 높이는 데 큰 도움이 될 것입니다!

 

 

챗GPT가 알려준대로 하나씩 살펴볼께요.

1. 효과용 에셋 준비

 

Unity Learn의 Platformer Microgame을 사용해서 게임을 만들고 있어요.

그래서 Platformer Microgame에서 제공하는 에셋을 사용하면 되요.

 

2. 파티클 시스템 생성

 

BouncyFireworks 파티클을 선택해요. 

 

3. 스크립트 작성
4. 스크립트 및 효과 연결

 

BouncyFireworks 파티클을 선택하면 Inspector 창에서 연결된 스크립트를 볼 수 있어요.

(Platformer Microgame을 사용하고 있어서 챗GPT가 알려준 예제 코드는 사용하지 않을 거예요.)

[파티클 Inspector 에서 연결된 스크립트 확인하기]

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

[RequireComponent(typeof(ParticleSystem))]
public class EmitParticlesOnLand : MonoBehaviour
{

    public bool emitOnLand = true;
    public bool emitOnEnemyDeath = true;

#if UNITY_TEMPLATE_PLATFORMER

    ParticleSystem p;

    void Start()
    {
        p = GetComponent<ParticleSystem>();

        if (emitOnLand) {
            Platformer.Gameplay.PlayerLanded.OnExecute += PlayerLanded_OnExecute;
            void PlayerLanded_OnExecute(Platformer.Gameplay.PlayerLanded obj) {
                p.Play();
            }
        }

        if (emitOnEnemyDeath) {
            Platformer.Gameplay.EnemyDeath.OnExecute += EnemyDeath_OnExecute;
            void EnemyDeath_OnExecute(Platformer.Gameplay.EnemyDeath obj) {
                p.Play();
            }
        }

    }

#endif

}

 

스크립트에 대한 설명도 정리했어요.

[코드 설명]
이 C# 코드는 Unity 엔진을 사용하는 게임 개발을 위한 스크립트로, 특정 게임 이벤트가 발생할 때 파티클 시스템을 활성화하는 기능을 수행합니다. 코드를 세부적으로 분석해보겠습니다:

코드 구조 및 주요 요소

네임스페이스 및 디렉티브:

- System.Collections, System.Collections.Generic: 컬렉션 프레임워크를 사용할 수 있게 해줍니다. 이 스크립트에서는 직접적으로 사용하지 않는 것 같습니다.
- UnityEngine: Unity 엔진의 기본 기능을 사용합니다.
- System.Reflection: 코드에서는 보이지 않지만, 일반적으로 타입 정보를 조사하고 동적으로 데이터를 조작하는 데 사용됩니다.

[RequireComponent(typeof(ParticleSystem))]:
- 이 어트리뷰트는 EmitParticlesOnLand 컴포넌트가 있는 게임 오브젝트에 반드시 ParticleSystem 컴포넌트도 함께 있어야 함을 의미합니다. 없을 경우 자동으로 추가됩니다.

클래스 정의:
EmitParticlesOnLand 클래스는 MonoBehaviour에서 파생되며, Unity 게임 오브젝트에 붙는 컴포넌트의 기능을 수행합니다.

멤버 변수:
emitOnLand, emitOnEnemyDeath: 각각 플레이어가 착지했을 때와 적이 죽었을 때 파티클 시스템을 실행할지를 결정하는 불리언 변수입니다.

프리프로세서 지시문 (#if UNITY_TEMPLATE_PLATFORMER):
이 코드는 UNITY_TEMPLATE_PLATFORMER라는 조건이 정의되었을 때만 컴파일됩니다. 이는 특정 Unity 템플릿 또는 플랫폼을 대상으로 할 때 사용됩니다.

초기화 및 이벤트 연결:
Start 메서드에서 ParticleSystem 컴포넌트를 가져오고, emitOnLand 및 emitOnEnemyDeath 설정에 따라 해당 이벤트 리스너를 추가합니다.

이벤트 핸들러:
PlayerLanded_OnExecute: 플레이어가 착지할 때 실행되며, ParticleSystem의 Play 메서드를 호출하여 파티클을 재생합니다.
EnemyDeath_OnExecute: 적이 죽었을 때 실행되며, 동일하게 파티클을 재생합니다.

주의사항 및 최적화
- 이벤트 핸들러가 구독 취소되지 않고 있습니다. 게임 오브젝트가 파괴되거나 비활성화될 때, 이벤트 구독을 해제하는 것이 좋습니다. 이는 메모리 누수를 방지하고 성능을 최적화하는 데 도움이 됩니다.
- #if 지시문은 코드가 특정 조건에서만 작동하도록 제한하므로, 다른 환경에서는 이 코드가 무시됩니다. 게임을 다양한 플랫폼에 배포할 때 이 점을 고려해야 합니다.

 

여기서 PlayerLanded_OnExecute가 어떻게 실행이 되는 지 궁금했어요.

그래서 연관된 소스를 전부 주고 물어봤어요.

[질문] 안녕 ChatGPT, 아래 소스를 검토해서 EmitParticlesOnLand에서 플레이어가 착지되었는지 어떻게 확인하는지 설명해줘

PlayerController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Platformer.Gameplay;
using static Platformer.Core.Simulation;
using Platformer.Model;
using Platformer.Core;

namespace Platformer.Mechanics
{
    /// <summary>
    /// This is the main class used to implement control of the player.
    /// It is a superset of the AnimationController class, but is inlined to allow for any kind of customisation.
    /// </summary>
    public class PlayerController : KinematicObject
    {
        public AudioClip jumpAudio;
        public AudioClip respawnAudio;
        public AudioClip ouchAudio;

        /// <summary>
        /// Max horizontal speed of the player.
        /// </summary>
        public float maxSpeed = 7;
        /// <summary>
        /// Initial jump velocity at the start of a jump.
        /// </summary>
        public float jumpTakeOffSpeed = 7;

        public JumpState jumpState = JumpState.Grounded;
        private bool stopJump;
        /*internal new*/ public Collider2D collider2d;
        /*internal new*/ public AudioSource audioSource;
        public Health health;
        public bool controlEnabled = true;

        bool jump;
        Vector2 move;
        SpriteRenderer spriteRenderer;
        internal Animator animator;
        readonly PlatformerModel model = Simulation.GetModel<PlatformerModel>();

        public Bounds Bounds => collider2d.bounds;

        void Awake()
        {
            health = GetComponent<Health>();
            audioSource = GetComponent<AudioSource>();
            collider2d = GetComponent<Collider2D>();
            spriteRenderer = GetComponent<SpriteRenderer>();
            animator = GetComponent<Animator>();
        }

        protected override void Update()
        {
            if (controlEnabled)
            {
                move.x = Input.GetAxis("Horizontal");
                if (jumpState == JumpState.Grounded && Input.GetButtonDown("Jump"))
                    jumpState = JumpState.PrepareToJump;
                else if (Input.GetButtonUp("Jump"))
                {
                    stopJump = true;
                    Schedule<PlayerStopJump>().player = this;
                }
            }
            else
            {
                move.x = 0;
            }
            UpdateJumpState();
            base.Update();
        }

        void UpdateJumpState()
        {
            jump = false;
            switch (jumpState)
            {
                case JumpState.PrepareToJump:
                    jumpState = JumpState.Jumping;
                    jump = true;
                    stopJump = false;
                    break;
                case JumpState.Jumping:
                    if (!IsGrounded)
                    {
                        Schedule<PlayerJumped>().player = this;
                        jumpState = JumpState.InFlight;
                    }
                    break;
                case JumpState.InFlight:
                    if (IsGrounded)
                    {
                        Schedule<PlayerLanded>().player = this;
                        jumpState = JumpState.Landed;
                    }
                    break;
                case JumpState.Landed:
                    jumpState = JumpState.Grounded;
                    break;
            }
        }

        protected override void ComputeVelocity()
        {
            if (jump && IsGrounded)
            {
                velocity.y = jumpTakeOffSpeed * model.jumpModifier;
                jump = false;
            }
            else if (stopJump)
            {
                stopJump = false;
                if (velocity.y > 0)
                {
                    velocity.y = velocity.y * model.jumpDeceleration;
                }
            }

            if (move.x > 0.01f)
                spriteRenderer.flipX = false;
            else if (move.x < -0.01f)
                spriteRenderer.flipX = true;

            animator.SetBool("grounded", IsGrounded);
            animator.SetFloat("velocityX", Mathf.Abs(velocity.x) / maxSpeed);

            targetVelocity = move * maxSpeed;
        }

        public enum JumpState
        {
            Grounded,
            PrepareToJump,
            Jumping,
            InFlight,
            Landed
        }
    }
}

PlayerLanded.cs
using Platformer.Core;
using Platformer.Mechanics;

namespace Platformer.Gameplay
{
    /// <summary>
    /// Fired when the player character lands after being airborne.
    /// </summary>
    /// <typeparam name="PlayerLanded"></typeparam>
    public class PlayerLanded : Simulation.Event<PlayerLanded>
    {
        public PlayerController player;

        public override void Execute()
        {

        }
    }
}

EmitParticlesOnLand.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;

[RequireComponent(typeof(ParticleSystem))]
public class EmitParticlesOnLand : MonoBehaviour
{

    public bool emitOnLand = true;
    public bool emitOnEnemyDeath = true;

#if UNITY_TEMPLATE_PLATFORMER

    ParticleSystem p;

    void Start()
    {
        p = GetComponent<ParticleSystem>();

        if (emitOnLand) {
            Platformer.Gameplay.PlayerLanded.OnExecute += PlayerLanded_OnExecute;
            void PlayerLanded_OnExecute(Platformer.Gameplay.PlayerLanded obj) {
                p.Play();
            }
        }

        if (emitOnEnemyDeath) {
            Platformer.Gameplay.EnemyDeath.OnExecute += EnemyDeath_OnExecute;
            void EnemyDeath_OnExecute(Platformer.Gameplay.EnemyDeath obj) {
                p.Play();
            }
        }

    }

#endif

}
[답변]
이 코드들을 통해 EmitParticlesOnLand 클래스에서 플레이어의 착지 여부를 감지하는 방법은 다음과 같습니다:

Unity의 이벤트 시스템 활용
EmitParticlesOnLand 클래스는 Unity의 이벤트 시스템을 활용하여 플레이어의 착지를 감지합니다. 이 클래스는  Platformer.Gameplay.PlayerLanded 이벤트가 발생할 때마다 특정 액션을 취합니다. 이 이벤트는
PlayerController 클래스에 의해 발동되는데, 그 구조와 동작을 이해하려면
PlayerController의 UpdateJumpState 메서드와 PlayerLanded 이벤트의 관계를 살펴보아야 합니다.

PlayerController 클래스 내 JumpState
PlayerController는 JumpState 열거형을 사용하여 플레이어의 점프 상태를 관리합니다. 플레이어의 점프 상태는 다음과 같이 변경됩니다:

PrepareToJump: 플레이어가 점프를 준비할 때.
Jumping: 실제로 점프가 시작될 때.
InFlight: 플레이어가 공중에 있는 상태.
Landed: 플레이어가 땅에 착지했을 때.

착지 감지

플레이어의 상태가 InFlight에서 Landed로 변경되는 순간, PlayerController는 PlayerLanded 이벤트를 발생시킵니다. 이 이벤트는 Platformer.Gameplay.PlayerLanded 유형의 이벤트 객체를 사용하여 Simulation 시스템을 통해 전파됩니다.
if (IsGrounded)
{
    Schedule<PlayerLanded>().player = this;
    jumpState = JumpState.Landed;
}​

위 코드는 플레이어가 땅에 닿았을 때 PlayerLanded 이벤트를 스케줄링합니다.

EmitParticlesOnLand에서의 이벤트 처리
EmitParticlesOnLand에서는 PlayerLanded 이벤트가 발생할 때마다 파티클 시스템을 활성화하는 이벤트 핸들러를 등록합니다:
void PlayerLanded_OnExecute(Platformer.Gameplay.PlayerLanded obj)
{
    p.Play();
}​

이 메서드는 플레이어의 착지를 감지하고, 설정된 파티클 시스템을 재생하여 시각적 효과를 제공합니다.

결론

따라서, EmitParticlesOnLand 클래스는 PlayerController에서 발생하는 PlayerLanded 이벤트를 통해 플레이어의 착지를 감지하고 이에 반응하여 파티클 효과를 생성합니다. 이 모든 과정은 Unity의 이벤트와 컴포넌트 시스템을 활용하여 효율적으로 구현됩니다.

 

위 결론과 같이 코드들이 실행되네요. 

ChatGPT가 있으니 몇 분만에 코드 흐름도 쉽게 이해되네요.

6. 테스트 및 조정
*5. 태그 설정은 하실 필요 없어요. 

 

아래 이미지와 같이 Player 의 자식 오브젝트로 파티클을 추가해요.

*BouncyFireworks 파티클 대신 BouncyGlowy를 사용했어요.

 

파티클 시스템을 플레이어의 자식 오브젝트로 설정하면, 파티클 시스템의 위치가 항상 플레이어를 따라갑니다. 이는 플레이어가 움직일 때 파티클 효과가 자연스럽게 플레이어와 함께 움직이게 하고, 별도로 위치를 업데이트할 필요가 없다는 것을 의미합니다. 예를 들어, 착지 효과를 만들 때 파티클 시스템이 플레이어의 위치에 정확히 맞춰져야 하면, 자식 오브젝트로 설정하는 것이 이상적입니다.
7. 씬에서 플레이 버튼을 눌러 플레이어 착지 시 효과가 잘 나오는 지 확인해요.

 

플레이어가 착지 또는 적을 제거했을 때 원하는 특수 효과가 잘 나와요.

 

오늘은 ChatGPT를 활용하여 Unity 플랫포머 게임에서 플레이어 착지 시 효과를 추가했어요.

파티클이 게임을 좀 더 생동감 있게 해요.

 

[인텔리노마드라이프와 함께 행복하세요 from IntelliOneStudio ]

 

지금까지,

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

감사합니다.

728x90