| 작업 목표 📋
1. 픽셀 아트를 플레이 하면서 복원을 모두 완료 시켰을 때, 도감리스트에 추가하는 도감 시스템 구현할 예정입니다.
2. 그동안 미뤄뒀던 이어하기 기능을 구현 할 예정 입니다.
| TO DO LIST 💡
- 도감 UI 구성
- 수집된 토픽 슬롯 UI
- 수집된 픽셀 아트 슬롯 UI
- 수집된 픽셀 아트 데이터 정보 팝업 UI
- 도감 추가 기능 구현
- CollectedTopicData, CollectedPixelArtData 추가
- 복원이 완료 된 PixelArtData 도감 반영
- 도감 씬 내 수집 토픽, 픽셀 아트 슬롯 생성
- 슬롯 내 데이터. 정보 팝업 UI 업데이트 구현
- 이어하기 기능 구현
| 작업 과정 🖥️
=> 도감 UI 구성
우선 추가한 도감 씬 전체 UI 입니다.
아직 UI 디자인을 적용 시키지 않고 구조만 잡은 모습 입니다.
| 수집된 토픽 슬롯 UI
수집된 토픽 데이터(CollectedTopicData)를 포함하며 아래와 같은 요소들을 담고 있습니다.
- 타이틀
- 복원 완료된 픽셀 아트의 수
- 해당 토픽의 추가되어 있는 전체 픽셀 아트의 수
- 복원 완료된 픽셀 아트 슬롯들
| 수집된 픽셀 아트 슬롯 UI
수집된 토픽 데이터(CollectedPixelArtData)를 포함하며 아래와 같은 요소들을 담고 있습니다.
- 타이틀
- 썸네일 이미지
| 수집된 픽셀 아트 데이터 정보 팝업 UI
클릭한 수집 픽셀 아트 슬롯을 누르면 해당 픽셀 아트의 대한 정보를 알려주는 팝업 UI입니다.
- 타이틀
- 썸네일
- 설명
=> 기능 구현 / CollectedTopicData, CollectedPixelArtData 추가
수집된 도감과 복원이 완료된 픽셀 아트의 경우 기존 TopicData와 PixelArtData의 데이터들이 모두 필요가 없기 때문에 수집된 정보를 별도로 관리 할 CollectedTopicData, CollectedPixelArtData 클래스를 추가 하였습니다.
- CollectedTopicData
- ID : 수집 토픽 데이터의 식별 값
- TItle: 수집 토픽의 타이틀
- Description: 수집 토픽의 설명
- TotalPixelArtDataCount : 수집 토픽의 전체 픽셀 아트 데이터 수
- CollectedPixelArtDataList : 복구 완료 된 픽셀 아트 데이터 리스트
[FirestoreData, Serializable]
public class CollectedTopicData
{
[FirestoreProperty] public string ID { get; private set; }
[FirestoreProperty] public string Title { get; private set; }
[FirestoreProperty] public string Description { get; private set; }
[FirestoreProperty] public int TotalPixelArtDataCount { get; private set; }
[FirestoreProperty] public List<CollectedPixelArtData> CollectedPixelArtDataList { get; private set; }
public CollectedTopicData()
{
ID = "";
Title = "";
Description = "";
TotalPixelArtDataCount = 0;
CollectedPixelArtDataList = new List<CollectedPixelArtData>();
}
public CollectedTopicData(string id, string title, string description, int totalPixelArtDataCount)
{
ID = id;
Title = title;
Description = description;
TotalPixelArtDataCount = totalPixelArtDataCount;
CollectedPixelArtDataList = new List<CollectedPixelArtData>();
}
}
- CollectedPixelArtData
- ID : 복구 완료 픽셀 아트 데이터의 식별 값
- ThumbnailData: 픽셀 아트의 썸네일 데이터
- Description : 픽셀 아트 설명
- ThumbnailSize : 썸네일 사이즈
[FirestoreData, Serializable]
public class CollectedPixelArtData
{
[FirestoreProperty] public string Title { get; private set; }
[FirestoreProperty] public string ThumbnailData { get; private set; }
[FirestoreProperty] public string Description { get; private set; }
[FirestoreProperty] public int ThumbnailSize { get; private set; }
public CollectedPixelArtData()
{
Title = "";
ThumbnailData = "";
Description = "";
ThumbnailSize = 0;
}
public CollectedPixelArtData(string title, string thumbnailData, string description, int thumbnailSize)
{
Title = title;
ThumbnailData = thumbnailData;
Description = description;
ThumbnailSize = thumbnailSize;
}
}
| 복원이 완료 된 PixelArtData 도감 반영
FillRandomPixel 메서드에 남은 픽셀 (복원 픽셀 수)가 없는 경우 완료 처리 하는 로직을 추가하였습니다.
// ColorMatchSystem.cs
private void FillRandomPixel()
{
...
if (_pixelArtData.PixelColorData.RemainingPixels == 0)
{
_pixelArtData.IsCompleted = true;
}
}
전반적인 게임 진행을 관리하는 GameManager 클래스를 추가 후
게임 플레이가 끝났을 때 픽셀 아트의 완료 유무를 체크 후 CollectedTopic의 CollectedPixelArtDataList 에 추가하는 CollectPixelArt 메서드를 구현하여 최종적으로 도감 데이터에 추가 하였습니다.
// GameManager.cs
private IEnumerator Playing()
{
while (!colorMatchSystem.IsGameOver())
{
yield return _timerDelay;
_playTime++;
playUI.UpdatePlayTime(_playTime);
}
playUI.HideUI();
if (_selectPixelArtData.IsCompleted)
{
CollectPixelArt();
}
SaveData();
}
private void CollectPixelArt()
{
_selectTopicData.CompleteCount++;
if (_selectTopicData.CompleteCount == _selectTopicData.TotalCount)
{
_selectTopicData.Complete = true;
}
_selectPixelArtData.PlayTime = _playTime;
CollectedTopicData curCollectTopicData = DataManager.userData.CollectedTopicDataList.Find((collectTopic) => collectTopic.ID == _selectTopicData.ID);
CollectedPixelArtData newCollectPixelArtData = new CollectedPixelArtData(_selectPixelArtData.Title, _selectPixelArtData.ThumbnailData, _selectPixelArtData.Description, _selectPixelArtData.Size);
curCollectTopicData.CollectedPixelArtDataList.Add(newCollectPixelArtData);
}
| 도감 씬 내 수집 토픽, 픽셀 아트 슬롯 생성
도감씬의 UI를 담당할 CollectUI 클래스를 추가한 후 수집된 토픽 데이터 슬롯과 픽셀 아트 슬롯들을 생성 하는 SetSlot메서드를 구현 하였습니다.
public class CollectUI : BodyUI
{
[SerializeField] private Transform genCollectTopicSlotParent;
[SerializeField] private CollectTopicSlot collectTopicSlotPrefab;
[SerializeField] private CollectPixelArtSlot collectPixelArtSlotPrefab;
...
private void SetSlot()
{
List<CollectedTopicData> curCollectTopicDataList = DataManager.userData.CollectedTopicDataList;
foreach (CollectedTopicData collectedTopicData in curCollectTopicDataList)
{
CollectTopicSlot newCollectTopicSlot = Instantiate(collectTopicSlotPrefab, genCollectTopicSlotParent);
newCollectTopicSlot.SetSlot(collectedTopicData);
foreach (CollectedPixelArtData collectedPixelArtData in collectedTopicData.CollectedPixelArtDataList)
{
CollectPixelArtSlot newCollectPixelArtSlot = Instantiate(collectPixelArtSlotPrefab, newCollectTopicSlot.GetCollectPixelArtParent());
newCollectPixelArtSlot.SetSlot(collectedPixelArtData);
newCollectPixelArtSlot.OnClick += CollectPixelArtSlotClickHandler;
}
}
}
}
| 수집 픽셀 아트 데이터 정보 팝업 UI 업데이트 구현
이제 도감 씬 내에 생성된 픽셀 아트 슬롯을 눌렀을 때 해당 픽셀 아트의 정보를 팝업 UI에 업데이트 시키기 위해
CollectPixelArtSlot 클래스 내에 아래와 같은 작업을 진행 하였습니다.
- CollectPixelArtSlotClickHandle 델리게이트 추가
- CollectPixelArtSlotClickHandle OnClick 이벤트 추가
- OnSlotClick() 메서드 구현
public class CollectPixelArtSlot : MonoBehaviour
{
...
public delegate void CollectPixelArtSlotClickHandler(CollectedPixelArtData collectedPixelArtData);
public event CollectPixelArtSlotClickHandler OnClick;
public void OnSlotClick()
{
OnClick?.Invoke(_collectPixelArtData);
}
public void SetSlot(CollectedPixelArtData collectedPixelArtData)
{
_collectPixelArtData = collectedPixelArtData;
titleText.text = $"{_collectPixelArtData.Title}";
thumbnailImage.sprite = PixelArtHelper.MakeThumbnail(_collectPixelArtData.ThumbnailData, _collectPixelArtData.ThumbnailSize);
}
}
마지막으로 CollectUI 클래스 내에 CollectPixelArtSlotClickHandler 구현을 통해 전체적인 기능 구현을 마무리 했습니다.
public class CollectUI : BodyUI
{
[SerializeField] private CollectPixelArtDetailPopup collectPixelArtDetailPopup;
...
private void CollectPixelArtSlotClickHandler(CollectedPixelArtData collectedPixelArtData)
{
collectPixelArtDetailPopup.SetPopup(collectedPixelArtData);
collectPixelArtDetailPopup.gameObject.SetActive(true);
}
}
| 이어하기 기능 구현
메인 씬의 플레이 버튼을 통해 이어하기를 진행 할 수 있도록 MainPlayButton 클래스를 아래와 같이 구현 하였습니다.
간단하게 동작을 우선하여 구현한 코드라 다소 난잡하긴 합니다.
public class MainPlayButton : MonoBehaviour
{
[SerializeField] private Button playButton;
[SerializeField] private Image buttonImage;
[SerializeField] private TMP_Text buttonText;
private TopicData _lastTopicData;
private PixelArtData _lastPixelArtData;
private async void Start()
{
LoadData();
ConfigurePlayButton();
await UpdateUserDataOnFirebase();
}
private void LoadData()
{
var selectTopicID = DataManager.userData.SelectTopicID;
if (string.IsNullOrEmpty(selectTopicID))
{
SetButtonInactive("토픽 선택");
return;
}
_lastTopicData = DataManager.LoadJsonData<TopicData>(DataPath.GalleryDataPath, selectTopicID);
_lastPixelArtData = _lastTopicData?.PixelArtDatas.Find(pixelArtData => pixelArtData.ID == DataManager.userData.SelectPixelArtID);
}
private async Task UpdateUserDataOnFirebase()
{
#if UNITY_ANDROID && !UNITY_EDITOR
string FUID = FirebaseManager.ins.FireAuth.FUID;
#else
string FUID = "Test";
#endif
await FirebaseManager.ins.Firestore.UpdateData(FirestoreCollections.UserData, FUID, DataManager.userData);
}
private void ConfigurePlayButton()
{
if (_lastTopicData == null)
{
playButton.interactable = false;
return;
}
if (_lastPixelArtData.IsCompleted)
{
DataManager.userData.SelectPixelArtID = string.Empty;
ConfigurePlayButtonForCompletedPixelArt();
}
else
{
ConfigurePlayButtonForIncompletePixelArt();
}
}
private void ConfigurePlayButtonForCompletedPixelArt()
{
if (_lastTopicData.Complete)
{
DataManager.userData.SelectTopicID = string.Empty;
SetButtonInactive("토픽 선택");
}
else
{
buttonText.text = _lastTopicData.Title;
buttonImage.gameObject.SetActive(false);
SetButtonActive(SceneManager.LoadScene, SceneNames.GalleryScene);
}
}
private void ConfigurePlayButtonForIncompletePixelArt()
{
buttonImage.sprite = PixelArtHelper.MakeThumbnail(_lastPixelArtData.ThumbnailData, _lastPixelArtData.Size);
buttonText.text = "이어하기";
SetButtonActive(SceneManager.LoadScene, SceneNames.PlayScene);
}
private void SetButtonActive(Action<string> action, string argument)
{
playButton.interactable = true;
playButton.onClick.AddListener(() => action(argument));
}
private void SetButtonInactive(string text)
{
buttonImage.gameObject.SetActive(false);
buttonText.text = text;
playButton.interactable = false;
}
}
| 작업 결과 ✅
| 다음에 작업할 내용 🕟︎
지금 까지 다양한 데이터 클래스들을 Firestore와 로컬을 통해 관리, 핸들링을 하면서 문제점과 좀 더 구조를 개선 시켜야 할 필요성을 느끼게 되어 다음엔 이를 작업할 예정입니다.