[Godot] Breakout(Arkanoid) 벽돌깨기 3
Godot 2026. 10. 17. 22:45 |반응형
플레이어(패들)를 만들어 보자. 이전 내용은 아래 링크를 참고한다.
2026.09.29 - [Godot] - [Godot] Breakout(Arkanoid) 벽돌깨기 1
2026.10.16 - [Godot] - [Godot] Breakout(Arkanoid) 벽돌깨기 2

플레이어 씬을 만들고 CharacterBody2D - Sprite2D - CollisionShape2D를 구성한다.

Sprite2D의 Texture는 GradientTexture2D를 이용해 적당히 만들어 준다.
using Godot;
public partial class Player : CharacterBody2D
{
public const float Speed = 600.0f;
public override void _PhysicsProcess(double delta)
{
Vector2 velocity = Vector2.Zero;
// 좌우 방향키 입력 받기 (-1.0은 왼쪽, 1.0은 오른쪽, 0은 정지)
float direction = Input.GetAxis("ui_left", "ui_right");
if (direction != 0)
{
velocity.X = direction * Speed;
}
Velocity = velocity;
MoveAndSlide();
}
}
플레이어 스크립트는 위와 같이 작성한다.
플레이어(패들)의 어느 부분에 볼이 충돌하는지에 따라 반사각을 결정하기 위해 볼 스크립트도 아래와 같이 수정 한다.
using Godot;
using System.Reflection.Metadata;
public partial class Ball : CharacterBody2D
{
public const float Speed = 400.0f;
// 처음에는 방향이 없는 상태(정지)로 둔다.
private Vector2 _direction = Vector2.Zero;
// 게임이 시작되었는지 확인하는 변수
private bool _isStarted = false;
// 화면에 직접 도형이나 선을 그리는 전용 함수
public override void _Draw()
{
if (!_isStarted)
{
// 내 위치(로컬 좌표계의 중심점인 Vector2.Zero)부터 마우스의 로컬 위치까지
// 노란색(Colors.Yellow)으로 두께 2.0f의 선을 그린다.
//DrawLine(Vector2.Zero, GetLocalMousePosition(), Colors.Yellow, 2.0f); // 직선
DrawDashedLine(Vector2.Zero, GetLocalMousePosition(), Colors.Yellow, 2.0f, 10.0f); // 점선
// 그리기 함수는 화면 전체(Global)가 아니라 내 캐릭터를 중심으로 한 지역(Local) 좌표를 기준으로
// 작동한다. 그래서 시작점을 내 위치인 Vector2.Zero(0, 0)로 두고, 끝점을 내 기준에서의 마우스
// 위치로 연결하면 완벽한 조준선이 된다.
}
}
// 매 프레임마다 화면 갱신을 요청.
public override void _Process(double delta)
{
if (!_isStarted)
{
// 발사 전일 때는 마우스가 움직일 때마다 선을 새로 그려야 하므로
// 엔진에게 다시 그리라고(Redraw) 지속적으로 요청한다.
QueueRedraw();
}
}
public override void _PhysicsProcess(double delta)
{
// 아직 발사 전이라면?
if (!_isStarted)
{
// 마우스 왼쪽 버튼을 눌렀을때
if (Input.IsMouseButtonPressed(MouseButton.Left))
{
_isStarted = true; // 발사 시작
// 마우스의 전역 좌표에서 내 좌표를 빼면 나로부터 마우스를 향하는 방향이다.
// Normalized()를 계산해 화살표의 길이를 딱 '1'로 맞춰주어야 속도가 일정해진다.
_direction = (GetGlobalMousePosition() - GlobalPosition).Normalized();
// 발사되는 순간, 더 이상 선을 그리지 않도록 마지막으로 한 번 더 갱신 요청을 해서 선을 지운다.
QueueRedraw();
}
// 아직 스페이스바를 안 눌렀다면 여기서 함수를 끝내서 움직이지 않게 한다.
return;
}
// 발사된 공을 이동시킨다. MoveAndCollide는 부딪힌 물체의 정보를 반환한다.
// delta를 곱해서 프레임과 상관없이 일정한 속도로 이동하게 만든다.
KinematicCollision2D collision = MoveAndCollide(_direction * Speed * (float)delta);
if (collision != null) // 만약 어딘가에 부딪혔다면..
{
Node2D collider = (Node2D)collision.GetCollider();
// 벽돌과 부딪혔을 때
if (collider is Brick brick)
{
_direction = _direction.Bounce(collision.GetNormal());
brick.Hit();
}
// 패들과 부딪혔을 때
else if (collider is Player player)
{
// 충돌한 X좌표에서 패들 중심의 X좌표를 뺀다.
// 결과가 음수면 패들의 왼쪽, 양수면 오른쪽에 맞은 것이다.
float hitPointX = collision.GetPosition().X;
float paddleCenterX = player.GlobalPosition.X;
float offset = hitPointX - paddleCenterX;
// 패들의 절반 너비 (막대기 크기에 맞춰 이 숫자를 조절한다. 예: 가로 100픽셀이면 50)
float halfPaddleWidth = 80.0f;
// 중심에서 벗어난 정도를 -1.0(왼쪽 끝) ~ 1.0(오른쪽 끝) 비율로 정규화.
// Mathf.Clamp는 주어진 값을 최소값과 최대값 사이로 제한하는 함수이다.
// 예를 들어 Clamp(10, -1, 1)은 1을 반환한다.
float ratio = Mathf.Clamp(offset / halfPaddleWidth, -1.0f, 1.0f);
// 최대 꺾이는 각도를 60도로 설정한다.
float maxAngle = Mathf.DegToRad(60.0f);
float bounceAngle = ratio * maxAngle;
// 수직 위쪽 방향(Vector2.Up)을 기준으로 우리가 구한 각도만큼 화살표를 회전
_direction = Vector2.Up.Rotated(bounceAngle);
}
// 일반 벽(StaticBody2D)과 부딪혔을 때
else
{
_direction = _direction.Bounce(collision.GetNormal());
}
}
}
}

위에서 만든 플레이어를 Instantiate하고 게임 씬을 적당히 구성한다.

반응형
'Godot' 카테고리의 다른 글
| [Godot] Breakout(Arkanoid) 벽돌깨기 2 (0) | 2026.10.16 |
|---|---|
| [Godot] Breakout(Arkanoid) 벽돌깨기 1 (0) | 2026.09.29 |
| [Godot] Game Manager (Singleton) 게임 메니저 (0) | 2026.09.28 |
| [Godot] Flickering Candles 바람에 흔들리는 촛불 (0) | 2026.09.22 |
| [Godot] Network Multi Play - Character Animation 네트워크 멀티 플레이 - 캐릭터 애니메이션 (0) | 2026.09.20 |
