[Unity 강의] 뱀서라이크 강의 - UI
카테고리: Class VamSurLike
태그: C#, Unity, VamSurLike
1. UI
✔ UI배치, 몬스터 처치 시 UI변경, 경험치 Slider 변경, 레벨업 시 Popup표시
UI작업 시 체크
✔ 클릭 문제 시 모든 UI Raycast Target 확인해 보기
✔ UI Sort Order 무언가 가려져있어서 클릭이 안되는지
✔ EventSystem 유 무 확인.
✔ UI 크기 조정 - UI Scale Mode - Scale With Screen Size
화면크기 바꾸면서 확인해 보기. - 늘어나고 줄어들 때 잘 배치되도록 설계하기
✔ 코드랑 연동했을 때 무엇을 고쳐야 하나?
✔ UI 작업자의 의도 확인, 관찰하기
✔ 다국어 생각
✔ 텍스트 부분 - 코드에서 수정하게
✔ UI Prefab - Script 1:1 대응되도록 하나의 스크립트로 내부 ui 관리
✔ 항상 켜져 있는 UI, Popup UI 구분
✔ POP UP - Stack으로 키고 끄는 관리
2. UI 코드
✔ UI에서 게임 중 변화되는 부분 확인, 체크
✔ 컴포넌트 어떤 부분을 변경해야 바뀌는지 확인
✔ 코드에서 [SerializeField]로 가져오기
✔ 컴포넌트 설정 변경
✔ 버튼 - ONClick() 사용
✔ UI 갱신 - event 콜백으로
🔹UIManager UI_Base - UI 관리
🔹UI_GameScene - 게임 기본 정보들 (Gem, kill)
🔹UI_SkillSelectPopup - 레벨 업 시 팝업(다음강의)
🔹UI_SkillCardItem - 스킬정보
UIManager
✔ UI들을 보여주고, Popup 을 Stack을 이용해 관리
✅ ShowSceneUI - UI_GameScene(유지되는UI 캐릭터 정보) 를 Instantiate.
✅ ShowPopup - Popup UI 보여줌.
✅ ClosePopup - popup 끄기
✅ RefreshTimeScale() - popup 시 시간 멈춤, 다시 진행
UIManager
public class UIManager
{
UI_Base _sceneUI;
Stack<UI_Base> _uiStack = new Stack<UI_Base>();
public T ShowSceneUI<T>() where T : UI_Base
{
if (_sceneUI != null)
return GetSceneUI<T>();
string key = typeof(T).Name + ".prefab";
T ui = Managers.Resource.Instantiate(key, pooling: true).GetOrAddComponent<T>();
_sceneUI = ui;
return ui;
}
public T GetSceneUI<T>() where T : UI_Base
{
return _sceneUI as T;
}
public T ShowPopup<T>() where T : UI_Base
{
string key = typeof(T).Name + ".prefab";
T ui = Managers.Resource.Instantiate(key, pooling: true).GetOrAddComponent<T>();
_uiStack.Push(ui);
RefreshTimeScale();
return ui;
}
public void ClosePopup()
{
if (_uiStack.Count == 0)
return;
UI_Base ui = _uiStack.Pop();
Managers.Resource.Destroy(ui.gameObject);
RefreshTimeScale();
}
public void RefreshTimeScale()
{
if (_uiStack.Count > 0)
Time.timeScale = 0;
else
Time.timeScale = 1;
}
}
UI_GameScene
✔ ui에서 게임 중 변화되는 부분 확인, 체크
✔ 킬 수, GEM Slider 변경
✔ [SerializeField] 사용
UI_GameScene
public class UI_GameScene : UI_Base
{
[SerializeField]
TextMeshProUGUI _killCountText;
[SerializeField]
Slider _gemSlider;
public void SetGemCountRatio(float ratio)
{
_gemSlider.value = ratio;
}
public void SetKillCount(int killCount)
{
_killCountText.text = $"{killCount}";
}
}
3. UI 갱신(Update)
✔ PlayerController에서 Gem 얻을 때 데이터 변경
✔ MonsterController에서 Ondead - Kill 수 변경
✔ UI 갱신 - event 콜백으로
🔹GameManager UI에 사용될 데이터 관리, Action, Set 콜백으로 변경될 때 실행
🔹GameScene - 이벤트 구독, 이벤트 발생 시 실행 함수(Handler)
GameManager
✔ gem, killCount관리, 변경 시 이벤트를 발생
✔ 프로퍼티 set 에서 이벤트 호출
GameManager
public class GameManager
{
int _gem = 0;
public event Action<int> OnGemCountChanged;
public int Gem
{
get { return _gem; }
set
{
_gem = value;
OnGemCountChanged?.Invoke(value);
}
}
#endregion
#region 전투
int _killCount;
public event Action<int> OnkillCountChanged;
public int KillCount
{
get { return _killCount; }
set
{
_killCount = value; OnkillCountChanged?.Invoke(value);
}
}
#endregion
}
GameScene
✔ UI_GameScenet 생성
✔ 킬 수, 젬 이벤트 구독
✔ 이벤트 발생 시 실행 함수(Handler)
GameScene
public class GameScene : MonoBehaviour
{
void StartLoaded()
{
Managers.UI.ShowSceneUI<UI_GameScene>();
Managers.Game.OnkillCountChanged -= HandleOnkillCountChanged;
Managers.Game.OnkillCountChanged += HandleOnkillCountChanged;
Managers.Game.OnGemCountChanged -= HandleOnGemCountChanged;
Managers.Game.OnGemCountChanged += HandleOnGemCountChanged;
}
int _collectedGemCount = 0;
int _remainingTotalGemCount = 10;
public void HandleOnGemCountChanged(int gemCount)
{
_collectedGemCount++;
if (_collectedGemCount == _remainingTotalGemCount)
{
Managers.UI.ShowPopup<UI_SkillSelectPopup>();
_collectedGemCount = 0;
_remainingTotalGemCount *= 2;
}
Managers.UI.GetSceneUI<UI_GameScene>().SetGemCountRatio((float)_collectedGemCount / _remainingTotalGemCount);
}
public void HandleOnkillCountChanged(int killCount)
{
Managers.UI.GetSceneUI<UI_GameScene>().SetKillCount(killCount);
if (killCount==5)
{
//보스? 컨텐츠
}
}
private void OnDestroy()
{
if (Managers.Game != null)
{
Managers.Game.OnGemCountChanged -= HandleOnGemCountChanged;
Managers.Game.OnGemCountChanged -= HandleOnGemCountChanged;
}
}
}
Player, Monster
✔ 데이터 변경 시점
✅ PlayerController - CollectEnv() - Managers.Game.Gem += 1;
✅ MonsterController - OnDead() - Managers.Game.KillCount++;
P,M Controller
public class PlayerController : CreatureController
{
void CollectEnv()
{
float sqrCollectDis = EnvCollectDist * EnvCollectDist;
var findGems = GameObject.Find("@Grid").GetComponent<GridController>().GatherObjects(transform.position, EnvCollectDist + 0.4f);
foreach (var go in findGems)
{
GemController gem = go.GetComponent<GemController>();
Vector3 dir = gem.transform.position - transform.position;
if (dir.sqrMagnitude <= EnvCollectDist)
{
//✅
Managers.Game.Gem += 1;
Managers.Object.Despawn(gem);
}
}
}
}
public class MonsterController : CreatureController
{
protected override void OnDead()
{
base.OnDead();
//✅
Managers.Game.KillCount++;
if (_coDotDamage != null)
StopCoroutine(_coDotDamage);
_coDotDamage = null;
//죽을 때 보석 스폰
GemController gc = Managers.Object.Spawn<GemController>(transform.position);
Managers.Object.Despawn(this);
}
}
이것저것 메모
UI - Data
UI 코드에서 데이터를 넣지 말자. 데이터랑 UI 분리하기.
지금 프로젝트에선 GameManager에서 데이터를 관리하지만
UI에서 쓰는 데이터를 따로 클래스를 만들어서 관리할 수도 있다. UI-Data 1:1
popup시 시간정지
RefreshTimeScale()
UI갱신 흐름 정리
1. Player,MonsterController에서 GameManager의 데이터 변경 시도.
2. GameManager - 데이터의 set 부분의 구독된 이벤트들 호출
3. GameScene에서 미리 구독된 Handler의 실행
4. 데이터에 맞게 UI 변경
잡담, 일기?
큰 규모의 UI - 다른 방법, 실수를 줄이는 방법 알아보기.
다음 시간 - 레벨 업 팝업 수정
댓글남기기