Unity/프로젝트

Breakout(벽돌깨기) - (1)

gcreators 2024. 9. 25. 18:03

어릴적부터 해왔고 지금도 비슷한 형식의 게임들이 나오고 있다.

요즘은 어떤 게임의 미니게임들로 여러가지 버전으로 나와서 플레이의 신선함을 주는 정도로 활용 되는 게임이다

출시 당시와는 다르게 지금은 여러 아이템이나 기능들을 활용하고 있기도 하다

 

기본적인 게임형식으로는 아래 패널을 조작해 필드의 공을 쳐서 위의 벽돌 오브젝트들을 파괴하고 점수를 쌓는 형태를 지니고 있다

 

공부하는 관점으로는 시시각각 변하는 공의 벡터로 인해 학습용도로 괜찮은 게임이라고 할 수 있겠다

 

준비하기

우선 공이 벽과 패널에 닿을 시 튀게 하는 것이 목표이다

공에는 양 옆의 벽에 닿았을 때는 x좌표값을 음수로 바꿔 방향을 전환하고

패널이나 윗 벽에 닿았을 때 y좌표값을 음수로 바꿔 방향을 전환 해야 한다.

 

using UnityEngine;

public class BoWall : MonoBehaviour
{
    public enum EDir { Left, Right, Up, Down } //어떤 벽인지, Down은 사용되지 않음

    [SerializeField]
    private EDir dir = EDir.Left; //초기화 및 인스펙터에서 수정가능하게 바꿈

    public EDir Direction //외부에서 해당 벽이 어떤 벽인지 탐색하게 함
    {
        get { return dir; }
    }

}
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]//rigidbody가 없다면 추가
public class BOPaddle : MonoBehaviour
{
    [SerializeField] [Range(10f,30f)]
    private float moveSpeed = 20f;

    private Rigidbody rb = null;

    private Vector3 moveDir = Vector3.zero;

    public Vector3 MoveDirection //움직이는 방향구하기
    {
        get { return moveDir;}
    }

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
    }

    private void Update()
    {
        float axisH = Input.GetAxis("Horizontal");//수평 방향값을 저장
        Moving(axisH); 
    }

    private void Moving(float _axisH) //수평방향 값을 받아 이동 업데이트
    {
        moveDir = Vector3.right * _axisH; 
        rb.linearVelocity = moveDir * moveSpeed; //유니티 공식 - rigidbody로 물체를 움직일때는 deltaTime을 곱할 필요가 없다고 함(곱해도 문제는 없다)
        
    }
}
using UnityEngine;

public class BOBall : MonoBehaviour
{
    [SerializeField]
    [Range(20f,30f)]
    private float moveSpeed = 20f;

    [SerializeField]
    private Vector3 resetPos = Vector3.zero; //리셋 위치 조정 변수

    private Vector3 moveDir = Vector3.zero; //이동

    //게으른 초기화(Lazy Initialization)
    public void Init() //공 초기 위치 리셋 및 첫 공 발사
    {
        transform.position = resetPos;
        moveDir = new Vector3(Random.Range(-1f, 1f), Random.Range(-0.3f, -1f), 0);
        moveDir.Normalize();
    }

    private void Update()
    {
        Moving();

        DebugInfo();
    }

    private void Moving()
    {
        transform.Translate(moveDir * moveSpeed * Time.deltaTime);
    }

    private void DebugInfo() //공 예상 진행 방향에 선으로 표시
    {
        Debug.DrawLine(transform.position, transform.position + (moveDir * 2f), Color.yellow);
    }

    private void OnTriggerEnter(Collider _collider) //트리거를 발동한 collider
    {
        if (_collider.CompareTag("BOWall")) //트리거를 발동한 collider가 벽이라면
        {
            OnTriggerBall(_collider); //충돌처리
        }
        else if (_collider.CompareTag("BOPaddle")) //패널이라면
            OnTriggerPaddle(_collider);
    }

    private void OnTriggerBall(Collider _collider)
    {
        BoWall wall = _collider.GetComponent<BoWall>(); //BOwall 컴포넌트 저장

        switch (wall.Direction)
        {
            case BoWall.EDir.Left:
            case BoWall.EDir.Right:
                moveDir.x *= -1f; //충돌한 벽이 왼쪽, 오른쪽일때 공의 x좌표에 -1곱하여 방향을 반대로 바꿈 
                break;
            case BoWall.EDir.Up:
                moveDir.y *= -1f; //충돌한 벽이 윗쪽벽일때 공의 y좌표를 -를 곱해 변경
                break;
        }
    }

    private void OnTriggerPaddle(Collider _collider)
    {
        BOPaddle paddle =
            _collider.GetComponent<BOPaddle>(); //패널 컴포넌트 저장

        moveDir.y *= -1f; //패널 충돌시 처리
        Vector3 newDir = moveDir + paddle.MoveDirection; //패널의 움직이는 방향을 더해줘서 움직임 변경
        newDir.Normalize();//정규화
        moveDir = newDir;//변경된 움직임을 적용
    }
}
using UnityEngine;

public class BOGameManager : MonoBehaviour
{
    private BOBall ball = null;

    private void Awake() //공을 찾아 초기화, 보통은 인스펙터에서 처리하는 것을 권장
    {
        GameObject ballGo = GameObject.Find("BOBall"); 
        ballGo = GameObject.FindGameObjectWithTag("BOBall");

        ball = ballGo.GetComponent<BOBall>();
    }

    private void Start()
    {
        ball.Init();
    }
    private void Update()
    {
        Cheat();
    }

    private void Cheat()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            ball.Init(); //R키로 공 초기화
        }
    }
}