아이템의 갯수는 PlayerPrefs를 사용하면 되지만 씬에 배치된 오브젝트는 저장할 수 없습니다.  예제에서는 JSON이라는 데이터 형식으로 배치된 데이터를 저장합니다.

JSON이란 텍스트 형식으로 쓰인 데이터 형식이빈다. 단순한 구조로 앱이나 인터넷에서 데이터 저장, 전달에 널리 사용됩니다.

JSON의 구체적인예

{
"hp":100,
"name":"유니",
"speed":10.3,
"isMoving":true
}

다룰수 있는 데이터형은 ,숫자,문자열,논리형, null, 배열 등입니다. {}로 감싼 데이터 자체를 값으로 가질 수도 있습니다.

유니티는 JsonUtility라는 클래스가 있어 쉽게JSON 형식을 사용할 수 있습니다.

배치 데이터를 저장하려면 JsonUtility 클래스를 사용 JSON형식(텍스트)으로 변환하고 PlayerPrefs 클래스를 활용 텍스트로 저장합니다, JSON자체는 텍스트이므로 PlayerPrefs클래스의 SetString메서드로 읽고 쓸수있습니다.

예제에서는 열린문, 열린상자, 획득한 아이템, 쓰러트린적 의 정보를 저장합시다. 저장하는 정보는 게임오브젝트를 식별할 수 있는 번호와 종류입니다. 번호는 9장까지 만들었던 Door, ItemBox, ItemData, EnemyController클래스의 arrnageID변수를 사용합니다. 종류는 각 게임오브젝트의 태그를 이용합니다.

 

배치된 정보를 기록하는 스크립트 만들기

JSON을 기록하려면 먼저 저장할 JSON과 같은 구조를 가진 클래스를 정의해야 합니다. SaveData라는 이름의 스크립트를 RoomManager폴더에 만들고 다음과 같이 작성합니다.

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class SaveData
{
    public int arrangeId = 0;       //배치 ID
    public string objTag = "";      //배치된 오브젝트의 태그
}

[System.Serializable]
public class SaveDataList
{
    public SaveData[] saveDatas;    //SaveData 배열
}

[System.Serializable]는 이 클래스는 저장할 대상입니다를 의미합니다. JSON으로 만들기 위해 필요한 것이라고 합시다.

SaveData 클래스는 배치된 오브젝트 하나를 나타내고. SaveDataList는 이들을 여러개 배열로 저장하는 데이터입니다.

두 클래스는 JSON형식(텍스트) 데이터를 클래스로 변환했을 때의 형을 정의합니다.

 

다음은 JSON데이터를 읽거나 써서 배치된 데이터를 관리하는 스크립트를 만들어보겠습니다.

 SaveDataManager  스크립트를 RoomManager폴더에 만들고 RoomManager의 프리팹에 어태치합니다. 

 

 

변수

SaveDataList클래스의 객체를 public static으로 선언합니다.

public static SaveDataList arrangeDataList;    //베치 데이터

Start메서드

 

 void Start() {
    //SaveDataList 초기화 
    arrangeDataList = new SaveDataList();
    arrangeDataList.saveDatas = new SaveData[] { };
    //씬 이름 불러오기
    string stageName = PlayerPrefs.GetString("LastScene");
    //씬 이름을 키로하여 저장 데이터 읽어오기
    string data = PlayerPrefs.GetString(stageName);
    if (data != "")   {
        //--- 저장 된 데이터가 존재할 경우  ---
        //JSON에서 SaveDataList로 변환하기
        arrangeDataList = JsonUtility.FromJson<SaveDataList>(data);
        for (int i = 0; i < arrangeDataList.saveDatas.Length; i++)  {
            SaveData savedata = arrangeDataList.saveDatas[i]; //배열에서 가져오기
            //태그로 게임 오브젝트 찾기
            string objTag = savedata.objTag;
            GameObject[] objects = GameObject.FindGameObjectsWithTag(objTag);
            for (int ii = 0; ii < objects.Length; ii++) {
                GameObject obj = objects[ii]; //배열에서 GameObject 가져오기
                //GameObject의 태그 확인하기
                if (objTag == "Door") {      //문 
                    Door door = obj.GetComponent<Door>();
                    if (door.arrangeId == savedata.arrangeId) {
                        Destroy(obj);  //arrangeId가 같으면 제거
                    }
                } else if (objTag == "ItemBox") {  //보물 상자
                    ItemBox box = obj.GetComponent<ItemBox>();
                    if (box.arrangeId == savedata.arrangeId) {
                        box.isClosed = false;   //arrangeIdd가 같으면 열기
                        box.GetComponent<SpriteRenderer>().sprite = box.openImage;
                    }
                }  else if (objTag == "Item") {     //아이템
                    ItemData item = obj.GetComponent<ItemData>();
                    if (item.arrangeId == savedata.arrangeId) {
                        Destroy(obj);   //arrangeId가 같으면 제거
                    }
                }
                else if (objTag == "Enemy") {      //적
                    EnemyController enemy = obj.GetComponent<EnemyController>();
                    if (enemy.arrangeId == savedata.arrangeId)  {
                        Destroy(obj);   //arrangeId가 같으면 제거
                    }
                }
            }
        }
    }
}

SetArrangeId()

public static void SetArrangeId(int arrangeId, string objTag)
{
    if (arrangeId == 0 || objTag == "")
    {
        //기록하지 않음
        return;
    }
    //추가 하기 위해 하나 많이 SaveData 배열 만들기
    SaveData[] newSavedatas = new SaveData[arrangeDataList.saveDatas.Length + 1];
    //데이터 복사
    for (int i = 0; i < arrangeDataList.saveDatas.Length; i++)
    {
        newSavedatas[i] = arrangeDataList.saveDatas[i];
    }
    //SaveData 만들기
    SaveData savedata = new SaveData();
    savedata.arrangeId = arrangeId; //Id를 기록
    savedata.objTag = objTag;       //태그 기록
    //SaveData 추가
    newSavedatas[arrangeDataList.saveDatas.Length] = savedata;
    arrangeDataList.saveDatas = newSavedatas;
}

SaveArrangeData()

public static void SaveArrangeData(string stageName)
{
    if (arrangeDataList.saveDatas != null && stageName != "")
    {
        //SaveDataList를 JSON 데이터로 변환
        string saveJson = JsonUtility.ToJson(arrangeDataList);
        //씬 이름을 키로하여 저장
        PlayerPrefs.SetString(stageName, saveJson);
    }
}

열린 보물 상자 저장

ItemBox.cs
0.00MB

보물 상자가 열리고아이템이 생성됐을때 호출되는 ItemBox 클래스의 OnCollisionEnter2D 메서드에 보물 상자의 열린 상태를 저장하는 처리를 추가합니다. 인수로 자기 자신(보물 상자)의 arrangeID와 태그를 전달합니다.

ItemBox스크립트를 열어 다음을 추가합니다.

public class ItemBox : MonoBehaviour
{
~ 생략
    //접촉 (물리)
    private void OnCollisionEnter2D(Collision2D collision)   {
        if (isClosed && collision.gameObject.tag == "Player")  {
            //상자가 닫혀 있는 상태에서 플레이어와 접촛
                //프리펩으로 아이템 만들기
            //배치 Id 기록
            SaveDataManager.SetArrangeId(arrangeId, gameObject.tag);
        }
    }
}

획득한 아이템 저장

ItemData.cs
0.00MB

아이템을 얻었을때 호출되는 ItemData클래스의 OnCollisionEnter2D()에 획득한 아이템을 저장하는 처리를 추가합니다. 인수로 arrangeID와 태그를 전달합니다.

public class ItemData : MonoBehaviour{
~생략
    //접촉 (물리)
    private void OnTriggerEnter2D(Collider2D collision)  {
        if (collision.gameObject.tag == "Player")     {
            if (type == ItemType.key)  {  //열쇠  }
            else if (type == ItemType.arrow)     {  //화살   }
            else if (type == ItemType.life)   {     }
            //++++ 아이템 획득 연출 ++++
            //충돌 판정 비활성
            //아이템의 Rigidbody2D가져오기
            //중력 젹용
            //위로 튀어오르는 연출
            //0.5초 뒤에 제거
            //배치 Id 기록
            SaveDataManager.SetArrangeId(arrangeId, gameObject.tag);
        }
    }
}

열린 문 저장

Door.cs
0.00MB

문이 열렸을때 호출되는 Door클래스의 OnCollisionEnter2D()에 문의 열린 상태를 저장하는 처리를 추가합니다. 인수로 arrangeID와 태그를 전달합니다.

public class Door : MonoBehaviour
{
~생략
    void OnCollisionEnter2D(Collision2D collision)  {
        if (collision.gameObject.tag == "Player")  {
            if (ItemKeeper.hasKeys > 0) {  //열쇠를 가지고있으면
                //열쇠를 하나 감소
                //문 열기 (제거하기)
                //배치 Id 기록
                SaveDataManager.SetArrangeId(arrangeId, gameObject.tag);
            }
        }
    }
}

쓰러트린 적 저장

EnemyController.cs
0.00MB

EmemyController의 OnCollisionEnter2D()에 쓰러트린 적을 저장하는 처리를 추가합니다. 인수로 arrangeID와 태그를 전달합니다.

private void OnCollisionEnter2D(Collision2D collision){
    if (collision.gameObject.tag == "Arrow") {
       
        hp--;  //데미지
        if (hp <= 0)  {    //사망!
           ~사망처리

            //배치 Id 기록
            SaveDataManager.SetArrangeId(arrangeId, gameObject.tag);
        }
    }
}

스테이지를 이동할 때 데이터 저장

RoomManager.cs
0.00MB

스테이지 이동은 RoomManager클래스의 ChangeScene메서드에서 실행합니다. 

public static void ChangeScene(string scnename, int doornum)
{
    doorNumber = doornum;   //문 번호를 static 변수에 저장

    string nowScene = PlayerPrefs.GetString("LastScene");
    if (nowScene != "")
    {
       SaveDataManager.SaveArrangeData(nowScene);      // 배치 데이터 저장
    }
    PlayerPrefs.SetString("LastScene", scnename);   // 씬 이름 저장
    PlayerPrefs.SetInt("LastDoor", doornum);        // 문 번호 저장
    ItemKeeper.SaveItem();                          // 아이템 저장

    SceneManager.LoadScene(scnename);   // 씬 이동
}

배치 데이터를 저장하기 위해 씬을 변경하기 전

SaveDataManager클래스의 SaveArrangeData메서드를 호출해 배치 데이터를 저장합니다.

 SaveArrangeData메서드에는 PlayerPrefs클래스의 GetString 메서드에 "LastScene"을 키로 전달하면 얻을 수 있는 "현재씬이름"을 사용합니다. 이름이 null이 아니면 SaveDataManager클래스의 SaveArrangeData메서드를 호출해 데이터를 저장합니다.

다음으로 PlayerPrefs클래스의 SetString메서드를 사용해 "LastScene"키로 앞으로 이동할 씬 이름을 저장합니다.

문번호는 PlayerPrefs클래스의 SetStringInt메서드를 사용해 "LastDoor"키로 문 번호를 저장합니다.

씬을 이동할 때 아이템을 저장하는 ItemKeeper클래스의 SaveItem메서드를 호출하면 그 방에서 획득한 아이템을 저장합니다.

 

게임이어하기 기능 만들기

TitleManager.cs
0.00MB

타이틀 화면의 [CONTINUE]버튼을 누르면 게임을 중간부터 시작할 수 있도록 합니다.

[GAME START]버튼을 누르면 이전 기록을 초기화 하는 처리도 추가합니다. TitleManager 스크립트를 다음과 같이 수정합니다.

변수

public GameObject startButton;      //스타트 버튼
public GameObject continueButton;   //이어하기 버튼
public string firstSceneName;       //게임 시작 씬 이름

Start()

PlayerPrefs "LastScene"키로 가져온 이름이 없다면 [CONTINUE]버튼을 비활성화 합니다. 마지막씬이 있다면 활성화합니다.

void Start()
{
    string sceneName = PlayerPrefs.GetString("LastScene");      //저장 된 씬
    if (sceneName == "")
    {
        continueButton.GetComponent<Button>().interactable = false; //비활성
    }
    else
    {
        continueButton.GetComponent<Button>().interactable = true; //활성
    }
    //타이틀 BGM 재생
    //SoundManager.soundManager.PlayBgm(BGMType.Title);
}

StartButtonClicked()

public void StartButtonClicked()
{
    //저장 데이터를 지움
    PlayerPrefs.DeleteAll();
    //HP 초기화 
    PlayerPrefs.SetInt("PlayerHP", 3);
    //저장된 스테이지 정보를 지움
    PlayerPrefs.SetString("LastScene", firstSceneName); //씬 이름 초기화
    RoomManager.doorNumber = 0;

    SceneManager.LoadScene(firstSceneName);
}

ContinueButtonClicked()

public void ContinueButtonClicked()
{
    string sceneName = PlayerPrefs.GetString("LastScene");      //저장된 씬
    RoomManager.doorNumber = PlayerPrefs.GetInt("LastDoor");    //문 번호
    SceneManager.LoadScene(sceneName);
}

게임 재시도 처리

UIManager.cs
0.00MB

UIManager 클래스의 Retry메서드도 UIManager스크립트에서 다음과 같이 수정합니다. Player.Prefs.SetInt로 저장된 플레이어의 HP를 3으로 되돌립니다.

 //재시도
public void Retry()
{
    //HP 되돌리기
    PlayerPrefs.SetInt("PlayerHP", 3);

    //게임 중으로 설정
    SceneManager.LoadScene(retrySceneName);   //씬 이동
}

 

저장할 수 있는 배치 데이터 만들기

이제 저장할 수 있는 배치 데이터를 만들어 봅시다. 배치할 수 있는 게임 오브젝트는 다음표와 같습니다. 각각 다른 태그가 설정됐으며. 각오브젝트에는 arrangeID라는 Int형 변수를 가지는 스크립트가 어태치됐습니다.

게임오브젝트 태그 arrangeID를 갖는 스크립트
Door Door
아이템 Item ItemData
보물상자 ItemBox ItemBox

예를 들어 보물 상자가 두개 배치되었으면 Itembox의 arrangeID에 각각 1이상이고 서로 중복되지 않는 숫자를 설정합니다. 이제 이상태에서 타이틀 화면은 [GAME START]버튼을 클릭해서게임을 시작합니다. 게임중 획득한 아이템이나 열린 문의 정보가 각 씬을 옮겨 다닐 때마다 저장되고 재현됩니다. 타이틀화면으로 돌아가 [CONTINUE] 버튼으로 게임을 시작하면 아이템의 수나 아이템의 배치 상태등 방의 모습이 재현된 상태로 게임이 시작됩니다.

 

게임 실행하기 

실행시 SoundManager관련 에러가 나면  주석처리 해줍니다. SoundManager는 맨 마지막에 만들겁니다.

이번에는 게임 중간부터 이어서 할수 있는 기능을 추가합니다. 지금까지는 씬이 변경된 후 변수의 값을 유지하기 위해 static 변수를 활용했습니다. static도 게임이 종료하면 없어지므로 데이터를 저장해서 다시 시작할때 불러서 사용하겠습니다.

저장할 데이터는 소지한 아이템수, 플레이어의 HP, 현재의 씬 이름, 배치된 아이템, 들어간 문의 번호, 열린 문, 쓰러뜨린 적입니다. 게임 데이터는 씬을 이동할 때 자동으로 저장되도록 합니다.

 

PlayerPrefs로 데이터 저장하기 및 불러오기

Unity가 제공하는 PlayerPrefs클래스를 사용하여 게임데이터를 저장하고 불러올수 있습니다.

int : PlayerPrefs.SetInt(키, 수치), PlayerPrefs.GetInt(키)

float: PlayerPrefs.SetFloat(키, 수치), PlayerPrefs.GetFloat(키)

string: PlayerPrefs.SetString(키, 수치), PlayerPrefs.GetString(키)

아이템 수 저장하기 및 불러오기 만들기

Item수는 itemKeeper클래스에서 관리하고 있어 itemKeeper스크립트에 다음 기능을 추가합니다.

ItemKeeper.cs
0.00MB

변수

키의 개수와 화살의 개수를 저장하는 변수입니다. 이건 이미 있었던 변수입니다.

public static int hasKeys = 0;          //열쇠 수
public static int hasArrows = 0;        //화살 소지수

Start()

PlayerPrefs 클래스를 이용 Keys와 Arrows의 저장된 값을 불러옵니다.

void Start()
{
    //아이템 불러오기
    hasKeys = PlayerPrefs.GetInt("Keys");
    hasArrows = PlayerPrefs.GetInt("Arrows");
}

SaveItem()

PlayerPrefs 클래스를 이용 Keys와 Arrows의 값을 저장합니다.

public static void SaveItem()
{
    PlayerPrefs.SetInt("Keys", hasKeys);
    PlayerPrefs.SetInt("Arrows", hasArrows);
}

 

플레이어 HP 저장하기및 불러오기 기능 만들기

HP는 PlayerController.cs에서 관리하고 HP를 저장하고 불러오는 기능을 추가합니다.

PlayerController.cs
0.01MB

시작시 PlayerPrefs클래스를 이용 HP를 읽어 저장합니다.

void Start()
{
	~생략
    hp = PlayerPrefs.GetInt("PlayerHP");
}

GetDamage()

피해를 입고 HP값이 변경되면 PlayerPrefs.SetInt메서드로 값을 저장합니다.

void GetDamage(GameObject enemy)
{
    if (gameState == "playing")
    {
        hp--;   //HP감소
        //HP 갱신
        PlayerPrefs.SetInt("PlayerHP", hp);
	~ 생략
    }
}

ItemData.cs의 OnTriggerEnter2D() 메써드

ItemData.cs
0.00MB

생명 아이템을 획득하고 hp가 변경되었을때 PlayerPrefs를 이용 hp를 저장합니다.

private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.tag == "Player")
    {
        if (type == ItemType.key)  {  }
        else if (type == ItemType.arrow) {   }
        else if (type == ItemType.life)   {
            //생명
            if (PlayerController.hp < 3)  {
                //HP가 3이하면 추가
                PlayerController.hp++;
                //HP 갱신
                PlayerPrefs.SetInt("PlayerHP", PlayerController.hp);
            }
        }
        //++++ 아이템 획득 연출 ++++
        //충돌 판정 비활성
        //아이템의 Rigidbody2D가져오기
        //중력 젹용
        //위로 튀어오르는 연출
        //0.5초 뒤에 제거
        //배치 Id 기록
    }
}

JSON을 사용해 씬의 상태 기록하기

아이템의 갯수는 PlayerPrefs를 사용하면 되지만 씬에 배치된 오브젝트는 저장할 수 없습니다.  예제에서는 JSON이라는 데이터 형식으로 배치된 데이터를 저장합니다.

JSON이란 텍스트 형식으로 쓰인 데이터 형식이빈다. 단순한 구조로 앱이나 인터넷에서 데이터 저장, 전달에 널리 사용됩니다.

JSON의 구체적인예

{
"hp":100,
"name":"유니",
"speed":10.3,
"isMoving":true
}

다룰수 있는 데이터형은 ,숫자,문자열,논리형, null, 배열 등입니다. {}로 감싼 데이터 자체를 값으로 가질 수도 있습니다.

유니티는 JsonUtility라는 클래스가 있어 쉽게JSON 형식을 사용할 수 있습니다.

배치 데이터를 저장하려면 JsonUtility 클래스를 사용 JSON형식(텍스트)으로 변환하고 PlayerPrefs 클래스를 활용 텍스트로 저장합니다, JSON자체는 텍스트이므로 PlayerPrefs클래스의 SetString메서드로 읽고 쓸수있습니다.

예제에서는 열린문, 열린상자, 획득한 아이템, 쓰러트린적 의 정보를 저장합시다. 저장하는 정보는 게임오브젝트를 식별할 수 있는 번호와 종류입니다. 번호는 9장까지 만들었던 Door, ItemBox, ItemData, EnemyController클래스의 arrnageID변수를 사용합니다. 종류는 각 게임오브젝트의 태그를 이용합니다.

 

배치된 정보를 기록하는 스크립트 만들기

JSON을 기록하려면 먼저 저장할 JSON과 같은 구조를 가진 클래스를 정의해야 합니다. SaveData라는 이름의 스크립트를 RoomManager폴더에 만들고 다음과 같이 작성합니다.

SaveData.cs
0.00MB

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class SaveData
{
    public int arrangeId = 0;       //배치 ID
    public string objTag = "";      //배치된 오브젝트의 태그
}

[System.Serializable]
public class SaveDataList
{
    public SaveData[] saveDatas;    //SaveData 배열
}

[System.Serializable]는 이 클래스는 저장할 대상입니다를 의미합니다. JSON으로 만들기 위해 필요한 것이라고 합시다.

SaveData 클래스는 배치된 오브젝트 하나를 나타내고. SaveDataList는 이들을 여러개 배열로 저장하는 데이터입니다.

두 클래스는 JSON형식(텍스트) 데이터를 클래스로 변환했을 때의 형을 정의합니다.

 

다음은 JSON데이터를 읽거나 써서 배치된 데이터를 관리하는 스크립트를 만들어보겠습니다.

 SaveDataManager  스크립트를 RoomManager폴더에 만들고 RoomManager의 프리팹에 어태치합니다. 

SaveDataManager.cs
0.00MB

변수

SaveDataList클래스의 객체를 public static으로 선언합니다.

public static SaveDataList arrangeDataList;    //베치 데이터

Start메서드

 

 void Start() {
    //SaveDataList 초기화 
    arrangeDataList = new SaveDataList();
    arrangeDataList.saveDatas = new SaveData[] { };
    //씬 이름 불러오기
    string stageName = PlayerPrefs.GetString("LastScene");
    //씬 이름을 키로하여 저장 데이터 읽어오기
    string data = PlayerPrefs.GetString(stageName);
    if (data != "")   {
        //--- 저장 된 데이터가 존재할 경우  ---
        //JSON에서 SaveDataList로 변환하기
        arrangeDataList = JsonUtility.FromJson<SaveDataList>(data);
        for (int i = 0; i < arrangeDataList.saveDatas.Length; i++)  {
            SaveData savedata = arrangeDataList.saveDatas[i]; //배열에서 가져오기
            //태그로 게임 오브젝트 찾기
            string objTag = savedata.objTag;
            GameObject[] objects = GameObject.FindGameObjectsWithTag(objTag);
            for (int ii = 0; ii < objects.Length; ii++) {
                GameObject obj = objects[ii]; //배열에서 GameObject 가져오기
                //GameObject의 태그 확인하기
                if (objTag == "Door") {      //문 
                    Door door = obj.GetComponent<Door>();
                    if (door.arrangeId == savedata.arrangeId) {
                        Destroy(obj);  //arrangeId가 같으면 제거
                    }
                } else if (objTag == "ItemBox") {  //보물 상자
                    ItemBox box = obj.GetComponent<ItemBox>();
                    if (box.arrangeId == savedata.arrangeId) {
                        box.isClosed = false;   //arrangeIdd가 같으면 열기
                        box.GetComponent<SpriteRenderer>().sprite = box.openImage;
                    }
                }  else if (objTag == "Item") {     //아이템
                    ItemData item = obj.GetComponent<ItemData>();
                    if (item.arrangeId == savedata.arrangeId) {
                        Destroy(obj);   //arrangeId가 같으면 제거
                    }
                }
                else if (objTag == "Enemy") {      //적
                    EnemyController enemy = obj.GetComponent<EnemyController>();
                    if (enemy.arrangeId == savedata.arrangeId)  {
                        Destroy(obj);   //arrangeId가 같으면 제거
                    }
                }
            }
        }
    }
}

10.1 타이틀 화면 추가하기

타이틀 화면을 만들고 이어하기 기능도 구현합니다. 타이틀화면에 [Game Start] [Continue] 버튼을 배치하고 게임을 중간에 이어할수 있는 Save기능도 구현합니다.

 

타이틀 화면 만들기

먼저 타이틀 화면의 씬과  UI를 만듭니다.

Title이라는 이름의 새로운 씬을 만듭니다. Build Settings를 열고 Title씬을 끌어다 가장 위로 추가합니다.

타이틀과 관련된 데이터는 Title폴더를 만들어 저장합니다. 사용할 이미지도 옮깁니다.

 

타이틀 화면 UI만들기

하이라키에서 +>UI>Image를 3개 추가해서 title_back, title_char, titile_logo을 연결해줍니다.

+>UI>Buttong을 2개 추가해서 bt_gamestart, bt_continue를 끌어다 연결합니다. 

필요에 따라 Image 컴포넌트의 Preserve Aspect, Set Native Size를 사용합니다.

Title Manager 스크립트 만들기

다음과 같이 TitleManager스크립트를 만들고 Canvas에 적용합니다.  아직 만드는 단계이므로 최종 파일은 나중에 업로드하겠습니다.

 

선언

UI와 Scene를 처리하기 위해 다음과 같이 선언합니다.

using UnityEngine.UI;
using UnityEngine.SceneManagement;

변수

버튼들의 참조를 연결할 외부변수입니다.

public GameObject startButton;      //스타트 버튼
public GameObject continueButton;   //이어하기 버튼

버튼이 눌리면 실행할 메쏘드를 준비합니다. 내용은 나중에 추가하겠습니다.

//스타트 버튼 눌림
public void StartButtonClicked()
{
}

//이어하기 버튼 눌림
public void ContinueButtonClicked()
{ 
}

GAME START 버튼 설정하기

하이라키뷰에서 StartButton을 선택하고 인스펙터뷰에 On Click() > +로 이벤트를 추가합니다. 게임오브젝트에 Canvas를 설정하고 풀다운 메뉴에서 No Function>TitleManager>StartButtonClicked()를 선택합니다.

 

Continue 버튼 설정하기

하이라키뷰에서 ContinueButton을 선택하고 인스펙터뷰에 On Click() > +로 이벤트를 추가합니다. 게임오브젝트에 Canvas를 설정하고 풀다운 메뉴에서  No Function>TitleManager>ContinueButtonClicked()를 선택합니다.

 

UIManager라는 UI를 관리하는 스크립트를 UIManager폴더를 만들어 저장후 Canvas에 어태치합니다.

앞에서 만든 아이템이나 HP표시 UI를 갱신하는 기능을 수행합니다.

UIManager의 내용은 다음과 같습니다.

변수

UI를 연결한 리소스의 참조들입니다.

public GameObject arrowText;        //화살의 수를 표시하는 Text
public GameObject keyText;          //열쇠 수를 표시하는Text
public GameObject hpImage;          //HP의 수를 표시하는 Image
public Sprite life3Image;           //HP3 이미지
public Sprite life2Image;           //HP2 이미지
public Sprite life1Image;           //HP1 이미지
public Sprite life0Image;           //HP0 이미지
public GameObject mainImage;        // 이미지를 가지는 GameObject
public GameObject resetButton;      // 리셋 버튼
public Sprite gameOverSpr;          // GAME OVER 이미지
public Sprite gameClearSpr;         // GAME CLEAR 이미지
public GameObject inputPanel;       //버추얼 패드와 공격 버튼을 배치한 조작 패널

소지품의 개수, 플레이어의 HP, 재시도하는 씬이름등입니다.

int hasKeys = 0;                    //열쇠 수
int hasArrows = 0;                  //화살 소지 수
int hp = 0;                         //플레이어의 HP

public string retrySceneName = "";  //재시도하는 씬 이름

Start()

아이템수와 HP를 갱신하기 위해 UpdateItemCont()와 UpdateHP()를 호출합니다. 기타 필요없는 버튼을 숨깁니다.

void Start()
{
    UpdateItemCount();  //아이템 수 갱신
    UpdateHP();         //HP갱신
    //이미지를 숨기기
    Invoke("InactiveImage", 1.0f);
    resetButton.SetActive(false);  //버튼 숨기기
}

Update()

Start()와 똑같이 아이템수와 HP를 갱신하기 위해 UpdateItemCont()와 UpdateHP()를 호출합니다. 

void Update()
{
    UpdateItemCount();  //아이템 수 갱심
    UpdateHP();         //HP갱신
}

UpdateItemCount()

static 변수인 ItemKeeper가 가진 각 아이템수가 현재UI와 다르면 UI를 변경합니다.

void UpdateItemCount() {
    //화살
    if (hasArrows != ItemKeeper.hasArrows)  {
        arrowText.GetComponent<Text>().text = ItemKeeper.hasArrows.ToString();
        hasArrows = ItemKeeper.hasArrows;
    }
    //열쇠
    if (hasKeys != ItemKeeper.hasKeys)  {
        keyText.GetComponent<Text>().text = ItemKeeper.hasKeys.ToString();
        hasKeys = ItemKeeper.hasKeys;
    }
}

UpdateHP()

레벨의 플레이어를 찾아 플레이어의 HP와 UI의 HP가 다르다면 갱신합니다. 만일 플레이어가 사망했다면 게임종료 상태로 변경하고 적절한 UI 를 표시합니다.

void UpdateHP() {
    //Player 가져오기
    if (PlayerController.gameState != "gameend")   {
        GameObject player = GameObject.FindGameObjectWithTag("Player");
        if (player != null)   {
            if (PlayerController.hp != hp)  {
                hp = PlayerController.hp;
                if (hp <= 0)  {
                    hpImage.GetComponent<Image>().sprite = life0Image;
                    //플레이어 사망!!
                    resetButton.SetActive(true);    //버튼 표시
                    mainImage.SetActive(true);      //이미지 표시
                                                    // 이미지 설정
                    mainImage.GetComponent<Image>().sprite = gameOverSpr;
                    inputPanel.SetActive(false);      //조작 UI 숨기기
                    PlayerController.gameState = "gameend";   //게임 종료
                }  else if (hp == 1) {
                    hpImage.GetComponent<Image>().sprite = life1Image;
                } else if (hp == 2) {
                    hpImage.GetComponent<Image>().sprite = life2Image;
                }   else {
                    hpImage.GetComponent<Image>().sprite = life3Image;
                }
            }
        }
    }
}

Retry()

게임오버가 되면 첫번째 Stage를 로딩합니다.

public void Retry()
{
    //HP 되돌리기
    PlayerPrefs.SetInt("PlayerHP", 3);
    //게임 중으로 설정
    SceneManager.LoadScene(retrySceneName);   //씬 이동
}

Cavas에 어태치된 UIManger 스크립트의 publc 변수들을 하이라키에서 끌어다 적용시킵니다.

 

여기까지 만들었으면 Cavas를 프로젝트뷰 UIManager에 놓고 프리팹으로 만듭니다.

 

다른씬에 프리팹 배치하기

dunjeon1 씬을 로딩하고 Canvas를 끌어다 놓습니다. 

Canvas의 인스펙터뷰에서 RenderCamera에 MainCamera를 설정합니다. 이제서야 UI가 나옵니다.ㅎ

마지막으로 하이라키의 +>UI>EventSystem을 선택해 추가해줍니다. 이미 있다면 생략해도 됩니다.

 

게임과 UI를 관리하는 시스템을 만들어 봅시다. UI관련 데이터는 UIManager폴더를 만들고 저장합니다.

 

게임 UI만들기

먼저 게임에 필요한 UI를 만듭니다. 각 아이템들의 개수와 현재 HP를 표시할 UI, 게임 오버 표시 재시도 버튼 그리고 사이드 뷰 게임과 마찬가지로 터치 조작으로 플레이어를 조작할 수 있게 버추얼 패드와 공격 버튼을 만듭니다.

 

아이템 소지수 표시하기

화살, 열쇠의 소지 수를 표시할 Image와 Text오브젝트를 만듭니다. 먼저 계측뷰에서 +>UI>Image를 선택후 Canvas와Image를 추가합니다. 이름을 ItemImage로 변경합니다.

 

Canvas오브젝트의 Render Mode를 Screen Space-Camera로 변경하고 Main Camera를 Renderer Camera로 Canvas의 Order in Layer를 10으로 설정합니다.

ItemImage오브젝트의 Image컴포넌트의 SoruceImage를 ItemImage로 설정하고, Presever Aspect, Set Native Size를 클릭합니다. Anchor Preset 위치를 ALT를 눌러 좌상으로 설정합니다.

ItemImage의 자식으로 UI>Text를 2개 추가하여 KeyText, ArrowText로 이름짓습니다. Width:120, Height:100정도로하고 Text내용은 000,Font Size 64정도 중앙정렬로 해서 맞춥니다. Text위치도 Image에 맞게 조정해줍니다.

플레이어 HP표시하기

플레이어의 HP를 표시할 이미지를 만듭니다. Life라는 Image입니다. 멀티스프라이트이므로 SpriteEditor에서 Automatic으로 분할합니다.

Automatic으로 분할하면 4개로 분할됩니다.

Canvas에 Image를 추가하고 이름을 HPImage로 하고 Source Image에 Life_0를 설정합니다. RectTransform Width:300, Height:100으로 하고 원본이미지의 크기로 설정하고 Canvas 우상단에 위치 시킵니다.

게임 진행상태 표시하기

Canvas 아래에 Image를 추가합니다. SourceImage로 GameStart 이미지를 적용시키고 Set Native Size버튼을 클릭합니다.

위치는 중앙 약간 위치쪽으로 설정하고 Rect Transform Anchor Presets를 중앙으로 합니다.

 

재시도 버튼 추가

Canvas중앙아래 Button을 추가하고 Button Image를 적용시키고 Set Native Size를 적용시킵니다. 이름 RetryButton이라고 합니다. Button의 자식 Text는 RETRY라고 하고 폰트사이즌는 64로 합니다.

 

스마트폰용 UI만들기

 

360도 버추얼패드 설정하기 

이미지는 사이드 뷰 게임과 다르지만  UI구성은 같습니다.

Canvas 아래에 Panel을 배치하고 자식으로 버추얼패드를 배치합니다. UI>Image를 총 2개 추가 화면 아래에 배치하고 이름은 VirtualPadBase로 합니다. ImageSource는 VirtualPad4D를 사용합니다. 자식으로 이미지를 하나도 배치하고 VirtualPad로 이름을 바꿉니다. VirtualPadTab이미지를 사용합니다.

공격버튼은 이미지가 아니라 UI>Button을 추가한후 이미지로 AttackButton을 사용하고 이름을 AttackButtond으로 바꾼후 다음과 같이 Panel의 자식 하이라키를 만듭니다.

Virtual Pad 스크립트는 사이드뷰에서 만든것을 그대로 사용합니다. VirtualPad에 어태치하고 Is4DPad를 체크합니다.

VirtualPad.cs
0.00MB

Virtual Pad를 선택하고 Event>Event Trigger컴포넌트를 추가합니다. 종류는 PointDown, Drag, PointUp을 하나씩 추가합니다.

추가된 EventType의 + 를 클릭히 None에 하이라키의 Virtual Pad를 추가해주고 None Function에 PadDown() PadDrag() PadUp()등 VirtualPad.cs의 메쏘드를 연결해줍니다.

공격버튼 설정하기

VirtualPad 클래스의 내용을 조금 변경해 봅니다. Attack 메서드를 추가합니다. Player 에 어태치된 ArrowShoot클래스의 Attack메서드를 호출합니다. VirtualPad클래스에 공격호출을 중계해 다른 씨에서 UI를 다시 사용하기 쉽습니다.

public void Attack()
{
    GameObject player = GameObject.FindGameObjectWithTag("Player");
    ArrowShoot shoot = player.GetComponent<ArrowShoot>();
    shoot.Attack();
}

VirtualPad 스크립트를 AttackButton에 어태치합니다. AttackButton을 선택하고 EventTrigger컴포넌트를 추가합니다. Add New EventType > PointDown을 추가합니다. +를 눌러 리스트를 추가하고 None에 하이라키의 VirtualPad를 추가하고 No Function에 풀다운메뉴에서 VirtualPad>Attack()를 선택해줍니다.

+ Recent posts