ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • (2) 스테이지 씬 구현
    Galaxy Ball/2. 싱글플레이 - 스토리모드 2024. 5. 13. 17:42

    지난 글에서 기본 튜토리얼 씬을 구현했으니 이번에는 튜토리얼이 끝난뒤 스테이지를 선택할 수 있는

    스테이지 씬을 구현해보도록 하겠다

     

    보통 스테이지 선택하면 이런것들을 떠올리지만 난 UI에 자신도 없고

    그래도 뭔가 이것보다는 좀 더 참신한 무언가를 해보고 싶어 고안해보았다

     

     

    약간 오픈월드 느낌으로 커다란 판을 하나 만든 뒤 이 한판안에 모든 스테이지를 전부 담아볼 생각이다

     

    임의의 요소들을 배치해보았다. 위 그림에 보이는

    파란공(Player)은 플레이어의 컨트롤로 직접 움직이게 할 수 있는 오브젝트,

    빨간공(Stage)는 플레이어 구체와 닿았을때 해당하는 스테이지를 불러올 오브젝트이다

     

    가장 먼저 플레이어 구체의 이동을 구현해보도록 하겠다. 사실 이동은 

     

    (8) 발사구현 마무리

    왜 한템포가 늦게 나가는지 생각을 해봤다 1. 클릭2. BallController가 삽입된 구체 생성3. BallController Start 컴포넌트에 있는 발사가 실행됨하지만 shotDirection, ShotDistance가 없으니 초기값으로 값을 넣

    sangeun00.tistory.com

    이때 끝내놓은지 오래다. 그냥 이때 코드를 가져와서 조금만 바꿔주면 된다

    다른점이 있다면 위 글에서는 firezone에 클릭을 해야지만 구체가 생성되고 드래그로 발사하지만

    이번에는 firezone이 아닌 이미 존재하는 구체를 클릭한뒤 드래그로 발사할 예정이다

     

    using System;
    using UnityEngine;
    
    public class StageGameManager : MonoBehaviour
    {
        private Vector3 clickPosition;
        public bool isDragging = false;
        public static float shotDistance;
        public static Vector3 shotDirection;
        public bool isdone = false;
    
        private void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                clickPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
                clickPosition.z = 0f;
                Collider2D[] colliders = Physics2D.OverlapPointAll(clickPosition);
                foreach (Collider2D collider in colliders)
                {
                    if (collider.CompareTag("StageBall"))
                    {
                        isDragging = true;
                        break;
                    }
                }
            }
    
            if (isDragging && Input.GetMouseButtonUp(0))
            {
                Vector3 currentPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
                currentPosition.z = 0f;
                shotDistance = Vector3.Distance(clickPosition, currentPosition);
                Vector3 dragDirection = (currentPosition - clickPosition).normalized;
                shotDirection = -dragDirection;
                isDragging = false;
                Debug.Log(shotDistance);
                Debug.Log(shotDirection);
                Debug.Log("정보를 성공적으로 전달");
                isdone = true;
            }
        }
    }

     

    GameManager에서 가져와 쓸데없는 부분은 전부 잘라낸뒤 필요한 파트만 남겨놓은 StageGameManager.

    클릭, 드래그로 발사에 필요한 정보들을 이 스크립트에서 얻을 예정이고 빈오브젝트에 넣을 예정

     

     foreach (Collider2D collider in colliders)
                {
                    if (collider.CompareTag("StageBall"))
                    {
                        isDragging = true;
                        break;
                    }
                }

    약간의 차이점이라면 이제 클릭했을때 클릭받은 오브젝트의 태그가 StageBall이라면 조건문을 진행시킨다

    물론 태그는 걸어주었다

     

    using System.Collections;
    using TMPro;
    using Unity.VisualScripting;
    using UnityEngine;
    
    public class StageBallController : MonoBehaviour
    {
        Rigidbody2D rigid;
        Vector2 lastVelocity;
        float deceleration = 2f;
        private StageGameManager stageGameManager;
    
        private void Start()
        {
            rigid = GetComponent<Rigidbody2D>();
            stageGameManager = FindObjectOfType<StageGameManager>();
        }
    
        private void Update()
        {
            if (!stageGameManager.isDragging)
            {
                Debug.Log("StageBallContrller에서 클릭 뗀것을 인지");
                rigid.velocity = StageGameManager.shotDirection * StageGameManager.shotDistance; // GameManager에서 값 가져와서 구체 발사
                stageGameManager.isDragging = true;
            }
            Move();
        }
    
        void Move()
        {
            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); // 감속하지 않고 반사만 진행
            }
        }
    }

    마찬가지로 Ballcontroller에서 가져온 StageBallControl. 사실 가장 밑에 있는 반사파트는 굳이 필요할까 싶지만

    우선은 지우지 않고 남겨두었다. 당연히 Player구체에 부착될 예정

     

     private void Start()
        {
            rigid = GetComponent<Rigidbody2D>();
            stageGameManager = FindObjectOfType<StageGameManager>();

            stageGameManager에서 컴포넌트 불러옴
        }

        private void Update()
        {
            if (!stageGameManager.isDragging)

            스테이지매니저의 bool 타입 변수 isDragging이 false라면
            {
                rigid.velocity = StageGameManager.shotDirection * StageGameManager.shotDistance;

                스테이지매니저에서 힘과 방향 정보 받아와 구체 날림
                stageGameManager.isDragging = true;

                속도를 줄이는 Move함수를 실행해야 하기 때문에 조건문을 빠져나갈수 있는 장치
            }
            Move();
        }

     

    가장 중요한 부분이다. 특히 조건문 조건에 Input.GetMouseButtonDown(0)으로 했다가

    한참동안 작동이 이상하게 되어 애를 먹었는데 우선 StageGameManager에서 힘과 방향 정보를 받아와야지

    BallControl에서 날릴수 있기에 굳이 조건문에 게임매니저 변수인 isDragging을 받아온것이다

     

    using UnityEngine;
    
    public class CameraControl : MonoBehaviour 
    {
        GameObject player;
    
        void Start()
        {
            this.player = GameObject.Find("Player");
        }
        void Update()
        {
            Vector3 playerPos = this.player.transform.position;
            transform.position = new Vector3(playerPos.x, playerPos.y, transform.position.z);
        }
    }

     

    한가지 더 추가해주자. 이번엔 한 화면에서 진행되는것이 아닌 큰 화면에서 이동해야하기 때문에

    Player 구체가 이동하는대로 카메라가 따라가야한다. 게임오브젝트 Player를 찾아 카메라가 따라가는 스크립트를 만들어주자. 위 스크립트는 카메라에 부착될 예정이다

     

     

     

    이제 3개의 스크립트를 알맞은곳에 부착한뒤 게임을 실행해보자

    구체가 드래그에 맞게 움직이는것도, 그에 맞춰 카메라가 따라가는것도 확인할 수 있다

     

    이번엔 플레이어의 구체가 스테이지에 닿았을때 스테이지 이미지와 플레이 버튼이 활성화되도록 해보자

    임의로 캔버스에 만들어본 UI이고 플레이버튼을 누르면 해당 스테이지로 씬변환을 할 예정이다

    당연히 이미지와 텍스트, 버튼이니 UI요소인건 맞지만

     

     

     

    이걸 생각못했다. 늘 한 화면에서만 UI를 사용하다 넓은 화면에서 사용하려니 이렇게 되는걸 생각안하고 있었는데

    이런 현상이 일어나는 이유는 스크린좌표와 월드좌표 차이 때문이다

    플레이어 구체는 월드 좌표에 속해있고, UI는 스크린 좌표에 속해있으니 이런일이 일어나는것이다

     

     

     

    원래 계획은 모든 UI들을 전부 알맞은 곳에 배치시킨뒤 충돌에 맞는 해당 UI만 활성화/비활성화할 생각이었지만

    계획을 변경하겠다. UI가 보이게 될 위치를 하나만 고정시키고 해당 오브젝트에 맞는 UI만 활성화/비활성화 시키겠다

     

     private void OnTriggerStay2D(Collider2D collision)
     {
         if (collision.gameObject.tag == "Stage")
         {
             StageStart.gameObject.SetActive(true);
         }
     }
     private void OnTriggerExit2D(Collider2D collision)
     {
         StageStart.gameObject.SetActive(false);
     }

    StageBallControl을 수정해주자.  public으로 StageStart라는 이름의 게임 오브젝트를 만들어준뒤

    만약 충돌한 오브젝트의 태그가 Stage라면 StageStart를 활성화, 빠져나오면 비활성화되도록 설계하였다

     

    당연히 스테이지 구체에는 태그를 추가해주고 Circle Collider 2D에 Is Trigger까지 체크해주었다

     

     

    원하는대로 잘 작동하는것을 확인할 수 있다

     

     

Designed by Tistory.