본문 바로가기
Development/C#

멋쟁이사자처럼부트캠프 Unity 게임 개발 3기 75일차

by Mobics 2025. 3. 18.

 

목차


    오목 게임 만들기

    25.03.17

    각자 맡은 역할에 따라 구현하기

    : 저는 메인화면 UI를 담당하여 구현

     

    Main Scene

    : Main Scene 추가

     

    >> Canvas 추가 및 설정

     

    >> Main Panel 생성 및 설정

    : Source Image를 None으로, Color는 (0, 0, 0, 255)로 설정

     

    >> 'Main Panel'의 자식으로 'Logo' 생성

    : UI-Image로 생성, Color는 (217, 217, 217, 255) --> Hexadecimal : 'D9D9D9'

    ▶ '64' Sprite Slice

    --> Anchor는 Alt + Shift

     

    >> 'Logo'의 자식으로 'Title Text' 생성

    : Font는 아직 미정

     

    └ Game Buttons 생성

    : 빈 게임 오브젝트로 생성, Vertical Layout Group 추가 및 설정

    --> Anchor는 Alt + Shift

     

    >> 'Game Buttons'의 자식으로 'Play Button', 'Exit Button' 생성

    : 'Dark UI' - 'Prefabs Button' - 'Play Button' 을 가져와서 Prefab을 해제한 다음 사용

    --> Exit Button은 'Play Button'을 복붙하여 Text 내용만 수정

     

    └ Menu Buttons 생성

    : 빈 게임 오브젝트로 생성, Horizontal Layout Group 추가 및 설정

    --> Anchor는 Alt + Shift

     

    >> 'Menu Buttons'의 자식으로 'Ranking Button', 'Notation Button', 'Shop Button', 'Settings Button' 생성

    : 'Dark UI' - 'Prefabs Button' - 'Ranking Button' 을 가져와서 Prefab을 해제한 다음 사용

    --> 나머지 버튼은 전부 'Ranking Button'을 복붙하여 Source Image를 바꾸고 Text 내용을 수정하여 사용

     

    >> Coin Image 및 Text 추가

    --> Anchor는 Alt + Shift

     

    └ User Interface 생성

    : 빈 게임 오브젝트로 생성

    --> Anchor는 Alt + Shift

     

    >> Image 생성

     

    >> Texts 생성

    : 빈 게임 오브젝트로 생성, Vertical Layout Group 추가

    --> Anchor는 Alt + Shift

     

    >> 'Texts'의 자식으로 'UserID Text', 'Nickname Text', 'Rank Text', 'Victories Text' 생성

     

    >> 'User Image' 생성

    : UI-Image로 생성

    ▶ '32' Sprite Slice

    --> Anchor는 Alt + Shift

     

    >> 'Icon Change Button' 생성

    : UI-Button으로 생성

    --> Anchor는 Alt + Shift

     

    >> 'Ranking Bar' 생성

    : 빈 게임 오브젝트로 생성

    --> Anchor는 Alt + Shift

     

    >> 'Ranking Bar'의 자식으로 'Ranking Bar Image', '0', 'UpPoint Text', 'DownPoint Text', 'Point Image', 'Ranking Text' 생성

    --> Anchor는 Alt + Shift

     

    >> 'Logout Button' 생성

    : UI-Button으로 생성

    --> Anchor는 Alt + Shift

     

    └ Popup Panel 생성

    : UI-Panel로 생성 --> 여러 Popup Panel의 공통된 부분을 만들기

    --> Anchor는 Alt + Shift

     

    >> 'Popup Panel'의 자식으로 'Panel' 생성

    : UI-Panel로 생성

     

    >> 'Panel'의 자식으로 'Panel BackGround', 'Panel Bottom Image', 'Close Button' 생성

    --> Anchor는 Alt + Shift

     

    >> 'Popup Panel'을 Prefab화 후, Hierarchy에서 삭제

     

    └ 코드 작성

    : 이전의 'Quiz Game' 프로젝트에서 'Singleton.cs' 가져오기

     

    >> 'MainPanelController.cs' 생성

    : Main Panel의 Button들을 눌렀을 때 호출되는 함수들을 정의

    --> Panel들의 Prefab을 받아 생성해줌

     

    >> 'PanelController.cs' 생성

    : DOTween을 사용하여 모든 Popup Panel에 쓰일 Animation 추가

     

    >> 각각 맞게 함수 추가 및 버튼의 OnClick()에 함수 바인딩

    1. Main Panel

    : Prefab으로 'Popup Panel'을 바인딩한 것은 임시로 테스트하기 위함

     

    2. 'User Interface'의 'Logout Button'

     

    3. 'Game Buttons'의 'Play Button', 'Exit Button'

    --> 'Exit Button'도 마찬가지로 맞게 바인딩

     

    4. 'Menu Buttons'의 'Ranking Button', 'Notation Button', 'Shop Button', 'Settings Button'

    --> 다른 버튼들도 마찬가지로 각각에 맞게 바인딩

     

    5. 'Popup Panel'

     

    6. 'Popup Panel'의 'Close Button'

     

    └ 구현된 모습

     

    최종 코드

    >> Singleton.cs

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public abstract class Singleton<T> : MonoBehaviour where T : Component
    {
        private static T _instance;
    
        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = FindObjectOfType<T>();
                    if (_instance == null)
                    {
                        GameObject obj = new GameObject();
                        obj.name = typeof(T).Name;
                        _instance = obj.AddComponent<T>();
                    }
                }
                return _instance;
            }
        }
    
        private void Awake()
        {
            if (_instance == null)
            {
                _instance = this as T;
                DontDestroyOnLoad(gameObject);
                // 경우에 따라 첫 Scene의 OnSceneLoaded가 호출이 안 되는 경우를 해결
                OnSceneLoaded(SceneManager.GetActiveScene(), LoadSceneMode.Single);
                
                // Scene 전환 시, 호출되는 Action Method 할당
                SceneManager.sceneLoaded += OnSceneLoaded;
            }
            else
            {
                Destroy(gameObject);
            }
        }
    
        // Destroy 후에는 OnSceneLoaded가 할당하지 않도록
        private void OnDestroy()
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
        }
    
        protected abstract void OnSceneLoaded(Scene scene, LoadSceneMode mode);
    }

     

    >> GameManager.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class GameManager : Singleton<GameManager>
    {
        public void StartGame()
        {
            // SceneManager.LoadScene("Game");
        }
        
        protected override void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            
        }
    }

     

    >> MainPanelController.cs

    using System.Collections;
    using System.Collections.Generic;
    using TMPro;
    using UnityEngine;
    
    public class MainPanelController : MonoBehaviour
    {
        [SerializeField] private Transform canvasTransform;
    
        [SerializeField] private GameObject rankingPanelPrefab;
    
        /// <summary>
        /// '대국 시작' 버튼을 눌렀을 때 호출되는 함수
        /// </summary>
        public void OnClickPlayButton()
        {
            GameManager.Instance.StartGame();
        }
    
        public void OnClickExitButton()
        {
            
        }
    
        /// <summary>
        /// '로그아웃' 버튼을 눌렀을 때 호출되는 함수
        /// </summary>
        public void OnClickLogoutButton()
        {
            
        }
    
        #region Main Menu 버튼 클릭 함수
    
        /// <summary>
        /// '랭킹' 버튼을 눌렀을 때 호출되는 함수
        /// </summary>
        public void OnClickRankingButton()
        {
            Instantiate(rankingPanelPrefab, canvasTransform);
        }
    
        /// <summary>
        /// '내 기보' 버튼을 눌렀을 때 호출되는 함수
        /// </summary>
        public void OnClickNotationButton()
        {
            
        }
    
        /// <summary>
        /// '상점' 버튼을 눌렀을 때 호출되는 함수
        /// </summary>
        public void OnClickShopButton()
        {
            
        }
    
        /// <summary>
        /// '설정' 버튼을 눌렀을 때 호출되는 함수
        /// </summary>
        public void OnClickSettingsButton()
        {
            
        }
        
        #endregion
    }

     

    >> PanelController.cs

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using DG.Tweening;
    
    public class PanelController : MonoBehaviour
    {
        [SerializeField] private GameObject panelObject;    // 팝업창 오브젝트
    
        private void Awake()
        {
            panelObject.GetComponent<CanvasGroup>().alpha = 0;
        }
    
        private void Start()
        {
            ShowPopupPanel();
        }
    
        public void OnClickCloseButton()
        {
            HidePopupPanel();
        }
    
        private void ShowPopupPanel()
        {
            // 초기화
            panelObject.GetComponent<CanvasGroup>().DOFade(0, 0);
            panelObject.GetComponent<RectTransform>().DOAnchorPosY(1000f, 0);
    
            panelObject.GetComponent<CanvasGroup>().DOFade(1f, 0.2f);
            panelObject.GetComponent<RectTransform>().DOAnchorPosY(0f, 0.2f);
        }
    
        private void HidePopupPanel()
        {
            // 초기화
            panelObject.GetComponent<CanvasGroup>().DOFade(1f, 0);
            panelObject.GetComponent<RectTransform>().DOAnchorPosY(0f, 0);
    
            panelObject.GetComponent<CanvasGroup>().DOFade(0, 0.2f);
            panelObject.GetComponent<RectTransform>().DOAnchorPosY(1000f, 0.2f)
                .OnComplete(() => Destroy(gameObject));
        }
    }