0. 들어가기 전에
이번엔 게임에 이펙트 효과를 적용했다. 점토가 성장할 때, 점토를 판매할 때, 점토를 해금할 때, 능력을 업그레이드할 때 이펙트 효과가 나타나도록 했다.
1. 게임 오브젝트
GameManager 의 게임 오브젝트를 수정했다. 자식 오브젝트로 아래 사진과 같이 네 개의 오브젝트를 추가했다. 게임이 시작되면 이펙트 프리팹으로 게임 오브젝트를 생성하는데 해당 게임 오브젝트의 부모가 될 게임 오브젝트들이다.
2. 스크립트
이번에 새로 만든 스크립트는 없다. GameManager, ClayPanel, ClaySellController, Clay 스크립트를 수정했다.
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;
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;
}
// 메서드 연결하기
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]); // 포획 여부 저장
}
// 수치 데이터 저장
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. 변수
이펙트 기능을 넣기 위해 변수를 선언했다. effectPrefabs 에는 이펙트 게임 오브젝트 프리팹을 넣어놓았다. 그리고 생성된 게임 오브젝트를 관리할 effects 배열도 만들었다.
그리고 생성된 게임 오브젝트의 부모를 지정하기 위해 게임 매니저 속 자식 게임 오브젝트들의 이름을 저장할 배열도 선언했다.
[Header("Effect")]
// 0: 점토 레벨업, 1: 점토 판매, 2: 점토 해금, 3: 업그레이드
public ParticleSystem[] effectsPrefabs; // 프리팹 넣어놓기
public ParticleSystem[] effects; // 관리용 변수
public string[] effectGameObjectNames;
2. Awake()
게임 시작하자마자 이펙트를 만들도록 했다. 근데 이펙트가 생성될 때 Play 도 자동으로 되기 때문에 일단 활성화도 동시에 끄도록 해서 안 보이도록 했다.
for (int i=0; i < effectsPrefabs.Length; i++)
{
// 이펙트 생성해서 넣어놓기
effects[i] = Instantiate(effectsPrefabs[i], GameObject.Find(effectGameObjectNames[i]).transform);
effects[i].gameObject.SetActive(false); // 비활성화
}
2.3 ClayPanel 스크립트 변경 사항 설명
1. 변수
호출할 이펙트의 인덱스를 지칭하는 변수를 선언했다.
[Header("Effect")]
public int effectIdx = 2;
2. Unlock(int idx)
PlayEffect(int idx) 메서드를 호출하는 부분이 추가됐다. 해금하기 버튼을 누르면 조건을 만족한 경우 해금됨과 동시에 이펙트가 소환되도록 했다.
public void Unlock(int idx)
{
// 야생에서 아직 안 잡았으면 해금 못하도록..
if (!GameManager.instance.catchedClays[pageIdx])
{
// 안내 판넬 띄우기
GameManager.instance.StartInfoPanel("야생에서 잡아와야 해요!");
return;
}
GameManager.instance.unLockedClays[idx] = true;
PlayEffect(effectIdx); // 이펙트 소환
SetClayPanel();
}
3. PlayEffect(int idx)
이펙트를 소환하는 메서드이다.
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();
}
2.4 ClaySellController 스크립트 변경 사항 설명
1. 변수
호출할 이펙트의 인덱스를 지칭하는 변수를 선언했다.
[Header("Effect")]
public int effectIdx = 1;
2. SellClay()
메서드를 수정했다. PlayEffect 메서드를 호출하는 부분이 추가되었다.
public void SellClay()
{
if (sellClay != null)
{
if (sellClay.clayLevel == 5) // 성체만 판매 가능
{
poolManager.ReturnToPool(sellClay.gameObject); // 비활성화시키기..
GameManager.instance.GetGold(sellClay.sellPrice); // 돈 얻기
GameManager.instance.poolManager.curClayNum--; // 현재 점토 개수 -1 해주기
PlayEffect(effectIdx);
}
else
{
// 안내 문구 시작
GameManager.instance.StartInfoPanel("성체가 된 점토만 판매할 수 있어요!");
return;
}
}
}
3. PlayEffect(int idx)
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();
}
2.5 Clay 스크립트 변경 사항 설명
1. 변수
public int effectIdx = 0;
2. OnMouseDown()
기존 코드를 LevelUpClay() 메서드로 만들어서 보기에 편하게 했다.
// 점토가 터치되면 저절로 호출됨
private void OnMouseDown()
{
prevPos = transform.position; // 드래그 하기 전 위치 저장
poolManager.curClay = gameObject.GetComponent<Clay>(); // 현재 선택된 클레이 지정
Debug.Log("터치됨!");
anim.SetTrigger("doTouch");
GameManager.instance.GetLove(loves[clayLevel - 1] * GameManager.instance.clayClickLevel); // 레벨에 맞는 수치를 함수로 넘겨주기
curTouchCnt++;
LevelUpClay();
}
3. LevelUpClay()
점토가 레벨업 할 때 호출되는 메서드이다. 기존 코드에서 PlayEffect 메서드를 호출하는 부분만 추가되었다.
private void LevelUpClay()
{
// 이미 점토의 레벨이 최고 레벨에 도달했으면 밑으로 진입 안 하도록..
if (clayLevel != 5)
{
if (curTouchCnt == touchCnts[clayLevel - 1])
{
clayLevel++; // 레벨 1 증가
curTouchCnt = 0; // 초기화
anim.runtimeAnimatorController = animators[clayLevel - 1]; // 레벨에 맞는 애니메이터로 바꿔주기
PlayEffect(effectIdx);
if (clayLevel == 5)
gameObject.GetComponent<SpriteRenderer>().sprite = animal;
}
}
}
4. PlayEffect(int idx)
private void PlayEffect(int idx)
{
if (GameManager.instance.effects[idx].gameObject.activeSelf == false)
GameManager.instance.effects[idx].gameObject.SetActive(true); // 활성화하기
GameManager.instance.effects[idx].transform.position = transform.position; // 이펙트 위치를 점토 위치로 바꿔주기
GameManager.instance.effects[idx].Play(); // 실행시키깅
}
2.6 UpgradePanel 스크립트 변경 사항 설명
1. 변수
[Header("Effect")]
public int effectIdx = 3;
2. UpgradeHouse() & UpgradeClick()
PlayEffect 메서드를 호출하는 부분이 추가되었다.
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
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();
}
3. PlayEffect(int idx)
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();
}
3. 결과물
4. 참고자료
생각보다 파티클때문에 시간을 많이 허비했다... UI 밑으로 파티클이 깔려서 해금 될 때 이펙트가 제대로 안보여서 정말 스트레스 받았다... 결국 나는 하이어라키 창에서 캔버스를 GameManager 위로 올려서 해결하긴 했는데 좋은 글들이 많아서 가져와봤다.
4.1 파티클 시스템
4.2 파티클이 UI 위로 가게 하는 법
Unity 2018 - Game VFX - UI / User Interface Effects
UI 위에 파티클 이펙트 뿌리기 Particle Effect on UI | 유니티 UGUI : 네이버 블로그