ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • (12) Start Scene #배경화면
    Galaxy Ball/1. 멀티플레이 - 대전모드 2024. 3. 29. 16:16

    이번엔 게임을 시작할때 볼 수 있는 게임시작 화면을 구현해보도록 하겠다

    우선 시작화면에 가장 필요한 핵심요소는 버튼과 배경화면이다

    버튼이야 UI를 쓰면 되니까 고민할게 없지만 중요한건 배경화면인데..

    어떻게하면 심플하면서 너무 간단하지는 않은 배경화면을 만들 수 있을까 고민이 됐다

     

    지금 생각난 아이디어는 2가지인데

     

    1. 적절하게 임의로 구체들을 배치해놓고 그 위에 UI를 띄우기

     

     

     

    2. 실시간으로 구체들이 발사되는 씬위에 UI를 띄우기

     

    당연히 간단한건 1번이지만 2번이 확실히 더 있어보이긴 할것같다. 

    그리고 플레이어가 직접 쏘게 하는것보다 임의로 구체를 날리게 하는게 조금은 더 간단한 방법이기 때문에 못할것도 없다

    확실히 나도 게임을 할때 시작화면이 정적이지 않고

    실시간으로 무언가가 동적으로 움직이고 있을때 퀄리티적인 부분에 더 만족감을 느꼈었던것 같다

     

    그렇다면 더더욱 게임을 퀄리티 있어보이게 할 수 있는 2번을 채택하겠다

    시작하기전 확실하게 정해놓고 시작하겠다

    내가 만드려는건 "실시간으로 랜덤값에 따라 자동으로 구체값이 발사되는 화면이고, 패배판정은 없다"

    패배 판정이 없는 이유는 시작화면에서 씬 변환까지 이동하기엔 과정이 복잡해지고 비효율적이기 때문이다

     

    우선 인게임 씬을 그대로 복붙해 Start Scene으로 이름을 붙여준다

    오히려 처음부터 만들 필요가 없으니 번거로움은 줄어든것 같다

     

    결국 공이 발사되는, 인게임의 핵심 스크립트는 단 2가지이다

     

    공을 생성하고, 그 공이 날아갈 방향과 힘을 정해주는 GameManager

    GameManager에서 받은 힘과 방향으로 날아가게 하는 BallController

     

    하지만 이번엔 내가 날리는것이 아니라 스크립트가 임의로 발사하는것이기 때문에

    GameManager에서 공이 날아갈 힘과 방향을 랜덤으로 잡아주고, 

    내가 클릭하지 않아도 공이 랜덤한 타임대에 생성하게 만들어주면된다

    즉, GameManager만 수정해주면 된다는 뜻

     

     

     

    인게임이나 시작화면이나 씬은 거의 똑같아보이지만 안에서 굴러가는 스크립트는 아예 다른 개념이기에 

    인게임 씬에 있는 모든것을 전부 복붙해와 가져와야 한다

     

    이번 경우는 아예 새 폴더를 만들어 이름앞에 SC(Start sCene)를 붙인뒤 보기편하게 모아뒀다

     

    그리고 오류방지를 위해 클래스의 이름과 각종 세부적인 내용들을 알맞게 변경해준다

     

    아까 복붙해둔 게임시작씬으로 들어가 GameManager 오브젝트에 부착된 기존 GameManager 스크립트를 제거한뒤

    새로만든 SCGameManager를 부착한다. 알맞게 오브젝트들도 넣어주자

    SCP1BallPrefab도 마찬가지. SC스크립트로 다시 붙여준뒤 해당 스크립트에 있는 오디오 소스도 다시 넣어주자

     

    Deadzone를 스크립트를 가져오지 않은 이유는 위에 설명한대로 패배판정을 주지 않을 예정이기 때문이다

    물론 씬에서도 패배판정이 없어 나는 오류를 없애주기 위해 Deadzone을 둘다 삭제해주자

     

    딱히 별거 없는데도 여기까지 오는데 왜 이렇게 오래걸린지 모르겠지만..

    이제 모든 준비는 끝났다. 본격적으로 새로 넣어준 스크립트를 수정해주자

     foreach (Collider2D collider in colliders)
     {
         if (P1FireMode && collider.gameObject == P1firezone)
         {
             Instantiate(P1ballPrefab, clickPosition, Quaternion.identity);
             P1FireMode = false;
             P2FireMode = true;
             isDragging = true;
             P1firezone.gameObject.SetActive(false);
             P2firezone.gameObject.SetActive(true);
             break;
         }
         if (P2FireMode && collider.gameObject == P2firezone)
         {
             Instantiate(P2ballPrefab, clickPosition, Quaternion.identity);
             P1FireMode = true; // P2FireMode가 활성화된 후에 P1FireMode를 재활성화
             P2FireMode = false;
             isDragging = true;
             P1firezone.gameObject.SetActive(true);
             P2firezone.gameObject.SetActive(false);
             break;
         }
     }
      if (isDragging && Input.GetMouseButtonUp(0))
      {
          Vector3 currentPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
          currentPosition.z = 0f;
          GameManager.shotDistance = Vector3.Distance(clickPosition, currentPosition);
          Vector3 dragDirection = (currentPosition - clickPosition).normalized;
          GameManager.shotDirection = -dragDirection;
          isDragging = false;
      }

     

    SCGameManager에서 수정해야 할 부분은 이렇게 두가지. 구체의 생성과 구체의 힘,방향이다

     

     private void Update()
     {
         // 최초 클릭 이후에만 힘이 가해지도록 설정
         if (Input.GetMouseButtonUp(0) && rigid != null && !hasBeenReleased)
         {
             rigid.velocity = Gamemanager.shotDirection * Gamemanager.shotDistance; // GameManager에서 값 가져와서 구체 발사
             hasBeenReleased = true; // 최초 클릭이 되었음을 표시
         }
         Move();
         expand();
     }

     

    SCBallContorller에서 수정해야 하는 부분도 있으니 바로 발사 조건이다

    지금은 마우스 버튼을 올려야 공이 발사되게 만들었으나 이것도 자동으로 발사되게 만들어야한다

     

    --------------------------------------------------------------------------------------------------------------

     

    내가 이걸 괜히 하겠다고 해서...온갖 고생끝에 겨우 완성했다

    using System.Collections;
    using UnityEngine;
    
    public class SCGameManager : MonoBehaviour
    {
        public GameObject P1ballPrefab;
        public GameObject P2ballPrefab;
        public GameObject P1firezone;
        public GameObject P2firezone;
    
        private bool P1FireMode = true;
        private bool P2FireMode = false;
    
        public static float shotDistance;
        public static Vector3 shotDirection;
    
        private void Start()
        {
            P1firezone.gameObject.SetActive(true);
            P2firezone.gameObject.SetActive(true);
            StartCoroutine(SpawnBall());
        }
    
        private IEnumerator SpawnBall()
        {
            while (true)
            {
                yield return new WaitForSeconds(Random.Range(2.5f, 3.5f));
    
                GameObject ballPrefab;
                GameObject fireZone;
    
                if (P1FireMode)
                {
                    ballPrefab = P1ballPrefab;
                    fireZone = P1firezone;
                }
                else
                {
                    ballPrefab = P2ballPrefab;
                    fireZone = P2firezone;
                }
    
                Vector3 spawnPosition = GetRandomPositionInFireZone(fireZone);
                GameObject ball = Instantiate(ballPrefab, spawnPosition, Quaternion.identity);
    
                CalculateRandomShot();
    
                SwitchFireMode();
            }
        }
    
        private Vector3 GetRandomPositionInFireZone(GameObject fireZone)
        {
            Collider2D zoneCollider = fireZone.GetComponent<Collider2D>();
            Bounds bounds = zoneCollider.bounds;
            Vector3 randomPoint = new Vector3(
                Random.Range(bounds.min.x, bounds.max.x),
                Random.Range(bounds.min.y, bounds.max.y),
                0
            );
            return randomPoint;
        }
    
        private void SwitchFireMode()
        {
            P1FireMode = !P1FireMode;
            P2FireMode = !P2FireMode;
            P1firezone.gameObject.SetActive(P1FireMode);
            P2firezone.gameObject.SetActive(P2FireMode);
        }
    
        private void CalculateRandomShot()
        {
            shotDistance = Random.Range(10f, 15f); // 예시 값, 원하는 범위로 조정 가능
            float x = Random.Range(0f, 360f); // 0부터 360도 사이의 각도
            float y = Random.Range(0f, 360f);
            shotDirection = new Vector3(x, y, 0).normalized; // 각도를 벡터로 변환
        }
    }
    using System.Collections;
    using System.Collections.Generic;
    using TMPro;
    using UnityEngine;
    
    public class SCBallController : MonoBehaviour
    {
        Rigidbody2D rigid;
        Vector2 lastVelocity;
        float deceleration = 2f;
        public float increase = 4f;
        private bool iscolliding = false;
        public bool hasExpanded = false;
        private bool isStopped = false;
        private int randomNumber;
        private TextMeshPro textMesh;
        private bool hasBeenReleased = false; // 최초 클릭이 되었는지 여부를 추적
    
        public AudioSource HitSound;
        public AudioSource SwellSound;
    
    
        private void Start()
        {
            rigid = GetComponent<Rigidbody2D>();
    
            GameObject textObject = new GameObject("TextMeshPro");
            textObject.transform.parent = transform;
            textMesh = textObject.AddComponent<TextMeshPro>();
            randomNumber = Random.Range(1, 6);
            textMesh.text = randomNumber.ToString();
            textMesh.fontSize = 4;
            textMesh.alignment = TextAlignmentOptions.Center;
            textMesh.autoSizeTextContainer = true;
            textMesh.rectTransform.localPosition = Vector3.zero;
            textMesh.sortingOrder = 1;
            LaunchBall();
    
        }
            Move();
            expand();
        }
    
        void Move()
        {
            if (rigid == null || isStopped) return;
    
            lastVelocity = rigid.velocity;
            rigid.velocity -= rigid.velocity.normalized * deceleration * Time.deltaTime;
    
            if (rigid.velocity.magnitude <= 0.01f && hasExpanded)
            {
                isStopped = true;
                StartCoroutine(DestroyRigidbodyDelayed());
            }
        }
        void expand()
        {
            if (rigid == null || iscolliding) return;
            if (rigid.velocity.magnitude > 0.01f) return;
            if (Input.GetMouseButton(0)) return;
    
            if (!hasExpanded)
            {
                SwellSound.Play();
            }
            transform.localScale += Vector3.one * increase * Time.deltaTime;
            hasExpanded = true;
        }
    
        private void OnCollisionEnter2D(Collision2D coll)
        {
            if (!hasExpanded)
            {
                HitSound.Play();
            }
    
            if ((coll.gameObject.tag == "P1ball" || coll.gameObject.tag == "P2ball") && rigid == null)
            {
                if (randomNumber > 0)
                {
                    randomNumber--;
                    textMesh.text = randomNumber.ToString();
                }
                if (randomNumber <= 0)
                {
                    Destroy(gameObject);
                }
            }
            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); // 감속하지 않고 반사만 진행
            }
            this.iscolliding = true;
    
        }
        private void OnCollisionExit2D(Collision2D collision)
        {
            this.iscolliding = false;
        }
    
        IEnumerator DestroyRigidbodyDelayed()
        {
            yield return new WaitForSeconds(0.8f);
            if (rigid != null)
                Destroy(rigid);
        }
        public void LaunchBall()
        {
            if (rigid != null)
            {
                rigid.velocity = SCGameManager.shotDirection * SCGameManager.shotDistance; // SCGameManager에서 값 가져와서 구체 발사
                hasBeenReleased = true; // 최초 클릭이 되었음을 표시
            }
        }
    }

     

    각각 SCGameManager와 SCBallController 코드이다

     

    일일이 다 설명하자면 너무 길어질것 같으니 수정된 내역을 간단하게 요약하자면

     

    <SCGameManager>

    1. shotDistance에는 float 타입 랜덤값, 

    2. shotDirection에서는 float 타입 변수 x,y에 랜덤값을 준뒤 normalized 해주어 랜덤 방향으로 초기화해준다

    3. 구체를 생성하는 시간도 모두 랜덤으로 잡아주었다

     

    <SCBallController>

    1. 발사되는 조건을 (rigid != null)로 간단하게 바꿈

    2. 공을 발사하는 LaunchBall() 메서드를 만들어 Start()에 배치

     

     

    패배판정 이루어지지 않고, 턴 번갈아가며 발사하는것을 볼수 있다

    번갈아 생성은 되는데 발사가 이루어지질 않아 이것때문에 애를 많이 먹었는데 감격스럽다..ㅠ

     

     

Designed by Tistory.