0. 들어가기 전에
이번엔 미리 만들어놓았던 장난감들을 점토 집을 업그레이드 할 때 소환되도록 했다. 그리고 데이터를 저장하고 다시 시작했을 때 저장해놓은 데이터를 반영해서 장난감들이 그대로 소환되도록 했다.
1. 게임 오브젝트
Plant Panel 게임 오브젝트의 Upgrade Panel 컴포넌트 속 Toys 배열에 미리 만들어놓은 장난감 버튼 게임 오브젝트를 넣어놓았다.
점토의 집을 업그레이드 할 때마다 각 단계에 맞는 장난감 게임 오브젝트가 나타나도록 하기 위함이다.
2. 스크립트
이번에 새로 만든 스크립트는 따로 없고 기존의 스크립트를 수정했다. 수정한 스크립트는 GameManager, UpgradePanel 이다.
2.1 GameManager 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
[Header("Game Data")]
public float love; // 애정
public float gold; // 골드
public bool[] unLockedClays; // 점토들의 해금 여부
public bool[] catchedClays; // 야생에서 잡아왔는지 확인용
public int clayHouseLevel = 1; // 점토 아파트 레벨
public int clayClickLevel = 1; // 점토 클릭 레벨
public int[] clayHouseLoveList; // 업그레이드 비용
public int[] clayClickLoveList; // 업그레이드 비용
public int curPossibleClayNum = 1; // 최대로 키울 수 있는 점토의 개수(1레벨은 1마리, 2레벨은 2마리...)
[Header("Game Manager")]
public static GameManager instance; // 싱글톤 이용하기 위함
[Header("GameDataUI")]
public GameDataUIController gameDataUI;
public delegate void SetInfoPanelHandler(string text); // 델리게이트 선언
public event SetInfoPanelHandler OnSetInfoPanel;
[Header("Pool Manager")]
public PoolManager poolManager;
[Header("Coroutine")]
public Coroutine updateTextUICoroutine;
[Header("Game Exit")]
public Button gameExitButton;
[Header("Effect")]
// 0: 점토 레벨업, 1: 점토 판매, 2: 점토 해금, 3: 업그레이드
public ParticleSystem[] effectsPrefabs; // 프리팹 넣어놓기
public ParticleSystem[] effects; // 관리용 변수
public string[] effectGameObjectNames;
[Header("Toy Control")]
public int curToyIdx = -1; // 현재 선택된 장난감
public RuntimeAnimatorController[] clayToyAnimators; // 가구랑 상호작용하는 애니메이터
public string[] toyInfo; // 가구를 클릭하면 안내 판넬에 띄울 내용
public delegate void SetClayHouseLevel(int houseLevel, int clickLevel);
public SetClayHouseLevel OnSetClayHouseInfo;
private void Awake()
{
// 싱글톤 패턴
if (instance != null && instance != this)
{
// 이미 존재하면 새로 만든거 없애
Destroy(gameObject);
return;
}
instance = this;
DontDestroyOnLoad(gameObject); // 얘는 다른 씬으로 전환되어도 안 없앨 거임
poolManager = GameObject.Find("PoolManager").GetComponent<PoolManager>(); // 풀매니저 찾아서 할당
for (int i=0; i < effectsPrefabs.Length; i++)
{
// 이펙트 생성해서 넣어놓기
effects[i] = Instantiate(effectsPrefabs[i], GameObject.Find(effectGameObjectNames[i]).transform);
effects[i].gameObject.SetActive(false); // 비활성화
}
}
void Start()
{
gameExitButton = GameObject.Find("OptionPanelParent").transform.Find("Option Panel").transform.Find("Image").transform.Find("Exit Button").GetComponent<Button>();
gameExitButton.onClick.AddListener(DataManager.instance.SaveGameData); // 게임 데이터 저장 메서드 연결
gameExitButton.onClick.AddListener(GameExit); // 게임 종료 메서드 연결
GetLove(0);
GetGold(1000);
// 저장된 게임 데이터가 있는 경우 데이터 가져와서 반영
if (DataManager.instance.data.unlockClays != null)
{
// 저장된 데이터 반영해서 가져오기
for (int i = 0; i < unLockedClays.Length; i++)
{
unLockedClays[i] = DataManager.instance.data.unlockClays[i];
catchedClays[i] = DataManager.instance.data.catchClays[i];
}
}
// 저장된 게임 수치 데이터가 있는 경우 데이터 가져와서 반영
if (DataManager.instance.data.valueDatas != null)
{
// 저장된 데이터 반영해서 가져오기
gold = DataManager.instance.data.valueDatas.gold;
love = DataManager.instance.data.valueDatas.love;
clayHouseLevel = DataManager.instance.data.valueDatas.clayHouseLevel;
clayClickLevel = DataManager.instance.data.valueDatas.clayClickLevel;
curPossibleClayNum = DataManager.instance.data.valueDatas.curPossibleClayNum;
SetUpgradePanel(); // 델리게이트 호출
}
// 메서드 연결하기
DataManager.instance.OnSave -= SetSaveData; // 중복 방지하기 위해 먼저 빼줌
DataManager.instance.OnSave += SetSaveData;
}
public void SetUpgradePanel()
{
Debug.Log("반영이 되고 있나여..?");
OnSetClayHouseInfo?.Invoke(clayHouseLevel, clayClickLevel); // UpgradePanel 클래스의 SetUpgardePanel() 메서드 호출
}
// 재화 얻는 함수
public void GetGold(float capacity)
{
// 이미 코루틴이 종료되지 않은 중에 동일한게 또 들어오면 겹쳐서 반영이 돼서 이상하게 될 수 있으므로 null 인지 판단해야함.
if (updateTextUICoroutine != null)
{
StopCoroutine(updateTextUICoroutine);
}
updateTextUICoroutine = StartCoroutine(gameDataUI.UpdateTextUI("gold", gold + capacity, gold));
gold += capacity;
//PlayerPrefs.SetFloat("Gold", gold); // 데이터 저장
}
public void GetLove(float capacity)
{
// 이미 코루틴이 종료되지 않은 중에 동일한게 또 들어오면 겹쳐서 반영이 돼서 이상하게 될 수 있으므로 null 인지 판단해야함.
if (updateTextUICoroutine != null)
{
StopCoroutine(updateTextUICoroutine);
}
updateTextUICoroutine = StartCoroutine(gameDataUI.UpdateTextUI("love", love + capacity, love));
love += capacity;
//PlayerPrefs.SetFloat("Love", love); // 데이터 저장
}
public void SetSaveData()
{
int size = unLockedClays.Length;
DataManager.instance.data.unlockClays = new List<bool>();
DataManager.instance.data.catchClays = new List<bool>();
for (int i = 0; i < size; i++)
{
DataManager.instance.data.unlockClays.Add(unLockedClays[i]); // 해금 여부 저장
DataManager.instance.data.catchClays.Add(catchedClays[i]); // 포획 여부 저장
}
// 수치 데이터 저장
DataManager.instance.data.valueDatas = new ValueDatas();
DataManager.instance.data.valueDatas.gold = gold;
DataManager.instance.data.valueDatas.love = love;
DataManager.instance.data.valueDatas.clayHouseLevel = clayHouseLevel;
DataManager.instance.data.valueDatas.clayClickLevel = clayClickLevel;
DataManager.instance.data.valueDatas.curPossibleClayNum = curPossibleClayNum;
}
public void GameExit()
{
// 게임 종료
Application.Quit();
}
public void StartInfoPanel(string text)
{
// 연결된 메서드 실행시키기
OnSetInfoPanel?.Invoke(text);
}
}
2.2 GameManager 스크립트 변경 사항 설명
1. 변수
델리게이트를 추가했다. 게임을 다시 시작할 때 로드한 데이터를 반영하는 일에 사용하기 위함이다.
public delegate void SetClayHouseLevel(int houseLevel, int clickLevel);
public SetClayHouseLevel OnSetClayHouseInfo;
2. Start()
SetUpgradePanel() 메서드를 호출하는 부분이 추가되었다. 로드한 데이터를 업그레이트 판넬에 반영하기 위함이다.
void Start()
{
gameExitButton = GameObject.Find("OptionPanelParent").transform.Find("Option Panel").transform.Find("Image").transform.Find("Exit Button").GetComponent<Button>();
gameExitButton.onClick.AddListener(DataManager.instance.SaveGameData); // 게임 데이터 저장 메서드 연결
gameExitButton.onClick.AddListener(GameExit); // 게임 종료 메서드 연결
GetLove(0);
GetGold(1000);
// 저장된 게임 데이터가 있는 경우 데이터 가져와서 반영
if (DataManager.instance.data.unlockClays != null)
{
// 저장된 데이터 반영해서 가져오기
for (int i = 0; i < unLockedClays.Length; i++)
{
unLockedClays[i] = DataManager.instance.data.unlockClays[i];
catchedClays[i] = DataManager.instance.data.catchClays[i];
}
}
// 저장된 게임 수치 데이터가 있는 경우 데이터 가져와서 반영
if (DataManager.instance.data.valueDatas != null)
{
// 저장된 데이터 반영해서 가져오기
gold = DataManager.instance.data.valueDatas.gold;
love = DataManager.instance.data.valueDatas.love;
clayHouseLevel = DataManager.instance.data.valueDatas.clayHouseLevel;
clayClickLevel = DataManager.instance.data.valueDatas.clayClickLevel;
curPossibleClayNum = DataManager.instance.data.valueDatas.curPossibleClayNum;
SetUpgradePanel(); // 델리게이트 호출
}
// 메서드 연결하기
DataManager.instance.OnSave -= SetSaveData; // 중복 방지하기 위해 먼저 빼줌
DataManager.instance.OnSave += SetSaveData;
}
3. SetUpgradePanel()
OnSetClayHouseInfo 델리게이트에 연결된 메서드를 호출하는 메서드이다. 이 메서드는 보통 상황에서는 안 쓰고 데이터를 로드할 때만 쓸 것이다.
public void SetUpgradePanel()
{
OnSetClayHouseInfo?.Invoke(clayHouseLevel, clayClickLevel); // UpgradePanel 클래스의 SetUpgardePanel() 메서드 호출
}
2.3 UpgradePanel 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using System.Net;
public class UpgradePanel : MonoBehaviour
{
[Header("Plant Panel UI")]
public Text houseSubText; // 업그레이드 내용
public Text houseLoveText; // 필요한 애정 수치
public Button houseUpButton; // 업그레이드 버튼
public Text clickSubText;
public Text clickLoveText;
public Button clickUpButton;
[Header("Effect")]
public int effectIdx = 3;
[Header("Clay Toys")]
public Button[] toys; // 0: 캣타워, 1: 러그, 2: 블라인더, 3: 조명
private void Awake()
{
GameManager.instance.OnSetClayHouseInfo -= SetUpgardePanel; // 메서드 연결
GameManager.instance.OnSetClayHouseInfo += SetUpgardePanel; // 메서드 연결
}
private void Start()
{
SetUpgardePanel(); // 판넬 정보 설정
// 버튼에 메서드 연결
houseUpButton.onClick.AddListener(UpgradeHouse);
clickUpButton.onClick.AddListener(UpgradeClick);
}
private void OnEnable()
{
// 활성화 될 때 호출되는 함수
SetUpgardePanel(); // ui 세팅
}
// 게임매니저의 델리게이트에 연결할거임
// 얘는 게임이 처음 시작할 때 호출할 것..
public void SetUpgardePanel(int houseLevel, int clickLevel)
{
// 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
if (GameManager.instance.clayHouseLevel == 5)
{
houseLoveText.text = "max";
}
else
{
houseSubText.text = "점토 수용량 " + (GameManager.instance.clayHouseLevel + 1);
houseLoveText.text = GameManager.instance.clayHouseLoveList[GameManager.instance.clayHouseLevel] + "";
}
// 집 레벨에 맞게 장난감 활성화
if (houseLevel >= 2)
{
for (int i=0; i<=houseLevel-2; i++)
{
// 장난감 활성화
toys[i].gameObject.SetActive(true);
}
}
// 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
if (GameManager.instance.clayClickLevel == 5)
{
clickLoveText.text = "max";
}
else
{
clickSubText.text = "클릭 생산량 x " + (GameManager.instance.clayClickLevel + 1);
clickLoveText.text = GameManager.instance.clayClickLoveList[GameManager.instance.clayClickLevel] + "";
}
}
// 얘는 게임이 진행되는 중에 언제든지 그냥 이용할 수 있음..
public void SetUpgardePanel()
{
// 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
if (GameManager.instance.clayHouseLevel == 5)
{
houseLoveText.text = "max";
}
else
{
houseSubText.text = "점토 수용량 " + (GameManager.instance.clayHouseLevel + 1);
houseLoveText.text = GameManager.instance.clayHouseLoveList[GameManager.instance.clayHouseLevel] + "";
}
// 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
if (GameManager.instance.clayClickLevel == 5)
{
clickLoveText.text = "max";
}
else
{
clickSubText.text = "클릭 생산량 x " + (GameManager.instance.clayClickLevel + 1);
clickLoveText.text = GameManager.instance.clayClickLoveList[GameManager.instance.clayClickLevel] + "";
}
}
public void UpgradeHouse()
{
// 이미 레벨이 최고면 걍 빠져나가도록..
if (GameManager.instance.clayHouseLevel == 5) {
// 안내창 띄우기
GameManager.instance.StartInfoPanel("최대 레벨입니다!");
return;
}
else if (GameManager.instance.clayHouseLoveList[GameManager.instance.clayHouseLevel] > GameManager.instance.love)
{
// 안내창 띄우기
GameManager.instance.StartInfoPanel("애정이 부족해요 ㅠ_ㅠ");
return;
}
GameManager.instance.GetLove(-GameManager.instance.clayHouseLoveList[GameManager.instance.clayHouseLevel]); // 애정 차감
GameManager.instance.clayHouseLevel++;
GameManager.instance.curPossibleClayNum++; // 키울 수 있는 점토 수 +1
toys[GameManager.instance.clayHouseLevel - 2].gameObject.SetActive(true); // 장난감 게임 오브젝트 활성화
if (GameManager.instance.clayHouseLevel == 5)
{
houseLoveText.text = "max";
}
PlayEffect(effectIdx); // 이펙트 소환
SetUpgardePanel();
}
public void UpgradeClick()
{
// 이미 레벨이 최고면 걍 빠져나가도록..
if (GameManager.instance.clayClickLevel == 5)
{
GameManager.instance.StartInfoPanel("최대 레벨입니다!");
return;
}
else if (GameManager.instance.clayClickLoveList[GameManager.instance.clayClickLevel] > GameManager.instance.love)
{
// 안내창 띄우기
GameManager.instance.StartInfoPanel("애정이 부족해요 ㅠ_ㅠ");
return;
}
GameManager.instance.GetLove(-GameManager.instance.clayClickLoveList[GameManager.instance.clayClickLevel]); // 애정 차감
GameManager.instance.clayClickLevel++;
if (GameManager.instance.clayClickLevel == 5)
{
clickLoveText.text = "max";
}
PlayEffect(effectIdx); // 이펙트 소환
SetUpgardePanel();
}
public void PlayEffect(int idx)
{
if (GameManager.instance.effects[idx].gameObject.activeSelf == false)
GameManager.instance.effects[idx].gameObject.SetActive(true); // 활성화하기
GameManager.instance.effects[idx].Play();
}
public void ClosePanel()
{
gameObject.SetActive(false); // 활성화 끄기..
}
}
2.4 UpgradePanel 스크립트 변경 사항 설명
1. 변수
버튼 게임 오브젝트(장난감) 을 넣어놓을 변수이다.
[Header("Clay Toys")]
public Button[] toys; // 0: 캣타워, 1: 러그, 2: 블라인더, 3: 조명
2. Awake()
OnSetClayHouseInfo 델리게이트에 SetUpgradePanel 메서드를 연결하는 로직이 들어있다. 중복 연결을 막기 위해서 메서드를 먼저 뺀 후 다시 더해준다.
만약 해당 메서드가 연결되어 있지 않을 때에도 메서드를 빼는게 문제되지 않는다고 한다.
private void Awake()
{
GameManager.instance.OnSetClayHouseInfo -= SetUpgardePanel; // 메서드 연결
GameManager.instance.OnSetClayHouseInfo += SetUpgardePanel; // 메서드 연결
}
3. SetUpgardePanel(int houseLevel, int clickLevel)
로드한 데이터를 반영할 때 호출하는 메서드이다.
점토 집 레벨에 맞게 업그레이드 판넬의 정보도 설정해주고, 장난감이 소환되도록 했다.
점토 클릭 레벨에 맞게 업그레이드 판넬의 정보도 설정해줬다.
// 게임매니저의 델리게이트에 연결할거임
// 얘는 게임이 처음 시작할 때 호출할 것..
public void SetUpgardePanel(int houseLevel, int clickLevel)
{
// 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
if (houseLevel == 5)
{
houseLoveText.text = "max";
}
else
{
houseSubText.text = "점토 수용량 " + (houseLevel + 1);
houseLoveText.text = GameManager.instance.clayHouseLoveList[houseLevel] + "";
}
// 집 레벨에 맞게 장난감 활성화
if (houseLevel >= 2)
{
for (int i=0; i<=houseLevel-2; i++)
{
// 장난감 활성화
toys[i].gameObject.SetActive(true);
}
}
// 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
if (clickLevel == 5)
{
clickLoveText.text = "max";
}
else
{
clickSubText.text = "클릭 생산량 x " + (clickLevel + 1);
clickLoveText.text = GameManager.instance.clayClickLoveList[clickLevel] + "";
}
}
4. SetUpgardePanel()
얘는 업그레이드 할 때마다 호출되는 메서드이다. 위의 SetUpgradePanel 이랑 논리적으로 다른 부분은 딱히 없다.
// 얘는 업그레이드 할 때 호출되는 메서드..
public void SetUpgardePanel()
{
// 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
if (GameManager.instance.clayHouseLevel == 5)
{
houseLoveText.text = "max";
}
else
{
houseSubText.text = "점토 수용량 " + (GameManager.instance.clayHouseLevel + 1);
houseLoveText.text = GameManager.instance.clayHouseLoveList[GameManager.instance.clayHouseLevel] + "";
}
// 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
if (GameManager.instance.clayClickLevel == 5)
{
clickLoveText.text = "max";
}
else
{
clickSubText.text = "클릭 생산량 x " + (GameManager.instance.clayClickLevel + 1);
clickLoveText.text = GameManager.instance.clayClickLoveList[GameManager.instance.clayClickLevel] + "";
}
}
2.5 DataManager 스크립트
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class DataManager : MonoBehaviour
{
[Header("Data Manager")]
public static DataManager instance;
[Header("Save Datas")]
string GameDataFileName = "GameData.json";
public Datas data = new Datas(); // 저장용 클래스 변수
public delegate void SaveDataHandler(); // 데이터 저장 관리
public event SaveDataHandler OnSave;
[Header("Save Button")]
public Button saveButton; // 얘는 게임 시작할 때랑, 다른 씬에서 돌아올 때마다 값 할당해줘야함.
private void Awake()
{
// 싱글톤 이용
if (instance != null && instance != this)
{
// 만약 이미 존재하면 그냥 없애
Destroy(gameObject);
return;
}
instance = this;
DontDestroyOnLoad(gameObject); // 얘는 다른 씬으로 전환되어도 안 없앨 거임
// 일단 맨 처음엔 null 로 설정..
data.unlockClays = null;
data.catchClays = null;
data.valueDatas = null;
LoadGameData(); // 게임 데이터 가져오기
}
private void Start()
{
saveButton = GameObject.Find("OptionPanelParent").transform.Find("Option Panel").transform.Find("Image").transform.Find("Save Button").GetComponent<Button>(); // 버튼 찾아서 할당
saveButton.onClick.AddListener(SaveGameData); // 데이터 저장 메서드 연결
}
// 불러오기
public void LoadGameData()
{
string filePath = Application.persistentDataPath + "/" + GameDataFileName;
// 저장된 게임이 있다면
if (File.Exists(filePath))
{
// 저장된 파일 읽어오고 Json 을 클래스 형식으로 전환해서 할당
string FromJsonData = File.ReadAllText(filePath);
print(FromJsonData);
data = JsonUtility.FromJson<Datas>(FromJsonData);
print("불러오기 완료");
}
}
// 저장하기
public void SaveGameData()
{
OnSave?.Invoke(); // OnSave 에 연결된 메서드 모두 실행
// 클래스를 Json 형식으로 변환(true: 가독성 좋게 작성)
string ToJsonData = JsonUtility.ToJson(data, true);
Debug.Log(ToJsonData + "저장할건디용");
string filePath = Application.persistentDataPath + "/" + GameDataFileName;
// 이미 저장된 파일이 있다면 덮어쓰고, 없다면 새로 만들어서 저장
File.WriteAllText(filePath, ToJsonData);
// 올바르게 저장됐는지 확인
print("저장완료");
}
}
3. 결과물
점토 집을 업그레이드 하면 장난감이 생겨나는 걸 볼 수 있다. 데이터를 저장하고 다시 플레이 버튼을 누르면 저장된 데이터를 반영하여 장난감이 그대로 소환된 모습을 볼 수 있다.