유니티 RPG - 30. 캐릭터 정보창 만들기 - GUI
Intro
이번포스팅에서는 내 캐릭터의 정보를 볼 수 있는 캐릭터 정보창을 만들어보았습니다.
GUI는 위와같이 설계했습니다.
- EquipmentUI : 마우스 이벤트와 장비슬롯들을 관리하는 클래스
- EquipmentSlotUI : 개별 장비슬롯을 관리하는 클래스
- StatTextUI : 플레이어 데이터를 읽어와 텍스트를 표시하는 클래스
GUI 만들기
캐릭터 정보창의 GUI는 아래와같이 만들었습니다.
- UI를 마우스 드래그로 움직일수 있도록 Header Area에 MovableHeaderUI 클래스를 재사용했습니다.
플레이어 데이터 추가
플레이어 데이터에는 현재 공격력과 방어력이 없습니다.
따라서 플레이어 데이터에 두가지 데이터를 추가했습니다.
// PlayerDataDTO.cs
[System.Serializable]
public class PlayerDataDTO
{
public List<StatusDTO> Status;
[System.Serializable]
// 스탯 정보
public class StatusDTO
{
public float maxHp; // 최대체력
public float curHp; // 현재체력
public float maxMp; // 최대마나
public float curMp; // 현재마나
public float speed; // 이동속도
public float rotateSpeed; // 회전속도
public float damage; // 기본 공격력
public float armor; // 기본 방어력
}
}
// PlayerData.cs
public class PlayerData
{
[SerializeField] private float maxHp;
[SerializeField] private float curHp;
[SerializeField] private float maxMp;
[SerializeField] private float curMp;
[SerializeField] private float speed;
[SerializeField] private float rotateSpeed;
[SerializeField] private float damage;
[SerializeField] private float armor;
public float MaxHp => maxHp;
public float CurHp => curHp;
public float MaxMp => maxMp;
public float CurMp => curMp;
public float Speed => speed;
public float RotateSpeed => rotateSpeed;
public float Damage => damage;
public float Armor => armor;
// 생성자 - Status 초기화
public PlayerData(PlayerDataDTO.StatusDTO dto)
{
this.maxHp = dto.maxHp;
this.curHp = dto.curHp;
this.maxMp = dto.maxMp;
this.curMp = dto.curMp;
this.speed = dto.speed;
this.rotateSpeed = dto.rotateSpeed;
this.damage = dto.damage;
this.armor = dto.armor;
}
}
// PlayerData.json
{
"Status": [
{
"maxHp": 1000.0,
"curHp": 1000.0,
"maxMp": 1000.0,
"curMp": 1000.0,
"speed": 3.0,
"rotateSpeed": 10.0,
"damage": 5.0,
"armor": 0
}
]
}
StatTextArea
StatTextArea 클래스는 DataManager를 통해 플레이어의 데이터를 받아와서 텍스트로 표시합니다.
// StatTextArea.cs
public class StatTextUI : MonoBehaviour
{
[Header("Connected Texts")]
[SerializeField] private Text DamageText;
[SerializeField] private Text HpText;
[SerializeField] private Text SpeedText;
[SerializeField] private Text ArmorText;
private void Update()
{
DamageText.text = string.Format("{0}", Mathf.FloorToInt(DataManager.Instance.GetPlayerData().Damage));
HpText.text = string.Format("{0}", Mathf.FloorToInt(DataManager.Instance.GetPlayerData().CurHp));
SpeedText.text = string.Format("{0}%", Mathf.RoundToInt(DataManager.Instance.GetPlayerData().Speed + 100));
ArmorText.text = string.Format("{0}", Mathf.FloorToInt(DataManager.Instance.GetPlayerData().Armor));
}
}
에디터의 인스펙터창에서 각 텍스트 오브젝트를 연결시켜주어야합니다.
EquipmentSlotUI
EquipmentSlotUI 클래스는 인벤토리에서와같이 장비슬롯에 마우스가 올라갈 때 강조효과가 발생하도록 했습니다.
대부분의 코드를 재사용했습니다.
public class EquipmentSlotUI : MonoBehaviour
{
[SerializeField] private Image iconImage; // 아이템 아이콘 이미지
[SerializeField] private Image highlightImage; // 하이라이트 이미지
#region ** Fields **
private EquipmentUI equipmentUI;
private GameObject highlightGo;
private GameObject iconGo;
private RectTransform highlightRect;
private float maxHighlightAlpha = 0.5f; // 하이라이트 이미지 최대 알파값
private float currentHighlightAlpha = 0f; // 현재 하이라이트 이미지 알파값
private float highlightFadeDuration = 0.2f; // 하이라이트 소요시간
#endregion
private void Awake()
{
Init();
}
private void Init()
{
equipmentUI = GetComponentInParent<EquipmentUI>();
highlightGo = highlightImage.gameObject;
highlightImage.raycastTarget = false;
highlightRect = highlightImage.rectTransform;
highlightGo.SetActive(false);
}
// 하이라이트 이미지를 상/하단으로 표시
public void SetHighlightOnTop(bool value)
{
if (value)
highlightRect.SetAsLastSibling();
else
highlightRect.SetAsFirstSibling();
}
// 슬롯 하이라이트 표시 및 해제
public void Highlight(bool show)
{
if (show)
StartCoroutine(nameof(HighlightFadeIn));
else
StartCoroutine(nameof(HighlightFadeOut));
}
#region ** Coroutines **
// 하이라이트 Fade-in
private IEnumerator HighlightFadeIn()
{
// 실행중인 fade-out 멈추기
StopCoroutine(nameof(HighlightFadeOut));
// 하이라이트 이미지 활성화
highlightGo.SetActive(true);
float timer = maxHighlightAlpha / highlightFadeDuration;
// 하이라이트 이미지 알파값을 서서히 증가시키기
for (; currentHighlightAlpha <= maxHighlightAlpha; currentHighlightAlpha += timer * Time.deltaTime)
{
highlightImage.color = new Color(
highlightImage.color.r,
highlightImage.color.g,
highlightImage.color.b,
currentHighlightAlpha
);
yield return null;
}
}
// 하이라이트 Fade-out
private IEnumerator HighlightFadeOut()
{
StopCoroutine(nameof(HighlightFadeIn));
float timer = maxHighlightAlpha / highlightFadeDuration;
// 하이라이트 이미지 알파값을 서서히 감소시키기
for (; currentHighlightAlpha >= 0f; currentHighlightAlpha -= timer * Time.deltaTime)
{
highlightImage.color = new Color(
highlightImage.color.r,
highlightImage.color.g,
highlightImage.color.b,
currentHighlightAlpha
);
yield return null;
}
highlightGo.SetActive(false);
}
#endregion
}
EquipmentUI
EquipmentUI 클래스는 마우스 이벤트를 구현하고, 각 장비슬롯들을 저장할 리스트를 가지고있습니다.
public class EquipmentUI : MonoBehaviour
{
[Tooltip("캐릭터 장비 슬롯")]
public List<EquipmentSlotUI> slotUIList = new List<EquipmentSlotUI>();
#region ** Fields **
private GraphicRaycaster gr;
private PointerEventData ped;
private List<RaycastResult> rrList;
private EquipmentSlotUI pointerOverSlot; // 현재 마우스 포인터가 위치한 곳의 슬롯
private EquipmentSlotUI beginDragSlot; // 마우스 드래그를 시작한 슬롯
private Transform beginDragIconTransform; // 마우스 드래그를 시작한 슬롯의 위치
#endregion
#region ** 유니티 이벤트 함수 **
private void Awake()
{
Init();
}
private void Update()
{
ped.position = Input.mousePosition;
OnPointerEnterAndExit();
}
#endregion
#region ** Private Methods **
// 초기화
private void Init()
{
TryGetComponent(out gr);
if (gr == null)
gr = gameObject.AddComponent<GraphicRaycaster>();
ped = new PointerEventData(EventSystem.current);
rrList = new List<RaycastResult>(10);
}
#endregion
#region ** 마우스 이벤트 함수들 **
// 마우스 커서가 UI 위에 있는지 여부
private bool IsOverUI() => EventSystem.current.IsPointerOverGameObject();
// 레이캐스팅한 첫 UI요소의 컴포넌트를 가져오기
private T RaycastAndgetFirstComponent<T>() where T : Component
{
// RaycastResult 초기화
rrList.Clear();
// 현재 마우스 위치에서 감지된 UI요소 저장
gr.Raycast(ped, rrList);
// 없으면
if (rrList.Count == 0)
return null;
// 첫번째 UI의 컴포넌트 반환
return rrList[0].gameObject.GetComponent<T>();
}
// 마우스 올라갈때 나갈때 처리
private void OnPointerEnterAndExit()
{
// 이전 프레임 슬롯
var prevSlot = pointerOverSlot;
// 현재 프레임 슬롯
var curSlot = pointerOverSlot = RaycastAndgetFirstComponent<EquipmentSlotUI>();
// 마우스 올라갈 때
if(prevSlot == null)
{
if(curSlot != null)
{
OnCurrentEnter();
}
}
// 마우스 나갈 때
else
{
if (curSlot == null)
{
OnPrevExit();
}
// 다른 슬롯으로 커서 옮길때
else if (prevSlot != curSlot)
{
OnPrevExit();
OnCurrentEnter();
}
}
void OnCurrentEnter()
{
curSlot.Highlight(true);
}
void OnPrevExit()
{
prevSlot.Highlight(false);
}
}
#endregion
}
댓글남기기