본문 바로가기

프로젝트/미니게임파티

두 번째 미니게임 블록 깨기 개발 일지 1

728x90
반응형

두 번째 미니게임 - 블록 깨기

첫 번째 미니게임은 로직이 단순해서 다음 미니게임은 조금 더 복잡한 로직을 요구하는 게임을 만들고 싶었다.

미니게임 규모에 맞고 아트 리소스도 단순한 게임이 뭐가 있을까 고민하다가 [블록 깨기]로 정했다!

구글에 'breakout game'라고 검색하면 웹에서 플레이 해볼 수도 있어서 참고하기도 좋다.

초기 세팅하기

공, 패들, 벽돌들과 공이 충돌할 수 있도록 안 보이는 벽만 세팅하면 끝이다.

점수, 목숨, 시간 등을 나타낼 수 있는 UI는 위치만 잡아두고 세팅을 끝냈다.

공의 움직임을 물리적으로 구현하기 위해 공에 Rigidbody2D와 Collider2D 컴포넌트를 추가해주었다.

공과의 충돌 처리를 위해 벽돌과 패들, 안 보이는 벽에는 Collider2D 컴포넌트를 추가해주었다.

벽돌은 테스트를 위해 우선 미리 씬에 배치해두었다.

실제 게임화면에 보이는건 빨간 박스 안의 모습만 보인다.

PaddleContollrer - 좌우 방향키로 패들 움직이는 기능

벽돌 깨기에서 유저가 유일하게 컨트롤하는 부분은 패들이다.

패들은 단순히 좌우로만 움직이면 되기 때문에 코드도 매우 간단하게 작성할 수 있다.

public class PaddleController : MonoBehaviour
{
    public float speed;

    private void FixedUpdate()
    {
        float currentX = transform.position.x;
        float newX = currentX;

        if (Input.GetKey(KeyCode.LeftArrow))
        {
            newX -= speed;
        } else if (Input.GetKey(KeyCode.RightArrow))
        {
            newX += speed;
        }

        newX = Mathf.Clamp(newX, -14.7f, 14.7f);
        transform.position = new Vector3(newX, transform.position.y, transform.position.z);
    }
}

 

좌,우 화살표 키보드 입력을 받으면 현재 위치에서 좌,우로 위치를 옮겨주면 된다.

다만 패들이 화면에서 벗어나지 않도록 Mathf.Clamp 함수를 사용하여 x poisition 값을 보정해준다.

BallController - 공이 자연스럽게 튕기는 기능

공이 자연스럽게 튕기는 기능이 사실상 블록 깨기의 핵심 로직이라고 생각한다.

그래서 이 기능을 구현하는데 꽤 시행착오를 겪게 되었다.... 😢

 

우선, 패들에 공이 튕기는 로직은 비교적 단순하게 짤 수 있었다.

 

  1. 패들의 왼쪽 끝을 -1, 오른쪽 끝을 1로 정규화하여 공이 부딪힌 위치를 -1과 1의 사이의 값(relativeX)으로 나타낸다.
  2. 공이 패들의 중앙에 가까울수록 위로 튕길 수 있도록 relativeX값을 이용하여 y 방향의 값을 구한다.
  3. relativeX를 x방향 성분, bounceAngle을 y방향 성분으로 갖는 방향 벡터를 생성하고 정규화한다.
  4. 공의 velocity를 방향벡터와 원하는 속도로 설정한다.

참고로 정규화한다는 것은 크기가 1이고 같은 방향의 값을 가진 벡터를 반환하는 것입니다.
크기를 1로 만들기 때문에 속도 조절하기 위해서는 정규화를 해주는 것이 좋습니다.

 

float paddleX = collision.transform.position.x;
float contactX = collision.contacts[0].point.x;
float relativeX = (contactX - paddleX) / (collision.collider.bounds.size.x / 2f);

float bounceAngle = Mathf.Lerp(1f, 0.7f, Mathf.Abs(relativeX));
direction = new Vector2(relativeX, bounceAngle).normalized;

rb.velocity = direction * speed;

 

패들에 공이 성공적으로 튕기는 것을 확인 한 후에는 벽의 충돌 처리 작업에 들어갔다.

공이 오른쪽 위로 향하는 도중 벽돌에 충돌한다면 오른쪽 아래로 튕겨야 한다.

오른 쪽 위 방향 👉 수평 충돌 👉 반사 👉 오른쪽 아래 방향

이렇게 단순한 알고리즘을 생각하고 일단 냅다 코드를 작성했다.

 

 

public Rigidbody2D rb; // 공의 rigidbody

private void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.CompareTag("Wall"))
    {
        Vector2 reflectedVelocity = Vector2.Reflect(rb.velocity.normalized, collision.contacts[0].normal);
        rb.velocity = reflectedVelocity * speed;
    }
}

 

공의 velocity의 방향을 입사각, 충돌체의 법선(수평)을 기준으로 반사한 방향으로 설정하면 될거라고 생각했다.

하지만 다음 움짤처럼 부자연스럽게 공이 튕겨 날아가버렸다...

 

 

원리가 잘못된건지, 코드의 오류인지 갈피를 못 잡고 엉뚱한 부분만 수정했었다.

Update에서 강제로 velocity의 speed를 일정하게 고정시켜도 보고, paddle과 충돌할 때 수치도 조정해보고...

하지만 공의 velocity를 디버깅해보니 충돌 할 때의 velocity를 사용하는 것이 문제였다.

수평 충돌 할 때의 velocity의 y 방향 성분은 0으로 나오기 때문이다.

디버깅이 늦은 이유는 AI에게 질문했을 때 맞는 코드라고 해줘서 다른 부분에서 문제를 찾으려고 했었다.

문제점을 명확하게 찾으니 해결법은 간단했다.

Vector2 direction = rb.velocity.normalized;
var lastVelocity = collision.relativeVelocity;

direction = Vector2.Reflect(lastVelocity.normalized, rb.velocity.normalized);

rb.velocity = direction * speed;

 

충돌 직전의 상대 속도(collision.relativeVelocity)값을 이용하여 수정해주었다.

충돌 직전의 상대 속도를 입사각으로 설정하고 충돌할 때의 방향을 기준으로 반사시키니 자연스레 튕긴다!!!

 

 

이제 핵심 로직은 완성했으니 다음은 디테일한 기능들을 추가해야겠다.

 

728x90
반응형