싱글톤 패턴이란?

싱글톤 패턴은 클래스의 인스턴스를 하나만 생성하고,

그 인스턴스를 어디서든 접근할 수 있도록 전역적인 접근점을 제공하는 패턴입니다.

게임에서 GameManager, AudioManager, UIManager처럼 한 개만 존재해야 하는 객체를 만들 때 주로 사용됩니다.

 

구현

유일성 보장: 추가 인스턴스 생성을 막아야 하므로 Awake()에서 중복을 제한합니다.

전역 접근: public static 속성을 사용해 언제 어디서든 접근할 수 있게 합니다.

 

기본 형태

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
        }
        else
        {
            Destroy(gameObject); // 중복 방지
        }
    }

    public void GameStart()
    {
        Debug.Log("Game Started!");
    }
}

 

사용 예시

public class GameManagerUser : MonoBehaviour
{
    void Start()
    {
        GameManager.Instance.GameStart();
    }
}

 

확장형 싱글톤 패턴

싱글톤을 여러 매니저 클래스에 사용할 경우, 매번 같은 코드를 반복해야 하는 단점이 있습니다.
이를 해결하기 위해 제너릭(Generic) 을 활용한 확장형 싱글톤 패턴을 사용할 수 있습니다.

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;

    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = FindObjectOfType<T>();
                if (instance == null)
                {
                    GameObject obj = new GameObject();
                    instance = obj.AddComponent<T>();
                }
            }
            return instance;
        }
    }
    private void Awake()
    {
        if (instance == null)
        {
            instance = this as T;
            DontDestroyOnLoad(gameObject);
        }
        else if (instance != this)
        {
            Destroy(gameObject);
        }
    }
}
public class GameManager : Singleton<GameManager>
{
    public void GameStart()
    {
        Debug.Log("Game Started!");
    }
}

 

Singleton<T> 클래스를 한 번 만들어두면,

다른 매니저 클래스들은 상속만으로 싱글톤 기능을 바로 사용할 수 있어

코드 중복을 줄이고 유지보수가 쉬워집니다.

 

 

 

Unity Gaming Services(UGS) 

Unity는 예전부터 다양한 게임 개발 지원 서비스를 제공해왔습니다.
그러나 각 서비스가 제각각 분리되어 제공되면서, 개발자 입장에서는 일관된 사용 경험을 얻기 어려운 문제가 있었습니다.
이러한 문제를 해결하기 위해, Unity는 기존 서비스를 하나로 통합하고 기능을 대폭 강화한

Unity Gaming Services(UGS) 를 선보였습니다.

 

인증(Authentication)

Unity Gaming Services(UGS)는 다른 서비스를 활용하기 위한 기본 인증 시스템을 제공합니다.

비용 : 무제한 무료

제공 인증 종류

- 익명(Anonymous)

- 일반/소셜(구글플레이/애플게임센터/유니티 통합 등) 로그인

- 에디터 상에서는 소셜 로그인이 어려우므로 일반 로그인 구현 추천

 

[공통] PackageManager에서 Authentication, Cloud Save 등 필요한 기능 설치해야 작동


인증 주요 메서드

InitializeUnityServices(); 
UnityServices.InitializeAsync(); 
// 유니티 게이밍 서비스 초기화

[계정]
AuthenticationService.Instance.SignInWithUsernamePasswordAsync(string username, string password);
비밀번호 규칙: 특수문자, 대문자, 12자 이상
[구글]
PlayGamesPlatform.Instance.GetServerAuthCode();
AuthenticationService.SignInWithGooglePlayGamesAsync(authCode)

 

https://docs.unity.com/ugs/manual/authentication/manual/platform-signin-username-password

 

Username & Password

Username & Password is an identity provider which is provided natively by the Unity Authentication service and the SDK. It provides support for the following scenarios: The username used by this provider is completely separate from the player name. Passwor

docs.unity.com

 

using System;
using System.Threading.Tasks; // Task 사용을 위한 네임스페이스
using TMPro; 
using Unity.Services.Authentication; // 인증 관련 네임스페이스
using Unity.Services.Core; // 유니티 서비스 네임스페이스
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class AuthenticationManager : MonoBehaviour
{
    private static AuthenticationManager _instance;
    [SerializeField] private TMP_InputField idField;
    [SerializeField] private TMP_InputField passwordField;

    public static AuthenticationManager Instance
    {
        ... 중략
    }
    private void Awake()
    {
        ... 중략

        Init();
    }

    private async void Init()
    {
        await InitializeUnityServices();
    }

    private async Task InitializeUnityServices()
    {
        try
        {
            await UnityServices.InitializeAsync();
            Debug.Log("Unity Services initialized");
        }
        catch(Exception e)
        {
            Debug.LogException(e);
        }
    }

    private async void Login()
    {
        string id = idField.text.Trim();
        string password = passwordField.text.Trim();

        if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(password))
        {
            Debug.Log("아이디와 비밀번호를 입력하세요");
            return;
        }

        try
        {
            await InitializeUnityServices();
            await AuthenticationService.Instance.SignInWithUsernamePasswordAsync(id, password);
            SceneManager.LoadScene("StartScene");
        }
        catch (AuthenticationException ex)
        {
            Debug.LogException(ex);
        }
        catch (RequestFailedException ex)
        {
            Debug.LogException(ex);
        }
    }

    private async void SignUp()
    {
        string id = idField.text.Trim();
        string password = passwordField.text.Trim();

        if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(password))
        {
            Debug.Log("아이디와 비밀번호를 입력하세요");
            return;
        }
        try
        {
            await InitializeUnityServices();
            await AuthenticationService.Instance.SignInAnonymouslyAsync();
            await AuthenticationService.Instance.AddUsernamePasswordAsync(id, password);
        }
        catch (AuthenticationException ex) // 회원가입 관련
        {
            Debug.LogException(ex);
        }
        catch (RequestFailedException ex) // 서버 요청 관련
        {
            Debug.LogException(ex);
        }
    }
}

 

에디터 상에서 버튼 하나로 직접 SetID를 실행할 수 있게 ContextMenu를 활용

 

아이디를 만들기 전 기본적으로 로그인이 되어 있어야 하므로

 

익명 로그인 후 계정 등록 가능함

 

입력받은 id, password가 null 또는 비어있는지(empty)를 확인

 

 

 

만약 Authentication에 정보가 등록되어 있다면

 

Services에서 Unity Project ID를 Unlink 후 프로젝트를 다시 생성해야한다.

 

 

정상 등록되었다면 이렇게 뜬다.

PlayerManagement에 정상적으로 아이디 생성이 완료되었다면 아이디가 뜬다.

Unity UI는 사용자 인터페이스를 만들 수 있는 Canvas 기반 시스템
플레이어가 게임과 상호작용할 수 있도록 버튼, 텍스트, 이미지, 슬라이더 등을 배치하는 데 사용

핵심 구조: Canvas System

1.  Canvas

  • 모든 UI 요소의 부모
  • UI는 이 Canvas 안에 자식으로 위치해야 화면에 보여짐
  • 3가지 Render Mode:
    • Screen Space - Overlay (카메라 무시하고 UI를 화면에 고정)
    • Screen Space - Camera (카메라 기준으로 UI 렌더링)
    • World Space (UI를 3D 오브젝트처럼 씀)

1-1.  Canvas Scaler

UI Scale Mode 캔버스에서 UI 요소가 스케일되는 방법을 결정합니다.
Constant Pixel Size UI 요소가 화면 크기에 관계없이 동일한 픽셀 크기로 유지됩니다.
Scale With Screen Size 화면이 커질수록 UI 요소도 커집니다.
Constant Physical Size 화면 크기와 해상도에 관계없이 UI 요소가 동일한 물리적인 크기로 유지됩니다.

 

https://docs.unity3d.com/kr/2022.3/Manual/script-CanvasScaler.html

 

캔버스 스케일러 - Unity 매뉴얼

캔버스 스케일러(Canvas Scaler) 컴포넌트는 캔버스 내 UI 요소의 전체적인 스케일과 픽셀 밀도를 제어하는 데 사용됩니다. 스케일은 글꼴 크기와 이미지 경계 등 캔버스 아래의 모든 요소에 영향을

docs.unity3d.com

 

2.  RectTransform

  • UI 요소의 좌표, 크기, 정렬(anchor) 등을 설정
  • Transform 대신 RectTransform을 씀

3.  EventSystem

  • UI 입력 이벤트(클릭, 드래그 등)를 감지하는 시스템
  • 대부분 자동으로 생성되며, UI가 작동하려면 필수 요소

4.  앵커 (Anchor)

  • UI 요소(RectTransform) 기준에서의 부착 지점
  • 부모 객체(Canvas나 Panel 등) 안에서 어디에 기준을 잡을지를 정하는 것
  • 앵커를 정하면, 화면 크기가 변해도 UI 위치와 크기가 자동으로 조정됨

📌 예시: 버튼의 앵커를 "오른쪽 아래"에 설정하면, 해상도가 바뀌어도 버튼은 항상 오른쪽 아래에 붙어있다.

  • 앵커는 (0,0)~(1,1) 사이 값으로 나타냄
    • (0,0) → 부모 왼쪽 아래
    • (1,1) → 부모 오른쪽 위

5. 피벗 (Pivot)

  • 오브젝트 자체의 중심점
  • 이 중심을 기준으로 회전하거나 스케일 조정이 일어남
  • 피벗은 주로 회전축, 크기 조정 시 중심을 바꿀 때 중요

📌 예시: 피벗을 (0,0)으로 설정하면, 왼쪽 아래 모서리를 기준으로 회전하거나 크기가 변함. 기본은 (0.5, 0.5) — 즉 중앙.

 

정리

항목 설명 주로 쓰는 상황
앵커 (Anchor) 부모 공간 안에서 고정할 위치 UI 위치 유지, 반응형 화면
피벗 (Pivot) 오브젝트 자체의 중심점 회전, 스케일 조정 중심

🌍 월드좌표 (World Coordinate)

  • 기준: 세계 전체를 기준으로 하는 좌표계
  • 설명: 게임/시뮬레이션 공간 전체에서의 절대 위치를 나타냄
  • 예시: (0, 0, 0)은 보통 맵의 중심이며, 이 위치는 변하지 않음
  • 사용 상황:
    • 오브젝트가 맵의 어느 위치에 있는지 파악할 때
    • 충돌, 탐지 범위, 맵 이동 등 전역적인 계산에 사용

📦 로컬좌표 (Local Coordinate)

  • 기준: 해당 오브젝트의 중심(또는 부모 오브젝트) 기준
  • 설명: 오브젝트가 자신의 중심을 기준으로 한 상대적인 위치나 방향
  • 예시: (0, 0, 1)은 해당 오브젝트의 앞쪽을 의미 (오브젝트가 회전하면 이 방향도 함께 바뀜)
  • 사용 상황:
    • 로컬 축을 따라 이동/회전할 때 (transform.forward, transform.up, 등)
    • 부모 자식 구조에서 자식 오브젝트의 상대 위치를 설정할 때

🔁 변환 관계

  • Unity 기준:
    • transform.position → 월드좌표
    • transform.localPosition → 로컬좌표
    • 로컬좌표 → 월드좌표 변환: transform.TransformPoint(localPosition)
    • 월드좌표 → 로컬좌표 변환: transform.InverseTransformPoint(worldPosition)

🎯 요약 표

구분 기준 예시 사용 함수
월드좌표 세계 전체 (10, 0, -5) transform.position
로컬좌표 오브젝트 또는 부모 기준 (0, 1, 0) transform.localPosition

'Unity' 카테고리의 다른 글

UI(User Interface)  (0) 2025.04.26
리소스 데이터 관리(제네릭 T, TryGetValue, TryAdd)  (0) 2025.04.08
SceneManager.GetActiveScene, AddListener  (0) 2025.03.21
플레이어까지 거리(벡터) 구하기  (0) 2025.03.19
오브젝트 풀링  (0) 2025.03.08
private Dictionary<string, Object> assetPool = new Dictionary<string, Object>();

public T LoadAsset<T>(eAssetType eAssetType, string assetName) where T : Object // 첫번째 T 반환타입, 2번째 T 어떤 클래스로 받을건지
    {
        string path = $"{eAssetType}/{assetName}"; 
        // 로드후 assetPool에 넣음, 로드할때마다 로드할 에셋이 assetPool에 있는지 확인
        // 있으면 assetPool꺼내서 줌, 없으면 다시 로드 후 assetPool에 넣음
        // 키 값이 있는지 없는지 확인
        if (assetPool.TryGetValue(path, out Object obj))
        {
            Debug.Log($"{path}가 이미 있습니다.");
            if(obj != null)
                return (T)obj;
        }
        Debug.Log($"{path} 새로 로드");
        obj = Resources.Load<T>(path);
        assetPool.TryAdd(path, obj);
        return (T)obj;
    }

 

리소스 데이터 로드 시 중복 생성 방지

 

다양한 데이터 자료형에 대응하기 위해 제너릭 T를 사용하며 Object 제약 조건을 설정합니다.

TryGetValue

딕셔너리에서 지정된 키, 연결된 값을 검색하는데 사용되며

키가 Dictionary에 있는지 여부를 나타내는 Bool 값을 반환(True, False)하고

키와 연결된 값이 있으면 출력합니다.

TryAdd

Dictionary에 추가할 때

Add와 다르게 해당 키가 이미 존재하는지 검사하고,

존재하지 않는 경우에만 항목을 추가한다.

 

'Unity' 카테고리의 다른 글

UI(User Interface)  (0) 2025.04.26
월드좌표, 로컬좌표  (0) 2025.04.24
SceneManager.GetActiveScene, AddListener  (0) 2025.03.21
플레이어까지 거리(벡터) 구하기  (0) 2025.03.19
오브젝트 풀링  (0) 2025.03.08

+ Recent posts