'전체 글'에 해당되는 글 720건

  1. 2026.10.17 [Godot] Breakout(Arkanoid) 벽돌깨기 3
반응형

플레이어(패들)를 만들어 보자. 이전 내용은 아래 링크를 참고한다.

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하고 게임 씬을 적당히 구성한다.

 

 

반응형
Posted by J-sean
: