안녕하세요. 인텔리원스튜디오(IntelliOneStudio)입니다.
오늘은 ChatGPT를 활용하여 Unity 플랫포머 게임에서 벽 타기 로직을 추가해볼께요.
- 오늘의 목표 : 플레이어 벽 타기 로직 구현
- 필요한 기능
1. 플레이어가 벽에 닿았는 지를 감지 및 슬라이딩 기능 추가
2. 점프 상태 추가
※ ChatGPT 답변 내용 중 제가 별도로 표시한 파란색 내용을 기준으로 읽으시면 좀 더 이해가 쉽게 될거예요.
벽 타기 로직을 추가할 스크립트를 먼저 학습 시켜요.
좀 더 코딩을 정확히 하기 위해 수정할 코드를 챗GPT에 학습을 시켰어요.
[질문] 안녕 ChatGPT, 아래 코드를 정확히 학습해줘
* 코드는 기존 PlayerController 스크립트
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 } } }
[답변]
이 코드는 Unity의 2D 플랫폼 게임에서 플레이어 움직임과 점프 로직을 구현하는 PlayerController 클래스입니다. 다음은 이 코드의 각 부분을 분석한 내용입니다.주요 기능 요약
- 플레이어의 오디오 및 애니메이션 관리:
- jumpAudio, respawnAudio, ouchAudio: 점프, 부활, 데미지 소리 재생에 사용되는 오디오 클립.
- audioSource: 오디오 클립을 재생하는 데 사용되는 오디오 소스 컴포넌트.
- animator: 플레이어 애니메이션을 제어하는 애니메이터.
- 점프 및 움직임 속성:
- maxSpeed: 플레이어의 최대 수평 속도.
- jumpTakeOffSpeed: 점프 시작 시 플레이어가 위로 올라가는 속도.
- 플레이어의 점프 상태:
- JumpState: 플레이어의 점프 상태를 나타내는 열거형으로, 상태 값은 Grounded, PrepareToJump, Jumping, InFlight, Landed가 있습니다.
- 움직임 제어 로직:
- move: 플레이어의 수평 움직임을 나타내는 2D 벡터.
- jump, stopJump: 점프와 점프 중지에 대한 상태 변수.
- 플레이어의 콜라이더 및 물리 속성:
- collider2d: 플레이어의 충돌을 감지하기 위한 Collider2D 컴포넌트.
- Bounds: 플레이어의 콜라이더 경계를 나타내는 프로퍼티.
- 플레이어 제어 로직:
- Update(): 매 프레임마다 호출되어 플레이어의 수평 이동, 점프 입력 및 상태를 업데이트합니다.
- UpdateJumpState(): 현재 점프 상태를 기반으로 점프, 비행, 착륙 상태를 업데이트합니다.
- ComputeVelocity(): 플레이어의 현재 속도와 애니메이션 상태를 계산합니다.
코드 흐름
- Awake(): 초기화 메서드로, 관련 컴포넌트를 가져오고 기본 속성을 설정합니다.
- Update():
- 수평 움직임을 Input.GetAxis("Horizontal")로 가져와 move에 설정합니다.
- 점프 입력(Jump 키)을 감지해 점프 상태로 전환합니다.
- UpdateJumpState() 메서드를 호출해 점프 상태를 갱신합니다.
- UpdateJumpState():
- PrepareToJump 상태에서 Jumping 상태로 전환하고, 점프를 시작합니다.
- Jumping 상태에서 지면에 닿지 않으면 InFlight 상태로 전환합니다.
- InFlight 상태에서 지면에 닿으면 Landed 상태로 전환하고, 다시 Grounded 상태로 돌아옵니다.
- ComputeVelocity():
- jump가 활성화되면 플레이어의 수직 속도를 설정해 점프를 수행합니다.
- stopJump가 활성화되면 수직 속도를 줄입니다.
- 플레이어의 수평 속도를 targetVelocity로 설정합니다.
이 코드를 바탕으로 추가 기능을 구현하거나 수정하려면 주로 Update()와 ComputeVelocity()에서 플레이어의 움직임과 점프 상태를 변경하면 됩니다.
학습이 완료되면 이제 기능 추가를 요청해요.
수정대상인 스크립트를 학습시키고 난 다음 원하는 기능을 추가해달라고 하면 되요.
[질문] 안녕 ChatGPT, 학습된 코드를 가지고 플레이어가 벽을 타는 기능을 추가해줘
[답변]
플레이어가 벽을 타는 기능을 추가하려면 벽 감지 로직을 작성하고, 점프 상태에 벽 타기 상태를 추가해야 합니다. 다음은 벽 타기 기능을 포함하도록 수정된 코드입니다.수정된 코드
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 { public class PlayerController : KinematicObject { public AudioClip jumpAudio; public AudioClip respawnAudio; public AudioClip ouchAudio; public float maxSpeed = 7; public float jumpTakeOffSpeed = 7; // 벽 타기 관련 변수 추가 public LayerMask wallLayer; public float wallSlideSpeed = 1.5f; public float wallJumpXSpeed = 5f; public float wallJumpYSpeed = 7f; private bool isTouchingWall; private bool wallSliding; private bool wallJumping; public JumpState jumpState = JumpState.Grounded; private bool stopJump; public Collider2D collider2d; 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; } UpdateWallSlide(); UpdateJumpState(); base.Update(); } // 벽 슬라이딩 상태를 업데이트하는 로직 void UpdateWallSlide() { // 벽 감지 (OverlapCircle 또는 BoxCast 사용) isTouchingWall = Physics2D.OverlapCircle(collider2d.bounds.center, collider2d.bounds.extents.x + 0.1f, wallLayer); // 벽 슬라이딩 상태 확인 if (isTouchingWall && !IsGrounded && move.x != 0) { wallSliding = true; velocity.y = Mathf.Clamp(velocity.y, -wallSlideSpeed, float.MaxValue); } else { wallSliding = false; } } // 점프 상태 업데이트 void UpdateJumpState() { jump = false; switch (jumpState) { case JumpState.PrepareToJump: if (wallSliding) { jumpState = JumpState.WallJumping; wallJumping = true; jump = true; stopJump = false; } else { 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.WallJumping: if (!wallJumping) { jumpState = JumpState.InFlight; } break; case JumpState.Landed: jumpState = JumpState.Grounded; break; } } protected override void ComputeVelocity() { if (jump) { if (wallJumping) { // 벽 점프 로직 velocity.x = wallJumpXSpeed * (spriteRenderer.flipX ? 1 : -1); velocity.y = wallJumpYSpeed; wallJumping = false; } else if (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, WallJumping, Landed } } }
추가된 벽 타기 로직
- UpdateWallSlide: 플레이어가 벽과 접촉하고 있는지 확인하고, 벽에 접촉할 때 천천히 미끄러지게 합니다.
- JumpState.WallJumping: 벽 점프 상태를 추가해 플레이어가 벽에서 점프할 수 있도록 합니다.
- ComputeVelocity: 벽에서 점프할 때 플레이어의 수평 및 수직 속도를 설정합니다.
사용 방법
- 벽 레이어 설정: Inspector에서 wallLayer 필드에 벽 레이어를 지정하세요.
- 감지 범위 조절: OverlapCircle의 범위가 벽을 감지하는 데 충분한지 확인하세요.
벽 레이어 설정하기
수정된 스크립트를 적용하기 위해 벽 레이어를 만들었어요.
Player 에 wallLayer 적용하기
이제 만들어진 wallLayer 를 Player 오브젝트에 추가했어요.
코드를 저장하고 실행을 했어요.
그런데 플레이어가 슬라이딩 될때 점프가 되지 않았어요.
슬라이딩 될 때 점프 로직을 수정해줘
그래서 플레이어가 벽에 슬라이딩 될 때 점프 로직을 수정하라고 했어요.
[질문] 안녕 ChatGPT, 플레이어가 벽에 슬라이딩될때 점프를 할 수 있게 코드를 수정해줘.
[답변]
벽에 슬라이딩될 때 점프할 수 있도록 기능을 구현하려면 UpdateWallSlide에서 벽에 접촉했을 때 jump 키 입력을 받아 벽 점프 상태로 전환해야 합니다. 이를 위해서는 UpdateJumpState에서 벽 점프 상태를 확인하고 ComputeVelocity에서 벽 점프 로직을 구현해야 합니다.
아래는 수정된 코드입니다:
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 { public class PlayerController : KinematicObject { public AudioClip jumpAudio; public AudioClip respawnAudio; public AudioClip ouchAudio; public float maxSpeed = 7; public float jumpTakeOffSpeed = 7; // 벽 타기 관련 변수 추가 public LayerMask wallLayer; public float wallSlideSpeed = 1.5f; public float wallJumpXSpeed = 5f; public float wallJumpYSpeed = 7f; private bool isTouchingWall; private bool wallSliding; private bool wallJumping; public JumpState jumpState = JumpState.Grounded; private bool stopJump; public Collider2D collider2d; 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 || wallSliding) && Input.GetButtonDown("Jump")) { jumpState = JumpState.PrepareToJump; } else if (Input.GetButtonUp("Jump")) { stopJump = true; Schedule<PlayerStopJump>().player = this; } } else { move.x = 0; } UpdateWallSlide(); UpdateJumpState(); base.Update(); } void UpdateWallSlide() { // 벽 감지 (OverlapCircle 또는 BoxCast 사용) isTouchingWall = Physics2D.OverlapCircle(collider2d.bounds.center, collider2d.bounds.extents.x + 0.1f, wallLayer); // 벽 슬라이딩 상태 확인 if (isTouchingWall && !IsGrounded && move.x != 0) { wallSliding = true; velocity.y = Mathf.Clamp(velocity.y, -wallSlideSpeed, float.MaxValue); } else { wallSliding = false; } } void UpdateJumpState() { jump = false; switch (jumpState) { case JumpState.PrepareToJump: if (wallSliding) { jumpState = JumpState.WallJumping; wallJumping = true; jump = true; stopJump = false; } else { 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.WallJumping: if (!wallJumping) { jumpState = JumpState.InFlight; } break; case JumpState.Landed: jumpState = JumpState.Grounded; break; } } protected override void ComputeVelocity() { if (jump) { if (wallJumping) { // 벽 점프 로직 velocity.x = wallJumpXSpeed * (spriteRenderer.flipX ? 1 : -1); velocity.y = wallJumpYSpeed; wallJumping = false; } else if (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, WallJumping, Landed } } }
코드 설명
- Update(): 벽 슬라이딩 상태에서 점프 버튼이 눌리면 JumpState.PrepareToJump 상태로 전환하도록 코드를 변경했습니다.
- UpdateWallSlide(): 플레이어가 벽과 접촉하는지 확인하는 로직을 사용해 벽 슬라이딩 상태를 확인하고, 이를 기반으로 속도를 조절합니다.
- ComputeVelocity(): 벽 점프 로직을 추가해 벽에서 점프할 수 있도록 합니다.
이렇게 하면 플레이어가 벽에 슬라이딩되면 점프를 할 수 있게 됩니다.
스크립트를 게임을 실행해서 플레이어가 잘 동작하는 지 확인했어요.
잘 동작하네요. 그런데 한 가지 궁금한 점이 생겼어요.
기존 소스와 변경된 소스를 비교해줄래?
궁금했어요. 두 개의 소스를 어떻게 비교할지.
[질문] 안녕 ChatGPT, 벽타는 로직 부분 즉 추가된 코드를 기존 코드와 비교를 해줄래?
[답변]
네, 벽 타기 로직 부분을 기존 코드와 비교해 드리겠습니다.
기존 코드
기존 코드는 플레이어의 기본 점프와 움직임 로직을 다루는 코드입니다. 플레이어는 지면에서 점프하거나 이동할 수 있고, 점프 상태를 관리합니다.
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; }
수정된 코드 (벽 타기 로직 포함)
수정된 코드에서는 벽에 슬라이딩하는 상태를 감지하고, 슬라이딩 상태에서 점프할 수 있도록 구현되었습니다. 이 추가된 기능을 위해 벽 감지, 벽 슬라이딩, 벽 점프 상태 등을 처리합니다.
void UpdateWallSlide() { // 벽 감지 (OverlapCircle 사용) isTouchingWall = Physics2D.OverlapCircle(collider2d.bounds.center, collider2d.bounds.extents.x + 0.1f, wallLayer); // 벽 슬라이딩 상태 확인 if (isTouchingWall && !IsGrounded && move.x != 0) { wallSliding = true; velocity.y = Mathf.Clamp(velocity.y, -wallSlideSpeed, float.MaxValue); Debug.Log("Wall Sliding"); } else { wallSliding = false; } } void UpdateJumpState() { jump = false; switch (jumpState) { case JumpState.PrepareToJump: if (wallSliding) { jumpState = JumpState.WallJumping; wallJumping = true; jump = true; stopJump = false; Debug.Log("Jump State: WallJumping"); } else { jumpState = JumpState.Jumping; jump = true; stopJump = false; Debug.Log("Jump State: Jumping"); } break; case JumpState.Jumping: if (!IsGrounded) { Schedule<PlayerJumped>().player = this; jumpState = JumpState.InFlight; Debug.Log("Jump State: InFlight"); } break; case JumpState.InFlight: if (IsGrounded) { Schedule<PlayerLanded>().player = this; jumpState = JumpState.Landed; Debug.Log("Jump State: Landed"); } break; case JumpState.WallJumping: if (!wallJumping) { jumpState = JumpState.InFlight; Debug.Log("Jump State: InFlight (from WallJumping)"); } break; case JumpState.Landed: jumpState = JumpState.Grounded; Debug.Log("Jump State: Grounded"); break; } } protected override void ComputeVelocity() { if (jump) { if (wallJumping) { // 벽 점프 로직 velocity.x = wallJumpXSpeed * (spriteRenderer.flipX ? 1 : -1); velocity.y = wallJumpYSpeed; wallJumping = false; Debug.Log("Wall Jump Performed"); } else if (IsGrounded) { velocity.y = jumpTakeOffSpeed * model.jumpModifier; Debug.Log("Regular Jump Performed"); } jump = false; } else if (stopJump) { stopJump = false; if (velocity.y > 0) { velocity.y = velocity.y * model.jumpDeceleration; Debug.Log("Jump Stopped"); } } 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; }
비교 분석
- 벽 감지 및 슬라이딩:
- UpdateWallSlide 메서드를 통해 벽에 접촉 중인지 확인하고, 접촉 중이면 속도를 제한하여 슬라이딩 상태로 전환합니다.
- 점프 상태 추가:
- JumpState.WallJumping을 추가하여 벽에서 점프할 수 있는 상태를 구현했습니다.
- 점프 상태 업데이트:
- UpdateJumpState에서 벽 슬라이딩 상태라면 WallJumping 상태로 전환합니다.
- 속도 조정:
- ComputeVelocity에서 벽 점프 시 플레이어의 수평 및 수직 속도를 설정해 벽에서 점프하는 기능을 추가했습니다.
이러한 로직을 통해 벽에 슬라이딩되는 동안 벽 점프를 수행할 수 있도록 수정되었습니다.
오늘은 ChatGPT를 활용하여 Unity 플랫포머 게임에서 벽 타기 로직을 추가해봤어요.
지금까지,
언제나 성장하는 인텔리원스튜디오(IntelliOneStudio)입니다.
감사합니다.
'디지털노마드' 카테고리의 다른 글
[챗GPT게임개발19] ChatGPT를 활용하여 가속 패드와 Jiggler 추가하기 (65) | 2024.05.09 |
---|---|
[챗GPT게임개발18] ChatGPT를 활용하여 트리거와 이벤트 시스템을 활용 (60) | 2024.05.09 |
[챗GPT게임개발16] ChatGPT를 활용하여 점프 패드(Jump Pad) 추가(2) (56) | 2024.05.08 |
[챗GPT게임개발15] ChatGPT를 활용하여 점프 패드(Jump Pad) 추가(1) (64) | 2024.05.07 |
[챗GPT게임개발14] ChatGPT를 활용하여 Player 착지 시 특수 효과 추가 (77) | 2024.05.06 |