안녕하세요. 인텔리원스튜디오(IntelliOneStudio)입니다.
오늘은 ChatGPT를 활용하여 Unity 플랫포머 게임에서 공격 애니메이션을 추가 해볼께요.
[오늘의 목표]
1. 공격 애니메이션 만들기
2. PlayerController 스크립트에 공격 애니메이션 추가하기
3. 실행 결과 살펴 보기
※ ChatGPT 답변 내용 중 제가 별도로 표시한 파란색 내용을 기준으로 읽으시면 좀 더 이해가 쉽게 될거예요.
공격 애니메이션 만들기
먼저 공격 애니메이션을 추가해볼께요.
1. 플레이어를 선택하고 애니메이션 메뉴를 클릭해요
2. PlayerPunch 애니메이션 클립을 생성해요.
3. 이제 필요한 스크라이트를 애니메이션으로 이동해요.
애니메이션이 잘 동작하는 지 확인해요.
PlayerController 스크립트에 공격 애니메이션 추가하기
이제 공격 애니메이션 스크립트를 기존 PlayerController 에 추가해볼께요.
[질문] 안녕 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 || wallSliding) && Input.GetButtonDown("Jump")) { jumpState = JumpState.PrepareToJump; Debug.Log("Jump State: PrepareToJump"); } else if (Input.GetButtonUp("Jump")) { stopJump = true; Schedule<PlayerStopJump>().player = this; Debug.Log("Jump Button Released"); } } 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); 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; } // 벽 점프 상태를 추가 public enum JumpState { Grounded, PrepareToJump, Jumping, InFlight, WallJumping, Landed } } }
[답변]
기존의 PlayerController 클래스에 공격 상태를 독립적으로 관리하는 방식으로 코드를 수정하여 플레이어가 점프 중에도 공격할 수 있도록 할 것입니다. 이를 위해 별도의 공격 상태 관리 시스템인 AttackState를 추가하고, 기존 로직은 그대로 유지하면서 공격 관련 로직을 통합하겠습니다.
AttackState 정의
공격 상태를 정의하기 위한 새로운 열거형을 추가합니다:
public enum AttackState { Idle, PreparingAttack, Attacking }
PlayerController 클래스 수정
기존 클래스에 공격 상태 관리를 위한 필드와 메소드를 추가합니다. 이는 점프 상태와 별개로 동작합니다:
public AttackState attackState = AttackState.Idle; protected override void Update() { if (controlEnabled) { move.x = Input.GetAxis("Horizontal"); // Jump input if ((jumpState == JumpState.Grounded || wallSliding) && Input.GetButtonDown("Jump")) { jumpState = JumpState.PrepareToJump; } else if (Input.GetButtonUp("Jump")) { stopJump = true; Schedule<PlayerStopJump>().player = this; } // Attack input if (Input.GetButtonDown("Fire1") && attackState == AttackState.Idle) { attackState = AttackState.PreparingAttack; } UpdateWallSlide(); UpdateJumpState(); UpdateAttackState(); } base.Update(); } void UpdateAttackState() { switch (attackState) { case AttackState.PreparingAttack: animator.SetTrigger("attack"); attackState = AttackState.Attacking; break; case AttackState.Attacking: if (animator.GetCurrentAnimatorStateInfo(0).IsName("PlayerPunch") && animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0) { attackState = AttackState.Idle; } break; } }
ComputeVelocity 수정
공격 중에는 플레이어의 이동을 제한할 수 있습니다:
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) { if (velocity.y > 0) { velocity.y = velocity.y * model.jumpDeceleration; } stopJump = false; } if (attackState == AttackState.Attacking) { move.x = 0; // 공격 중 이동 제한 } 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; }
이와 같은 방식으로 PlayerController 클래스를 수정하면, 플레이어는 점프 중에도 공격할 수 있으며, 기존 코드의 기능에 영향을 주지 않으면서 공격 기능을 통합할 수 있습니다. 공격과 점프를 독립적으로 관리함으로써 더 다양하고 동적인 게임 플레이를 구현할 수 있습니다.
기본적인 스크립트는 완료되었어요.
애니메이터를 수정해요 : animator.SetTrigger("attack")
공격 애니메이션이 정확히 실행될 수 있도록 애니메이터를 추가 및 수정해요.
플레이어를 선택하고 애니메이터 메뉴를 오픈해요. 방법은 아래 동영상과 같이 하면 되요.
플레이 모드에서 결과를 확인해요.
스크립트, 애니메이션 및 애니메이터 적용 사항을 저장하고 플레이 모드에서 결과를 확인해요.
원하는 결과가 나왔어요.
챗GPT와 개발은 언제나 쉽고 재미있는 것 같아요.
오늘은 ChatGPT를 활용하여 Unity 플랫포머 게임에서 공격 애니메이션을 추가 해봤어요.
다음 시간에는 공격 애니메이션을 적을 공격하는 기능을 추가해볼께요.
지금까지,
언제나 성장하는 인텔리원스튜디오(IntelliOneStudio)입니다.
감사합니다.
'디지털노마드' 카테고리의 다른 글
[챗GPT게임개발22] ChatGPT를 활용하여 적 공격 처리 추가하기(2) (63) | 2024.05.12 |
---|---|
[챗GPT게임개발21] ChatGPT를 활용하여 적 공격 처리 추가하기(1) (71) | 2024.05.11 |
[챗GPT게임개발19] ChatGPT를 활용하여 가속 패드와 Jiggler 추가하기 (65) | 2024.05.09 |
[챗GPT게임개발18] ChatGPT를 활용하여 트리거와 이벤트 시스템을 활용 (60) | 2024.05.09 |
[챗GPT게임개발17] ChatGPT를 활용하여 벽 타기 로직 추가하기 (58) | 2024.05.08 |