티스토리 뷰
💻 Unity
2D Raycast
isGrounded = Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);
void OnDrawGizmos()
{
Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));
}
groundCheck 위치에서 플레이어의 아래쪽(Vector2.down)으로 groundCheckDistance만큼 레이캐스트를 쏴서 땅이 있는지 확인
isWallDetected = Physics2D.Raycast(wallCheck.position, Vector2.right, wallCheckDistance * facingDir, whatIsGround);
void OnDrawGizmos()
{
Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance * facingDir, wallCheck.position.y));
}
wallCheck 위치에서 **바라보는 방향(facingDir)**으로 wallCheckDistance만큼 레이캐스트를 쏴서 벽이 있는지 확인
🕹️ 실습 (2D 횡스크롤 2)
Entity와 상속으로 Player, Enemy 구현
public class Entity : MonoBehaviour
{
protected Animator animator;
protected Rigidbody2D rb;
protected int facingDir = 1;
protected bool isFacingRight = true;
[Header("Collision Info")]
[SerializeField]
protected Transform groundCheck;
[SerializeField]
protected float groundCheckDistance;
[Space]
[SerializeField]
protected Transform wallCheck;
[SerializeField]
protected float wallCheckDistance;
[Space]
[SerializeField]
private LayerMask whatIsGround;
protected bool isGrounded;
protected bool isWallDetected;
protected virtual void Start()
{
animator = GetComponentInChildren<Animator>();
rb = GetComponent<Rigidbody2D>();
}
protected virtual void Update()
{
CheckCollision();
}
protected virtual void Flip()
{
facingDir = facingDir * -1;
isFacingRight = !isFacingRight;
transform.Rotate(0, 180, 0);
}
protected virtual void CheckCollision()
{
isGrounded = Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);
isWallDetected = Physics2D.Raycast(wallCheck.position, Vector2.right, wallCheckDistance * facingDir, whatIsGround);
}
protected virtual void OnDrawGizmos()
{
Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));
Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance * facingDir, wallCheck.position.y));
}
}
public class EnemySkeleton : Entity
{
bool isAttacking;
[Header("Move Info")]
[SerializeField]
private float moveSpeed;
[Header("Player Derection")]
[SerializeField]
private float playerCheckDistance;
[SerializeField]
private LayerMask whatIsPlayer;
private RaycastHit2D isPlayerDetected;
protected override void Start()
{
base.Start();
}
protected override void Update()
{
base.Update();
MoveEnemySkeleton();
HandlePlayerDerection();
}
protected override void CheckCollision()
{
base.CheckCollision();
isPlayerDetected = Physics2D.Raycast(transform.position, Vector2.right, playerCheckDistance * facingDir, whatIsPlayer);
if (!isGrounded || isWallDetected)
{
Flip();
}
}
private void MoveEnemySkeleton()
{
if (!isAttacking)
{
rb.linearVelocity = new Vector2(moveSpeed * facingDir, rb.linearVelocityY);
}
}
private void HandlePlayerDerection()
{
if (!isAttacking)
{
rb.linearVelocity = new Vector2(moveSpeed * facingDir, rb.linearVelocityY);
}
if (isPlayerDetected)
{
if (isPlayerDetected.distance > 1)
{
// 추적
rb.linearVelocity = new Vector2(moveSpeed * 1.5f * facingDir, rb.linearVelocityY);
isAttacking = false;
Debug.Log("Enemy : 플레이어 감지");
}
else
{
isAttacking = true;
Debug.Log($"Enemy : 공격! {isPlayerDetected.collider.gameObject.name}");
}
}
}
protected override void OnDrawGizmos()
{
base.OnDrawGizmos();
Gizmos.color = Color.blue;
Gizmos.DrawLine(transform.position, new Vector3(transform.position.x + playerCheckDistance * facingDir, transform.position.y));
}
}
🕹️ 실습 (2D 횡스크롤 3)
StateMachine으로 Player 구현
public class PlayerState
{
protected Player player;
protected PlayerStateMchine stateMachine;
private string animationBoolName;
public PlayerState(Player _player, PlayerStateMchine _stateMachine, string _animationBoolName)
{
player = _player;
stateMachine = _stateMachine;
animationBoolName = _animationBoolName;
}
public virtual void Enter()
{
Debug.Log($"Enter - {animationBoolName}");
}
public virtual void Update()
{
Debug.Log($"Update - {animationBoolName}");
}
public virtual void Exit()
{
Debug.Log($"Exit - {animationBoolName}");
}
}
public class PlayerStateMchine
{
public PlayerState currentState { get; private set; }
public void Initialize(PlayerState startState)
{
currentState = startState;
currentState.Enter();
}
public void ChangeState(PlayerState newState)
{
currentState.Exit();
currentState = newState;
currentState.Enter();
}
}
public class PlayerIdleState : PlayerState
{
public PlayerIdleState(Player _player, PlayerStateMchine _stateMachine, string _animationBoolName)
: base(_player, _stateMachine, _animationBoolName)
{
}
public override void Enter()
{
base.Enter();
}
public override void Update()
{
base.Update();
if (Input.GetKeyDown(KeyCode.N))
{
player.stateMachine.ChangeState(player.moveState);
}
}
public override void Exit()
{
base.Exit();
}
}
public class Player : MonoBehaviour
{
public PlayerStateMchine stateMachine { get; private set; }
public PlayerIdleState idleState { get; private set; }
public PlayerMoveState moveState { get; private set; }
private void Awake()
{
stateMachine = new PlayerStateMchine();
idleState = new PlayerIdleState(this, stateMachine, "Idle");
moveState = new PlayerMoveState(this, stateMachine, "Move");
}
private void Start()
{
stateMachine.Initialize(idleState);
}
private void Update()
{
stateMachine.currentState.Update();
}
}
📝 과제
스테이트 머신 구현
'Unity > 멋쟁이사자처럼' 카테고리의 다른 글
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 29일차 회고 (0) | 2025.04.03 |
---|---|
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 28일차 회고 (0) | 2025.04.03 |
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 27일차 회고 (0) | 2025.04.03 |
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 23일차 회고 (0) | 2025.04.03 |
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 22일차 회고 (0) | 2025.04.03 |