ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • (3) 게임 세팅 , 공 발사 & 반사
    Galaxy Ball/1. 멀티플레이 - 대전모드 2024. 3. 25. 17:50

    <Stage>

    이번에는 게임이 구현될 가장 기본적인 스테이지를 만들 예정이다

     

    쉽게 말해 그림으로 그린 인게임 화면을 유니티로 똑같이 만들어보겠다

    첫번째로 가장 기본이 될 BackGround. 적당히 밝은 색으로 잡아준뒤, 위에서 공이 이동을 해야하기 때문에

    Rigidbody2D 컴포넌트를 넣어주고, Static으로 고정시켜준다

     

    두번째로 공이 밖으로 빠져나가지 않도록 벽의 역할을 해줄 Box Collider 2D를 Rigidbody2D와 함께 사면에 추가해준다

    이것 역시 움직일일 없이 고정시켜야 하므로 Rigidbody2D - Static을 선택해준다

     

     

    세번째로 DeadZone. 공이 닿아버리는 순간 게임을 종료시켜버릴 공간이다

    하지만 여기서 중요한건 Player1의 공이 Player2의 Deadzone에 닿는건 아무 문제가 되질 않는다는것이다

    마찬가지로 Player2의 공이 Player1의 DeadZone에 닿는것 역시 패배로 간주하지 않는다

     

    사전에 그것을 전부 인지하고 각각 맞는 상황에만 패배처리를 해주기 위해서 각각의 Deadzone에 태그를 지정해주자

    각각의 Deadzone에 알맞는 태그를 만들어 넣어주었다. 올바른 패배처리에 쓰일 예정이다

    이번엔 밸런스를 위해 고정구체를 배치해보겠다. 확실히 움직이지 않는 정적의 느낌을 위해 칙칙한 그레이색에

    Rigidbody, Circle Collider를 추가해주고 이것 역시 static으로 고정해둔다

     

    일일이 쓰진 않았지만 공통적으로 같은 성질을 가진 오브젝트끼리는 한 폴더안에 묶어넣는게 훨씬 수월하다

     

     

    Fire Zone과 아이템 UI만 제외하면 전부 만들어졌다. 이 둘은 후에 발사구현과, 아이템 구현에서 추가하는걸로 하겠다

     

    <Ball>

    이제 스테이지를 만들었으니 어떻게보면 가장 핵심이라고 할수 있는 구체를 만들어보자

    왼쪽은 Player1가 발사할 공, 오른쪽은 Player2의 공이다

    적당한 크기로 잡아준뒤 색상은 살짝 연한 빨간색으로 잡아보았다. 

    공은 플레이어 전용으로 각각 1개씩 만들어주었고, 아까처럼 공에도 각각 태그 P1Ball, P2Ball을 만들어 넣어주었다

    (이 모든게 다 Deadzone과 부딫힐때 나타날 패배처리를 위해서이다)

     

    이번엔 이동을 해야하는 구체니

     

    Rigidbody2D

    -Body Type : Dynamic

    -Gravity Scale : 0

     

    Circle Collider 2D를 추가해준다

     

    그리고 이제 본격적인 스크립트를 작성해주자. 원래는 발사구현을 한뒤 공의 이동을 구현해야 하지만

    지금은 다양한 위치에서 공이 움직이는것을 봐야하기 때문에

    원하는곳에 클릭을 하면 그곳에 공이 생성되어 적당한 세기로 앞으로 나아가는 코드정도로만 작성하겠다

     

    GameManager : 클릭한곳에 공이 생성

    BallController : 공의 움직임

     

    이런식으로 스크립트에 역할을 적당히 나눠보겠다

     

    using UnityEngine;
    
    public class GameManager : MonoBehaviour
    {
        public GameObject P1ballPrefab; 
        public GameObject P2ballPrefab;
        void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
                mousePosition.z = 0f; // z축을 0으로 설정하여 2D 공간에 배치합니다.
                Instantiate(P1ballPrefab, mousePosition, Quaternion.identity);
            }
            if (Input.GetMouseButtonDown(1))
            {
                Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
                mousePosition.z = 0f; // z축을 0으로 설정하여 2D 공간에 배치합니다.
                Instantiate(P2ballPrefab, mousePosition, Quaternion.identity);
            }
        }
    }

     

    우선 GameManager이다. Public으로 P1ball, P2ball 두 게임 오브젝트를 입력받는다

    그리고 편의를 위해 마우스 좌측버튼은 P1BallPrefab, 우측버튼은 P2BallPrefab을 생성시켜준다

     

    using System.Collections;
    using UnityEngine;
    
    public class BallController : MonoBehaviour
    {
        Rigidbody2D rigid;
        Vector2 lastVelocity;
        float deceleration = 2f;
        public float increase = 4f;
        public bool hasExpanded = false;
        private bool isStopped = false;
    
        private void Start()
        {
            rigid = GetComponent<Rigidbody2D>();
            rigid.velocity = transform.up * 300 * Time.deltaTime;
        }
    }

     

    이제 GameManager에서 생성시켜준 공을 움직여줄 스크립트이다. 

    Rigidbody2D 컴포넌트를 불러온뒤 velocity로 일정한 속력을 내어 앞으로 300 만큼 힘을 주었다

     

    BallController까지 다 만들었으니 공안에 각각 스크립트를 넣어준뒤 두 공 모두 프리팹으로 만들어준다

     

    이제 Hierarchy창에 빈 오브젝트 GameManager를 만들어준뒤 GameManager 스크립트를 넣어주고

    각각 알맞는 게임 오브젝트를 집어넣어준다

    한번 실행결과를 확인해보자. 잘 전진도 하고 뭔가 충돌판정도 일어나는것 같긴한데 뭔가 묘하게 달라붙는 느낌이다

    내가 원하는건 확실하게 반사가 되어 팅겨지는것을 원하는데 지금은 확실히 어색하다

     

    using System.Collections;
    using TMPro;
    using UnityEngine;
    
    public class BallController : MonoBehaviour
    {
        Rigidbody2D rigid;
        Vector2 lastVelocity;
        float deceleration = 2f;
        public float increase = 4f;
        public bool hasExpanded = false;
        private bool isStopped = false;
    
        private void Start()
        {
            rigid = GetComponent<Rigidbody2D>();
            rigid.velocity = transform.up * 300 * Time.deltaTime;
        }
        private void Update()
        {
            Move();
        }
        void Move()
        {
            if (rigid == null || isStopped) return;
    
            lastVelocity = rigid.velocity;
            rigid.velocity -= rigid.velocity.normalized * deceleration * Time.deltaTime;
        }
        private void OnCollisionEnter2D(Collision2D coll)
        {
            if (coll.contacts != null && coll.contacts.Length > 0)
            {
                // 충돌 지점 정보가 있을 때만 처리
                Vector2 dir = Vector2.Reflect(lastVelocity.normalized, coll.contacts[0].normal);
                if (rigid != null)
                    rigid.velocity = dir * Mathf.Max(lastVelocity.magnitude, 0f); // 감속하지 않고 반사만 진행
            }
        }
    }

     

    반사를 구현하기 위해 가장 보편적으로 사용하는것은 Reflect 기능이다

     

    Vector3.Reflect

    지정된 법선을 사용하여 표면을 벗어난 백터의 반사를 계산하는 함수 Vector3.Reflect(반영하려는 들어오는 벡터의 방향, 입력 벡터가 반사될 표면의 법선 벡터) incoming이 (1,1) normal(법선벡터)이 (0,1)

    sangeun00.tistory.com

    아예 Reflect에 대하여 글까지 작성해두었다. 반사는 그냥 무조건 이걸 가장 많이 사용하는것 같다

    이 자연스러운 반사를 구현하기위해 꽤나 시간이 걸렸다. 막상 다 짜고보니 별거 없어 보이는데 왜이렇게 애먹었나 싶다

     

     

     

    이제 확실히 반사가 자연스럽게 이루어지는것을 확인할 수 있다

Designed by Tistory.