[멋쟁이사자처럼 부트캠프 TIL회고] 73일차 : 퀴즈 게임
오늘 배운 것
1. 자투리 지식
2. 퀴즈 게임 제작기
1. 자투리 지식
UI 앵커 관련 - 스트레치와 미들 센터 앵커
문제 : UI가 보이지 않았음
해결 : 부모의 RectTransform 변경
부모가 자식을 담을 만큼 충분히 크지 못해 발생한 문제였다. 부모의 크기를 늘려주면 해결 !
* 참고 : 스트레치 앵커 : 부모의 RectTransform 영역에 맞춰 UI 요소를 늘려 채우는 방식
미들 센터 앵커 : UI 요소의 중심점을 부모 RectTransform의 중심점에 맞추는 방식
그러니까 UI 요소를 늘려 채우면 크기가 늘어나서 해결 !
미들 센터 앵커는 중심점만을 이동시키기에 해결되지 않는다.
다만 이것 저것 실험해 보던 중 신기한 현상이 있었다.
분명 부모가 자식보다 크기가 작아 처음엔 보이지 않다가, 부모의 크기를 변경만 해 주면 (여전히 자식보다 작을지라도)
그 크기에 맞게 자식의 크기가 조절되는 것이었다.
즉, 유니티에서 RectTransform이 변경될 때 내부적으로 레이아웃 리빌드가 발생해 이런 현상이 있었다.
2. SetParent 관련 부분
위와 같은 문제를 해결하기 위해 다른 방식을 쓸 수도 있었다.
SetParent의 두 번째 매개변수인 bool worldPositionStatys였다.
그러니까 위치를 유지할 것이냐 ! 라는 것이다.
기본값은 true로써 오브젝트의 위치는 기본적으로 유지된다.
이걸 false로 바꿔주면 부모의 위치에 따라 오브젝트의 위치가 조정된다.
2. 퀴즈 게임 제작기
공용 오브젝트 풀 제작
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
public class ObjectPool : MonoBehaviour
{
[SerializeField] private GameObject prefab;
[SerializeField] int poolSize;
private Queue<GameObject> _pool;
private static ObjectPool _instance;
// 이 정도로 싱글톤을 활용 ?
public static ObjectPool Instance
{
get { return _instance; }
}
private void Awake()
{
_instance = this;
_pool = new Queue<GameObject>();
for (int i = 0; i < poolSize; i++)
{
CreateNewObject();
}
}
/// <summary>
/// Object Pool 에 새로운 오브젝트 생성
/// </summary>
private void CreateNewObject()
{
GameObject newObject = Instantiate(prefab);
newObject.SetActive(false);
_pool.Enqueue(newObject);
}
/// <summary>
/// 오브젝트 풀에 있는 오브젝트 반환 메서드
/// </summary>
/// <returns>오브젝트 풀에 있는 오브젝트</returns>
public GameObject GetObject()
{
if (_pool.Count == 0) CreateNewObject();
GameObject newObject = _pool.Dequeue();
newObject.SetActive(true);
return newObject;
}
/// <summary>
/// 사용한 오브젝트를 오브젝트 풀로 되돌려 주는 메서드
/// </summary>
/// <param name="card">반환할 오브젝트</param>
public void RuturnObject(GameObject card)
{
card.SetActive(false);
_pool.Enqueue(card);
}
}
게임패널컨트롤러 제작( 카드를 하나씩 넘기는 느낌을 주기 위한)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
using UnityEngine.UI;
public class GamePanelController : MonoBehaviour
{
[SerializeField] private GameObject quizCardPrefab; // Quiz Card Prefab
[SerializeField] private Transform quizCardParent; // Quiz Card가 표시될 UI Parent
private List<GameObject> quizCards;
private Transform _quizCardsPos1;
private Transform _quizCardsPos2;
private Transform _quizCardsPos3;
private void Start()
{
InitQuizCards();
}
private void InitQuizCards()
{
var firstCardObject = ObjectPool.Instance.GetObject();
var secondCardObject = ObjectPool.Instance.GetObject();
var thirdCardObject = ObjectPool.Instance.GetObject();
firstCardObject.GetComponent<Image>().color = Color.green;
secondCardObject.GetComponent<Image>().color = Color.red;
thirdCardObject.GetComponent<Image>().color = Color.blue;
quizCards = new List<GameObject>();
quizCards.Add(firstCardObject);
quizCards.Add(secondCardObject);
quizCards.Add(thirdCardObject);
quizCards[0].transform.localPosition = new Vector3(-50f, 0f, -1f);
quizCards[1].transform.localPosition = new Vector3(0f, 0f, 0f);
quizCards[2].transform.localPosition = new Vector3(50f, 0f, 1f);
_quizCardsPos1 = quizCards[0].transform;
_quizCardsPos2 = quizCards[1].transform;
_quizCardsPos3 = quizCards[2].transform;
quizCards[0].transform.SetSiblingIndex(2); // 맨 앞으로
quizCards[1].transform.SetSiblingIndex(1); // 중간
quizCards[2].transform.SetSiblingIndex(0); // 맨 뒤로
}
private void CycleQuizCard(List<GameObject> cards)
{
GameObject temp = quizCards[0];
quizCards[0] = quizCards[1];
quizCards[1] = quizCards[2];
quizCards[2] = temp;
quizCards[0].transform.DOMove(_quizCardsPos1.transform.position, 0.5f);
quizCards[1].transform.DOMove(_quizCardsPos2.transform.position, 0.5f);
quizCards[2].transform.DOMove(_quizCardsPos3.transform.position, 0.5f);
quizCards[0].transform.SetSiblingIndex(2); // 맨 앞으로
quizCards[1].transform.SetSiblingIndex(1); // 중간
quizCards[2].transform.SetSiblingIndex(0); // 맨 뒤로
}
public void OnClickNextCard()
{
CycleQuizCard(quizCards);
}
}
문제 1 : Transform VS Vector3
1. 파란색이 계속해서 앞에 있고 위치가 변하지 않음
2. 버튼을 연타하다보면 가운데로 모든 카드가 뭉쳐짐
해결한 코드는 다음과 같다.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
using UnityEngine.UI;
public class GamePanelController : MonoBehaviour
{
[SerializeField] private GameObject quizCardPrefab; // Quiz Card Prefab
[SerializeField] private Transform quizCardParent; // Quiz Card가 표시될 UI Parent
private List<GameObject> quizCards;
private Vector3 _quizCardsPos1;
private Vector3 _quizCardsPos2;
private Vector3 _quizCardsPos3;
private void Start()
{
InitQuizCards();
}
private void InitQuizCards()
{
var firstCardObject = ObjectPool.Instance.GetObject();
var secondCardObject = ObjectPool.Instance.GetObject();
var thirdCardObject = ObjectPool.Instance.GetObject();
firstCardObject.GetComponent<Image>().color = Color.green;
secondCardObject.GetComponent<Image>().color = Color.red;
thirdCardObject.GetComponent<Image>().color = Color.blue;
quizCards = new List<GameObject>();
quizCards.Add(firstCardObject);
quizCards.Add(secondCardObject);
quizCards.Add(thirdCardObject);
_quizCardsPos1 = quizCards[0].transform.position;
_quizCardsPos2 = quizCards[1].transform.position;
_quizCardsPos3 = quizCards[2].transform.position;
quizCards[2].SetActive(false);
}
private void CycleQuizCard(List<GameObject> cards)
{
quizCards[2].SetActive(true);
GameObject temp = quizCards[0];
quizCards[0] = quizCards[1];
quizCards[1] = quizCards[2];
quizCards[2] = temp;
quizCards[0].transform.SetSiblingIndex(2); // 맨 앞으로
quizCards[1].transform.SetSiblingIndex(1); // 중간
quizCards[2].transform.SetSiblingIndex(0); // 맨 뒤로
quizCards[0].transform.DOMove(_quizCardsPos1+ new Vector3(-20f,0,0), 0.5f);
quizCards[1].transform.DOMove(_quizCardsPos2, 0.5f);
quizCards[2].transform.DOMove(_quizCardsPos3+ new Vector3(20f, 0,0), 0.5f)
.OnComplete(() =>
{
quizCards[2].SetActive(false);
});
}
public void OnClickNextCard()
{
CycleQuizCard(quizCards);
}
}
결국 한 가지 잘못된 선택에서 비롯된 문제였다.
바로
private Transform _quizCardsPos1;
private Transform _quizCardsPos2;
private Transform _quizCardsPos3;
Transform으로 저장한 _quizCardsPos를 사용하여 위치를 이동시켜 주었던 것.
Transfrom은 '위치, 회전 , 크기, 부모와의 관계' 등을 포함한 참조 값이다.
따라서 참조시 상태가 계속 변경되면서 변경된 상태를 기준으로 또 이동하게 되고, 버튼을 빨리 누르다 보면 이 값이 점점 수렴하게 되는 것이었다.
그래서 우리가 이렇게
_quizCardsPos1 = quizCards[0].transform;
_quizCardsPos2 = quizCards[1].transform;
_quizCardsPos3 = quizCards[2].transform;
참조 값을 설정했기 때문에, 버튼을 한 번 클릭했을 때
첫 번째 카드는 세번째 카드의 위치를 따라서 정상 이동한다.
두 번째 카드는 첫 번째 카드가 이동중인 위치 중 현재 위치를 따라서 이동한다.
세 번째 카드는 두 번째 카드의 이동중인 위치 중 현재 위치를 따라서 이동한다.
첫 바퀴는 정상적으로 흘러가지만, 두 번째 바퀴에서
두 번째 카드는 현재 맨 앞에 있기 때문에 세 번째 카드의 원래 위치를 향해 이동해야 하지만,
참조 값이기 때문에 세 번째 카드의 '현재 위치'( 두 번째 카드의 이동중이었던 위치) 를 향해 움직이게 된다.
이런 식으로 오류가 발생하게 되고, 버튼을 세 번 누르면 모두 원래의 자리로 돌아오는 것을 알 수 있다.
오류가 발생했던 영상이다.
문제 2 : Local Position VS position
사실 중간에 보면 Transform과 Vector3 이외에도 localPosition을 position으로 고친 것을 볼 수 있다.
참조 문제를 고치고 나니, 왼쪽 아래로 모든 패널들이 이동했었다. 이는 로컬 포지션을 저장해 두어서 그렇다.
패널들의 로컬 포지션은 중심점을 기준으로 잡히므로 0,0,0이 된다. 그런데 우리는 DOMove를 사용했다.
DOMove는 월드 스페이스를 기준으로 이동시키므로 부모 오브젝트와의 관계를 포함하는 local position을 써 버리면
왼쪽 아래(월드 스페이스 기준 0,0,0)으로 이동하게 된다.
이렇게 두고 비교해 보면 그 차이를 더 잘 알 수 있다.
_quizCardsPos1 = quizCards[0].transform.localPosition;
_quizCardsPos2 = quizCards[1].transform.position;
결과물
오늘의 목표
1. 코테 2페이지까지 다 풀기(완료)
2. Attribute 혹은 Reflection 개념 정리