Unity

[Unity] Unity 2D 게임 개발 정리 (4)

sunlight-dby 2025. 6. 4. 01:56

 

이 글은 해당 유튜브를 보고 진행한 것에 대해 공부한 것을 정리한 글입니다.

 


 

 UI 요소 : Grid Layout Group 컴포넌트

Grid Layout Group은 Unity UI에서 자식 오브젝트를 격자 형태로 자동 정렬해주는 컴포넌트입니다.

버튼이나 이미지 등의 UI 요소를 행과 열로 나열할 때 유용하게 사용됩니다.


Enum.GetValues

C#에서 제공하는 Enum.GetValues 메서드는 주어진 열거형의 모든 값을 배열로 반환합니다.

foreach (MyEnum value in Enum.GetValues(typeof(MyEnum))
{
    Dbug.Log(value);
}

PlayerPrefs

PlayerPrefs는 전의 글에서도 정리한 내용으로, Unity에서 제공하는 간단한 로컬 저장소 시스템입니다.

주로 정수형, 문자열, 부울 값을 저장할 수 있으며, 게임 설정, 점수, 튜토리얼 진행 여부 등에 사용됩니다.

 

[Unity] MonoBehaviour, Prefab, Rigidbody, PlayerPrefs

이 글은 '레트로의 유니티 게임 프로그래밍 에센스' 책을 보고 진행한 것에 대해 공부하여 정리한 글입니다. MonoBehaviourMonoBehaviour는 유니티 게임 엔진의 핵심 기능들과 상호작용할 수 있게 해주

sunlight-dby.tistory.com

 

해당 프로젝트에서는 업적 달성에 관한 것을 PlayerPrefs를 활용하여 저장하였습니다.

AchiveManager.cs를 만들어, 캐릭터에 대한 해금을 PlayerPrefs를 통해 활용하였습니다.

 

PlayerPrefs에 MyData 키를 만들고, 해당 키가 1이면 초기화가 된 상태, MyData 키가 존재하지 않으면 초기화가 되지 않은 상태입니다.

 

Awake() 함수에서 MyData라는 키가 존재하는지를 확인한 후, 없다면 초기화를 Init() 함수를 통해 진행해줍니다.

해당 프로젝트에서 업적은 2개 뿐이라, PlayerPrefs에 저장되는 키도 MyData를 제외하고 2개 뿐입니다.

하지만 업적은 다양하고 그 수도 정말 많을 수 있습니다.

이를 쉽게 초기화하기 위해서는 foreach를 통해 Achive[ ]로 선언한 achives를 순회하며, PlayerPrefs에 SetInt를 해주면  됩니다.

Achive[] achives;

// ...

void Awake()
{
    achives = (Achive[])Enum.GetValues(typeof(Achive));
    wait = new WaitForSecondsRealtime(5);

    if (!PlayerPrefs.HasKey("MyData"))
    {
        Init();
    }
}

void Init()
{
    PlayerPrefs.SetInt("MyData", 1);

    foreach (Achive achive in achives)
    {
        PlayerPrefs.SetInt(achive.ToString(), 0);
    }
}

WaitForSecondsRealtime

WaitForSecondsRealtime은 게임의 시간 스케일(Time.timeScale)에 영향을 받지 않는 대기 시간입니다.

일시정지 중에도 타이머나 효과를 유지하고 싶을 때 사용됩니다.

 

해당 프로젝트에서는 업적이 달성될 때, UI를 코루틴을 통해 활성화 / 비활성화를 하는데, 시간이 정지된 상태에서도 업적은 달성될 수 있으므로 WaitForSecondsRealtime을 활용하였습니다.


Unity 오디오 시스템


AudioClip

AudioClip은 Unity에서 사용하는 오디오 파일 에셋의 타입입니다. 배경음악(BGM), 효과음(SFX) 등 다양한 오디오를 저장하고 재생할 수 있습니다.

  • 지원 포맷 : WAV, MP3, OGG 등

AudioSource

AudioSource는 AudioClip을 재생할 수 있게 해주는 컴포넌트입니다. 게임 오브젝트에 붙여 재생 위치와 볼륨, 루프 여부 등을 제어할 수 있습니다.

  • 주요 속성 : clip, volume, loop, playOnAwake

AudioListener

AudioListener는 Unity에서 마이크 역할을 하는 컴포넌트로, 카메라나 플레이어 캐릭터에 주로 부착됩니다.

기본적으로는 메인 카메라에 부착되어 있으며, 3D 오디오 공간에서는 소리의 위치와 방향성을 결정합니다.


Audio High Pass Filter

Audio High Pass Filter 컴포넌트는 오디오 신호 중 저주파 영역을 차단하고, 고주파만 통과시키는 필터입니다. AudioListener 또는 Audio Source에 부착하여 특정 효과를 낼 수 있습니다.


다량의 효과음을 낼 수 있도록 채널 개수 이용

해당 프로젝트에서는 언데드들이 죽을 때 Dead SFX가 재생됩니다. 많은 언데드들이 죽을 때, 동시 재생 수가 제한될 수 있으므로 다수의 효과음을 동시에 재생하기 위해 AudioSource를 여러 개를 배열로 관리합니다.

 

int형 정수 channels를 public으로 선언한 뒤, AudioSource[ ] 타입의 sfxPlayers를 선언합니다.

효과음 플레이어를 초기화할 때, sfxPlayers를 new AudioSource[channels]를 통해 초기화합니다.

이후, for문으로 0부터 sfxPlayers.Length까지 순회하며 효과음 플레이어를 초기화해줍니다.

public int channels;
AudioSource[] sfxPlayers;

// ...

void Init()
{
    // ...
    
    // 효과음 플레이어 초기화
    GameObject sfxObject = new GameObject("SfxPlayer");
    sfxObject.transform.parent = transform;
    sfxPlayers = new AudioSource[channels];

    for (int index = 0; index < sfxPlayers.Length; index++)
    {
        sfxPlayers[index] = sfxObject.AddComponent<AudioSource>();
        sfxPlayers[index].playOnAwake = false;
        sfxPlayers[index].bypassListenerEffects = true;
        sfxPlayers[index].volume = sfxVolume;
    }
}

Prefab 연결 끊기

 

Unity에서 Prefab을 오브젝트로 사용하고, Prefab과의 연결을 끊기 위해서는 Unpack Compltely 기능을 사용해야 합니다.

 

Prefab의 마우스 우클릭에서 Prefab > Unpack Completely를 클릭하면 됩니다.

 

이때, 해당 오브젝트와 Prefab 간의 연결이 완전히 끊어지며, 더 이상 원본 Prefab의 영향을 받지 않게 됩니다.

 

 

 

 

 

 

 

 

 

 

 


Level Up System 변경

해당 프로젝트에서 레벨 업을 할 때, 아이템을 선택하여 player의 스탯을 업그레이드 하거나, 체력을 회복할 수 있습니다.

Level up에 관한 로직은 LevelUp.cs의 Next() 함수에서 실행됩니다.


기존 Level Up System

기존의 방식은 모든 아이템을 비활성화하고, 그 중에서 랜덤 3개 아이템만 활성화하는 방식이였습니다. 만약 만렙 아이템일 경우에는 소비 아이템인 회복 아이템으로 대체하였습니다.

 

다만, 이 방식은 랜덤 3개의 아이템 중 만렙 아이템이 껴있을 때, 만렙이 되지 않은 다른 아이템으로 대체되는 것이 아니라 무조건 소비 아이템으로 변경되기 때문에, 선택 창이 2개만 활성화되어 있을 경우가 존재하는 방식이였습니다.

 

저는 이 방식보다, 랜덤 3개의 아이템 중 만렙 아이템이 있을  때, 선택되지 않은 아이템 중 만렙이 되지 않은 아이템이 있다면 그 아이템으로 대체되게끔 하여, 이런 경우에 선택 창이 3개의 아이템 모두가 활성화 될 수 있게 구현하였습니다.

void Next()
{
    // 모든 아이템 비활성화
    foreach (Item item in items)
    {
        item.gameObject.SetActive(false);
    }

    // 그 중에서 랜덤 3개 아이템만 활성화
    int[] random = new int[3];
    while (true)
    {
        random[0] = Random.Range(0, items.Length);
        random[1] = Random.Range(0, items.Length);
        random[2] = Random.Range(0, items.Length);

        if (random[0] != random[1] && random[1] != random[2] && random[0] != random[2])
        {
            break;
        }
        
        for (int index = 0; index < random.Length; index++)
        {
            Item randItem = items[random[index]];
            
            // 만렙 아이템의 경우는 소비아이템으로 대체
            if (randItem.level == randItem.data.damages.Length)
            {
                items[4].gameObject.SetActive(true);
            }
            else
            {
                randItem.gameObject.SetActive(true);
            }
        }
    }
}

수정된 Level Up System

수정된 방식은 모든 아이템을 비활성화하고, 애초에 레벨 업이 가능한(만렙이 되지 않은) 아이템만 리스트에 추가합니다.

그 후 리스트 안의 요소를 모두 섞어주어 랜덤성을 추가합니다.

 

그 뒤 새로운 리스트를 다시 생성하고,

기존 리스트의 요소가 3개 이상이라면, 기존 리스트에서 3개의 랜덤 요소를 가져와 새로운 리스트에 포함합니다.

만약 3개 이상은 아니지만, 0보다 크다면 소비 아이템을 명시적으로 추가해줍니다.

위의 모든 조건이 아니라면, 소비 아이템만 추가해줍니다.

 

이후 새로운 리스트를 foreach문으로 순회하면 활성화하여 줍니다.

void SuffleList(List<int> list)
{
    for (int index = 0; index < list.Count; index++)
    {
        int randIndex = Random.Range(index, list.Count);
        int temp = list[index];
        list[index] = list[randIndex];
        list[randIndex] = temp;
    }
}
    
void Next()
{
    // 모든 아이템 비활성화
    foreach (Item item in items)
    {
        item.gameObject.SetActive(false);
    }

    List<int> availableList = new List<int>();

    // 0 ~ 3번 아이템 중 레벨업 가능한 것만 리스트에 추가가
    for (int index = 0; index < 5; index++)
    {
        if (items[index].level < items[index].data.damages.Length)
        {
            availableList.Add(index);
        }
    }

    // 리스트 셔플
    SuffleList(availableList);

    List<int> result = new List<int>();

    if (availableList.Count >= 3)
    {
        result.AddRange(availableList.GetRange(0, 3));
        while (result.Count < 3)
        {
            int rand = availableList[Random.Range(0, availableList.Count)];
            
            if (!result.Contains(rand))
            {
                result.Add(rand);
            }
        }
    }
    else if (availableList.Count > 0)
    {
        result.AddRange(availableList);
        if (!result.Contains(4))
        {
            result.Add(4);
        }
    }
    else
    {
        result.Add(4);
    }

    // 결과 아이템 활성화
    foreach (int index in result)
    {
        items[index].gameObject.SetActive(true);
    }
}

 

 

 

 

 

Git

 

GitHub - Dobby-yhs/Undead-Survivor

Contribute to Dobby-yhs/Undead-Survivor development by creating an account on GitHub.

github.com