0. 들어가기 전에
이번엔 게임 화면에 안내 문구를 띄우는 기능을 만들었다. 점토를 구매할 때 돈이 부족하거나, 점토를 해금할 때 불가능하거나, 다 자라지 않은 점토를 판매하려고 하는 등의 상황에 안내 문구를 나타나게 해서 플레이어가 알 수 있도록 했다.
1. 게임 오브젝트
이번엔 Info Panel 게임 오브젝트를 추가했다.
1. 하이어라키 창 모습
2. 인스펙터 창 모습
3. 애니메이터
안내 문구 판넬에 애니메이션을 적용하기 위해서 애니메이터를 만들고, 애니메이션도 만들었다.
애니메이터의 상태는 다음 사진과 같다.
Started 가 체크 되면 Start 애니메이션이 수행되고 끝나면 Exit 로 이동하도록 했다. Start 에는 직접 만든 애니메이션을 넣어놓았다.
2. 스크립트
이번에 새로 만든 스크립트는 InfoPanelController 이고 수정한 스크립트는 ClaySellController, ClayPanel, GameManager 이다.
2.1 InfoPanelController 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
public class InfoPanelController : MonoBehaviour
{
[Header("Animation")]
public Animator anim;
[Header("UI")]
public Text infoText;
private void Start()
{
anim = gameObject.GetComponent<Animator>(); // 할당하기
// 메서드 연결
GameManager.instance.OnSetInfoPanel -= SetAndPlayInfoPanel;
GameManager.instance.OnSetInfoPanel += SetAndPlayInfoPanel;
}
public void SetAndPlayInfoPanel(string text)
{
infoText.text = text; // 안내 문구 설정
anim.SetTrigger("Started"); // 애니메이션 실행
}
}
2.2 InfoPanelController 스크립트 설명
1. 변수
anim, infoText 변수가 존재한다. anim 은 안내 문구 판넬에 애니메이션을 적용하기 위해서 필요하고, infoText 는 안내 문구를 설정하기 위해 필요하다.
[Header("Animation")]
public Animator anim;
[Header("UI")]
public Text infoText;
2. Start()
anim 에 현재 안내 문구 판넬 게임 오브젝트의 Animator 컴포넌트를 할당한다.
그리고 GameManager 의 OnSetInfoPanel 델리게이트에 SetAndPlayInfoPanel 메서드를 연결해서 신호를 주면 연결된 메서드가 호출될 수 있도록 했다.
private void Start()
{
anim = gameObject.GetComponent<Animator>(); // 할당하기
// 메서드 연결
GameManager.instance.OnSetInfoPanel -= SetAndPlayInfoPanel;
GameManager.instance.OnSetInfoPanel += SetAndPlayInfoPanel;
}
3. SetAndPlayInfoPanel(string text)
입력 받은 값으로 안내 문구 판넬의 문구를 설정하고 애니메이션을 실행하는 메서드이다.
public void SetAndPlayInfoPanel(string text)
{
infoText.text = text; // 안내 문구 설정
anim.SetTrigger("Started"); // 애니메이션 실행
}
2.3 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; // 야생에서 잡아왔는지 확인용
[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;
private void Awake()
{
// 싱글톤 패턴
if (instance != null && instance != this)
{
// 이미 존재하면 새로 만든거 없애
Destroy(gameObject);
return;
}
instance = this;
DontDestroyOnLoad(gameObject); // 얘는 다른 씬으로 전환되어도 안 없앨 거임
poolManager = GameObject.Find("PoolManager").GetComponent<PoolManager>(); // 풀매니저 찾아서 할당
}
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];
}
}
// 메서드 연결하기
DataManager.instance.OnSave -= SetSaveData; // 중복 방지하기 위해 먼저 빼줌
DataManager.instance.OnSave += SetSaveData;
}
// 재화 얻는 함수
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]); // 포획 여부 저장
}
}
public void GameExit()
{
// 게임 종료
Application.Quit();
}
public void StartInfoPanel(string text)
{
// 연결된 메서드 실행시키기
OnSetInfoPanel?.Invoke(text);
}
}
2.4 GameManager 스크립트 변경 사항 설명
1. 변수
아래와 같은 변수가 추가되었다. 일단 OnSelInfoPanel 이라는 이름의 델리게이트를 선언했다. 여기에 메서드를 연결해서 호출할 수 있도록 했다.
public delegate void SetInfoPanelHandler(string text); // 델리게이트 선언
public event SetInfoPanelHandler OnSetInfoPanel;
2. StartInfoPanel(string text)
얘는 OnSetInfoPanel 에 연결된 메서드를 호출한다. OnSetInfoPanel 에는 InfoPanelController 의 SetAndPlayInfoPanel 메서드가 연결되어 있다.
즉, StartInfoPanel 메서드를 호출하면 SetAndPlayInfoPanel 메서드를 호출하는 것이다.
public void StartInfoPanel(string text)
{
// 연결된 메서드 실행시키기
OnSetInfoPanel?.Invoke(text);
}
2.5 ClayPanel 스크립트
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
public class ClayPanel : MonoBehaviour
{
[Header("UI")]
public Button pageLeftButton; // 페이지 왼쪽 버튼
public Button pageRightButton; // 페이지 오른쪽 버튼
public Button buyClayButton; // 점토 구매 버튼
public Button unlockClayButton; // 점토 해금 버튼
public Image lockedImage; // 잠금된 점토 이미지
public Image clayImage; // 점토 이미지
public Text clayName; // 점토 이름
public Text buyPrice; // 점토 가격
public GameObject lockedPage; // 잠금 페이지
[Header("Control")]
public int pageIdx = 0; // 페이지 인덱스
public int maxIdx;
public int minIdx = 0;
private void Start()
{
maxIdx = GameManager.instance.poolManager.clayPrefabs.Length;
pageLeftButton.onClick.AddListener(DownPageIdx);
pageLeftButton.onClick.AddListener(SetClayPanel);
pageRightButton.onClick.AddListener(UpPageIdx);
pageRightButton.onClick.AddListener(SetClayPanel);
}
private void OnEnable()
{
// 활성화 될 때 호출되는 함수
ResetClayPanel();
}
public void ResetClayPanel()
{
pageIdx = 0;
SetButtonInfo(); // 버튼 설정해주기
}
public void SetClayPanel()
{
Clay clay = GameManager.instance.poolManager.clayPrefabs[pageIdx].GetComponent<Clay>();
if (GameManager.instance.unLockedClays[pageIdx] == false)
{
lockedPage.SetActive(true); // 해금이 안된 경우엔 잠금 판넬 활성화..
}
else
{
lockedPage.SetActive(false); // 해금된 경우엔 잠금 판넬 비활성화..
}
// 페이지 인덱스에 맞게 설정
lockedImage.sprite = clay.clay; // 점토의 이미지 가져오기
clayImage.sprite = clay.clay;
clayName.text = clay.clayName; // 점토 이름 가져오기
buyPrice.text = clay.buyPrice + ""; // 점토 가격 가져오기
}
public void UpPageIdx()
{
if (pageIdx >= maxIdx - 1) return; // 빠져나가기..
pageIdx++;
SetButtonInfo();
}
public void DownPageIdx()
{
if (pageIdx <= 0) return; // 빠져나가기..
pageIdx--;
SetButtonInfo();
}
private void SetButtonInfo()
{
ColorBlock col = unlockClayButton.colors;
// 만약 야생에서 점토를 잡아왔으면 해금하기 버튼 노란색으로 바꿔주기
if (GameManager.instance.catchedClays[pageIdx])
{
col.normalColor = new Color32(255, 220, 90, 255); // 노란색
col.highlightedColor = new Color32(255, 220, 90, 255); // 노란색
unlockClayButton.colors = col; // 색 만들어 놓은거 할당해주기
}
else
{
col.normalColor = new Color32(255, 255, 255, 255); // 흰색
col.highlightedColor = new Color32(255, 255, 255, 255); // 흰색
unlockClayButton.colors = col; // 색 만들어 놓은거 할당해주기
}
unlockClayButton.onClick.RemoveAllListeners(); // 일단 다 지웡
unlockClayButton.onClick.AddListener(() => Unlock(pageIdx)); // Unlock 메서드 추가
buyClayButton.onClick.RemoveAllListeners(); // 일단 다 지웡
buyClayButton.onClick.AddListener(() => BuyClay(pageIdx)); // BuyClay 메서드 추가
}
public void Unlock(int idx)
{
// 야생에서 아직 안 잡았으면 해금 못하도록..
if (!GameManager.instance.catchedClays[pageIdx])
{
// 안내 판넬 띄우기
GameManager.instance.StartInfoPanel("야생에서 잡아와야 해요!");
return;
}
GameManager.instance.unLockedClays[idx] = true;
SetClayPanel();
}
public void BuyClay(int idx)
{
int price = GameManager.instance.poolManager.clayPrefabs[idx].GetComponent<Clay>().buyPrice;
// 돈이 충분하면 구매
if (GameManager.instance.gold >= price)
{
GameManager.instance.GetGold(-price);
GameManager.instance.poolManager.GetGameObject(idx); // 동물 get!
}
else
{
// 안내 판넬 띄우기
GameManager.instance.StartInfoPanel("돈이 부족해요 ㅠ_ㅠ");
}
}
public void ClosePanel()
{
gameObject.SetActive(false); // 활성화 끄기..
}
}
2.6. ClayPanel 스크립트 변경 사항 설명
1. Unlock(int idx)
아래와 같이 Unlock 메서드의 일부를 수정했다. 해금하기 버튼을 눌렀는데 해금이 불가능한 상황이라면 안내 판넬을 띄우도록 했다.
// 야생에서 아직 안 잡았으면 해금 못하도록..
if (!GameManager.instance.catchedClays[pageIdx])
{
// 안내 판넬 띄우기
GameManager.instance.StartInfoPanel("야생에서 잡아와야 해요!");
return;
}
2. BuyClay(int idx)
아래와 같이 일부 코드를 수정했다. 돈이 부족한 경우에 안내 판넬을 띄우도록 했다.
// 돈이 충분하면 구매
if (GameManager.instance.gold >= price)
{
GameManager.instance.GetGold(-price);
GameManager.instance.poolManager.GetGameObject(idx); // 동물 get!
}
else
{
// 안내 판넬 띄우기
GameManager.instance.StartInfoPanel("돈이 부족해요 ㅠ_ㅠ");
}
2.7 ClaySellController 스크립트
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.EventSystems;
public class ClaySellController : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
[Header("Clay Info")]
public PoolManager poolManager;
public Clay sellClay; // 판매할 점토
private void Start()
{
poolManager.OnSellClay += SellClay; // 델리게이트에 메서드 연결
}
private void OnDestroy()
{
poolManager.OnSellClay -= SellClay; // 이벤트 구독 해제
}
public void OnPointerEnter(PointerEventData eventData)
{
poolManager.isSellZone = true;
// 만약 점토가 점토 판매 버톤으로 드래그되어서 들어온다면..
if (poolManager.curClay.isDragging)
sellClay = poolManager.curClay; // 판매할 점토를 현재 드래그 중인 클레이로 설정..
}
public void OnPointerExit(PointerEventData eventData)
{
poolManager.isSellZone = false;
sellClay = null;
}
public void SellClay()
{
if (sellClay != null)
{
if (sellClay.clayLevel == 5) // 성체만 판매 가능
{
poolManager.ReturnToPool(sellClay.gameObject); // 비활성화시키기..
GameManager.instance.GetGold(sellClay.sellPrice); // 돈 얻기
}
else
{
// 안내 문구 시작
GameManager.instance.StartInfoPanel("성체가 된 점토만 판매할 수 있어요!");
return;
}
}
}
}
2.8 ClaySellController 스크립트 변경 사항 설명
1. SellClay()
아래와 같이 일부 코드만 수정했다. 점토를 판매 버튼에 드래그&드롭 했는데 만약 성체가 아니라면 팔지 않고 나가도록 한다. 메서드를 빠져나가기 전에 안내 문구를 띄우도록 했다.
if (sellClay.clayLevel == 5) // 성체만 판매 가능
{
poolManager.ReturnToPool(sellClay.gameObject); // 비활성화시키기..
GameManager.instance.GetGold(sellClay.sellPrice); // 돈 얻기
}
else
{
// 안내 문구 시작
GameManager.instance.StartInfoPanel("성체가 된 점토만 판매할 수 있어요!");
return;
}
3. 결과물