XR플밍 - 12. UnityEngine3D Reactive 프로그래밍 - 기업협약 프로젝트 8일차 (8/27)

이형원·2025년 8월 27일
0

XR플밍

목록 보기
178/215

1. 금일 한 업무

어제자 작업 내용과 같이 PR을 올렸다. 데모 씬에 들어가야 할 필수 작업 내용에 대해 진행했다.

  • 가챠 시스템 구현(UI까지 구현 완료)
  • 편성 시너지 활성화 UI 작업 중간까지 완료

가챠 시스템(UI 효과만 적용 - 데이터베이스 적용 및 획득여부 판정은 x)

편성 시스템에서의 시너지 UI 활성화 - 데모 버전의 게임에서는 해당 기능이 들어가지 않을 예정

2. 문제의 발생과 해결 과정

2.1 가챠 시스템의 구현

가챠 시스템의 틀 자체는 어제 만들어놓기는 했지만, UI 구성까지 직접 해보기로 했다.
연출은 당장 넣지는 않고 UI 출력만 나오게 연출해보기로 했다.

처음에는 1회 연출 가챠와 10회 연출 가챠를 분리해야 한다고 생각했는데, 가만 생각해보니 Grid Layer Group로 하나의 UI로서 처리하는 방식을 떠올렸다.

Middle Center로 설정한 뒤에 1회에는 1개의 UISlot을 활성화하고, 10회에는 10개의 UISlot을 활성화한다.

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class GachaResultUI : MonoBehaviour,IPointerClickHandler
{
    [SerializeField] private GameObject _gachaResultSlotUI;
    [SerializeField] private Transform _content;
    [SerializeField] private Image[] _gachaResultImages;

    private GameObject[] _slots = new GameObject[10];
    
    public void HeroGachaUpdate(UnitData data, int index)
    {
        if (_slots[index] == null)
        {
            _slots[index] = Instantiate(_gachaResultSlotUI, _content);
        }
        GachaResultUISlot slot = _slots[index].GetComponent<GachaResultUISlot>();
        slot.UpdateUI(data.Icon);
    }

    // 비활성화와 동시에 슬롯을 한개만 남겨두고 전부 파괴(Grid UI를 위해서 임시 처리)
    // 오브젝트 풀 반영할 수 있을 것 같습니다 -> 추후 풀 반영
    private void OnDisable()
    {
        if (_slots.Length <= 1) return;
        else
        {
            for (int i = 1; i < _slots.Length; i++)
            {
                Destroy(_slots[i]);
            }
        }
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        gameObject.SetActive(false);
    }
}
using UnityEngine;
using UnityEngine.UI;

public class GachaResultUISlot : MonoBehaviour
{
    [SerializeField] Image _resultImage;

    public void UpdateUI(Sprite sprite)
    {
        _resultImage.sprite = sprite;
    }
}

개선 사항으로는 주석에서 적어놨다시피 오브젝트 풀을 적용할 필요성은 있어 보인다.
또한 연출이 아직 없으며 데이터 연동 및 캐릭터 획득 여부에 대한 부분도 추가해야 한다.

2.2 편성 시스템에서의 시너지 UI 활성화

이 부분은 아무래도 UI 기획이 온전하지 않은 것도 있고, 혼자서 끙끙대면서 코드 분석을 하다가 여러 시행착오를 겪었다. 결국 시스템 자체를 설계한 팀장님한테 맡겨야 하나 싶었는데, 불완전하지만 일단 UI 표시 자체는 되는 방식으로 구현하기는 했다.

이렇게 얘기하긴 했었는데 구조를 알아도 쉽지가 않았다...

  • TeamOrganizeManager 중
#region Synergy

public Dictionary<ClassType, int> GetClassSynergyCount()
{
    Dictionary<ClassType, int> classSynergyCount = new();

    foreach(var unit in _currentPreset)
    {
        if(unit.Data == null) continue;
        if(unit.Data.EnhancementData == null) continue;

        ClassType classSynergy = unit.Data.EnhancementData.ClassSynergy;

        if (!classSynergyCount.ContainsKey(classSynergy))
            classSynergyCount[classSynergy] = 0;

        classSynergyCount[classSynergy]++;
        Debug.Log($"{classSynergy.ToString()} : {classSynergyCount[classSynergy]}");
    }
    
    return classSynergyCount;
}

public Dictionary<Synergy, int> GetSynergyCount()
{
    Dictionary<Synergy, int> synergyCounts = new();

    foreach (var unit in _currentPreset)
    {
        if (unit.Data == null) continue;
        if (unit.Data.EnhancementData == null) continue;

        Synergy synergy = unit.Data.EnhancementData.Synergy;

        if (!synergyCounts.ContainsKey(synergy))
            synergyCounts[synergy] = 0;

        synergyCounts[synergy]++;
    }

    return synergyCounts;
}

#endregion
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class LobbySynergyController : MonoBehaviour
{
    [SerializeField] GameObject _synergySlotPrefab;
    [SerializeField] GameObject _classSynergySlotPrefab;
    [SerializeField] Transform _content;
    [SerializeField] Transform _classContent;

    private Dictionary<string, LobbySynergySlot> _synergySlots = new(50);
    private Dictionary<string, LobbyClassSynergySlot> _classSynergySlots = new(50);

    private TeamOrganizeManager _manager;

    private void Start()
    {
        _manager = GetComponentInParent<TeamOrganizeManager>();
        InitializeSynergySlots();
    }

    private void OnEnable()
    {
        _manager.OnCharacterDataChanged += UpdateSynergySlots;
        _manager.OnCharacterDataChanged += UpdateClassSynergySlots;
    }

    private void OnDisable()
    {
        _manager.OnCharacterDataChanged -= UpdateSynergySlots;
        _manager.OnCharacterDataChanged -= UpdateClassSynergySlots;
    }

    private void InitializeSynergySlots()
    {
        foreach (var data in SynergyController.SynergyDB._synergyDataDic.Values)
        {
            if (data is ClassSynergyData classSynergy)
            {
                LobbyClassSynergySlot slot = Instantiate(_classSynergySlotPrefab, _classContent).GetComponent<LobbyClassSynergySlot>();
                slot.Init(data, 0);
                _classSynergySlots.Add(classSynergy.Synergy.ToString(), slot);
            }
            else if (data is UnitSynergyData unitSynergy)
            {
                LobbySynergySlot slot = Instantiate(_synergySlotPrefab, _content).GetComponent<LobbySynergySlot>();
                slot.Init(data, 0);
                _synergySlots.Add(unitSynergy.Synergy.ToString(), slot);
            }
        }
    }    

    private void UpdateSynergySlots()
    {
        Dictionary<Synergy, int> synergyCount = _manager.GetSynergyCount();

        foreach(var data in synergyCount)
        {
            Synergy synergy = data.Key;
            int count = data.Value;

            _synergySlots[synergy.ToString()].UpdateUI(count);
        }

        SetHiararchy();
    }

    private void UpdateClassSynergySlots()
    {
        Dictionary<ClassType, int> classSynergyCount = _manager.GetClassSynergyCount();

        foreach(var data in classSynergyCount)
        {
            ClassType classSynergy = data.Key;
            int count = data.Value;

            _classSynergySlots[classSynergy.ToString()].UpdateUI(count);
        }

        SetClassHiararchy();
    }    

    private void SetHiararchy()
    {
        List<LobbySynergySlot> slots = new List<LobbySynergySlot>(_synergySlots.Values);

        slots.RemoveAll(s => s.ActiveCount <= 0);

        slots.Sort((a, b) =>
        {
            int result = b.ActiveCount.CompareTo(a.ActiveCount);
            if (result == 0)
                result = b.UpgradeCount.CompareTo(a.UpgradeCount);

            return result;
        });

        for (int i = 0; i < slots.Count; i++)
        {
            slots[i].transform.SetSiblingIndex(i);
        }
    }

    private void SetClassHiararchy()
    {
        List<LobbyClassSynergySlot> slots = new List<LobbyClassSynergySlot>(_classSynergySlots.Values);

        slots.RemoveAll(s => s.ActiveCount <= 0);

        slots.Sort((a, b) =>
        {
            int result = b.ActiveCount.CompareTo(a.ActiveCount);
            if (result == 0)
                result = b.UpgradeCount.CompareTo(a.UpgradeCount);

            return result;
        });

        for (int i = 0; i < slots.Count; i++)
        {
            slots[i].transform.SetSiblingIndex(i);
        }
    }
}
using UnityEngine;
using UnityEngine.UI;

public class LobbySynergySlot : MonoBehaviour
{
    [SerializeField] private Image _icon;

    private SynergyData _synergyData;
    private int[] _synergyCountArray;
    public int ActiveCount { get; private set; }
    public int UpgradeCount => _synergyData.CurrentUpgradeIdx;

    public void Init(SynergyData data, int activeCount)
    {        
        _synergyData = data;
        _icon.sprite = data.Icon;
        SetSynergyCount();
        UpdateUI(activeCount);
    }

    public void UpdateUI(int activeCount)
    {
        ActiveCount = activeCount;
    }

    public void SetSynergyCount()
    {
        _synergyCountArray = new int[_synergyData.SynergyLevelData.Length];

        for (int i = 0; i < _synergyData.SynergyLevelData.Length; i++)
        {
            _synergyCountArray[i] = _synergyData.SynergyLevelData[i].SynergyNeedCount;
        }
    }
}
using UnityEngine;
using UnityEngine.UI;

public class LobbyClassSynergySlot : MonoBehaviour
{
    [SerializeField] private Image _icon;

    private SynergyData _synergyData;
    private int[] _synergyCountArray;
    public int ActiveCount { get; private set; }
    public int UpgradeCount => _synergyData.CurrentUpgradeIdx;

    public void Init(SynergyData data, int activeCount)
    {
        _synergyData = data;
        _icon.sprite = data.Icon;
        SetSynergyCount();
        UpdateUI(activeCount);
    }

    public void UpdateUI(int activeCount)
    {
        ActiveCount = activeCount;
    }

    public void SetSynergyCount()
    {
        _synergyCountArray = new int[_synergyData.SynergyLevelData.Length];

        for (int i = 0; i < _synergyData.SynergyLevelData.Length; i++)
        {
            _synergyCountArray[i] = _synergyData.SynergyLevelData[i].SynergyNeedCount;
        }
    }
}

문제는 일단 작동은 하는 거 같은데 초기화도 안되고 정렬 상태가 뭔가 이상하고, 활성화된 UI가 뭔지 판별이 잘 안된다. 애초에 기획도 잘 안나와있어서 이렇게 각각 구성하는 게 맞는지도 모르겠고.
일단은 작동은 하니까 그대로 두자는 팀장님의 제의로 그대로 두기로 했다.

3. 개선점 및 과제

3.1 빌드 제출

3.2 UI 폴리싱

3.3 가챠 시스템 개선

profile
게임 만들러 코딩 공부중

0개의 댓글