이전에 맵 생성 로직에 사용된 가중치 랜덤 시스템에서 문제가 되는 부분을 고쳤다 싶었는데 다시 중복 뽑기가 되는 현상이 관찰되었다. 이번엔 원인이 뭐였을까?
저번처럼 로직 자체에 있는 문제는 아니었다. 다만 처음에는 쉽사리 파악하기 힘든 문제였는데, 맵 생성 로직이 변경됨에 따라 생긴 문제였다.
private void OnEnable()
{
if (PhotonNetwork.IsMasterClient)
{
for (int i = 0; i < mapListTransform.Length; i++)
{
RandomInit();
RandomMapSelect(i);
}
}
}
맵을 생성하는 방식은 다음과 같다.
라운드가 3개이므로, 랜덤 설정을 하고 뽑는 과정을 세 번 반복한다.
하지만 여기에서 함정이 존재하는데, 랜덤을 설정하는 과정에 있다.
private void RandomInit()
{
for (int i = 0; i < mapResources.Length; i++)
{
mapWeightedRandom.Add(mapResources[i], 1);
}
}
이와 같이 맵을 각 한 번 씩 넣은 다음 그 중에서 뽑으면서 키를 지우므로 처음에는 중복되지 않게 뽑게 될 것이다. 다만, 이걸 3번 반복한다는 것이 문제다.
왜냐하면, 이와 같은 상황이 벌어지기 때문이다.
예를 들어, 맵이 A, B, C, D, E 가 있었을 때 이 중 3개를 랜덤으로 뽑아서 키를 제거했을 것이다. 하지만 두 번째 뽑기에서는, 이 mapWeightedRandom을 그대로 쓰고 있다. 즉 1라운드에서 뽑히지 않은 맵 두 개의 키가 그대로 남아있으므로, 이 상태에서 다시 A, B, C, D, E 가 더해진다. 그러므로, 키가 두 개인 맵이 두 개 존재하게 되며, 이것이 두 번 뽑혔을 경우 중복이 되게 된다.
심지어 3번까지 누적된 후 마지막 라운드에서 똑같은 맵이 3번 나오는 경우까지도 나왔다.
그러므로, 이 상황에서 필요한 건, 해당 랜덤 뽑기 리스트를 초기화하는 것이다.
이 초기화에 관한 기능은 이전에 없었으므로, 직접 추가해줘야 한다. 간단하게 아래와 같이 구성했다.
public void ClearList()
{
_dic = null;
_dic = new Dictionary<T, int>();
}
이와 같이 랜덤 설정을 초기화하는 함수를 추가하고, 이를 맵을 뽑은 뒤에 호출해주면 된다.
private void OnEnable()
{
if (PhotonNetwork.IsMasterClient)
{
for (int i = 0; i < mapListTransform.Length; i++)
{
RandomInit();
RandomMapSelect(i);
mapWeightedRandom.ClearList();
}
}
}
PhotonNetwork.Instantiate로 생성한 오브젝트를 특정 오브젝트의 자식 오브젝트로 설정하는 방법에 대해서는 전에 맵으로 설정하는 방식을 구현하는데 성공했지만, 또 다른 문제가 발생했다.
이번의 경우엔 이미지를 특정 패널의 자식 오브젝트로 연결하는 과정이었다.
...
private void ShowCardList()
{
leftCardList = TestIngameManager.Instance.GetSkillInfo("Left");
rightCardList = TestIngameManager.Instance.GetSkillInfo("Right");
if(leftCardList.Length > leftUIImage.Count)
{
for(int i = leftUIImage.Count; i < leftCardList.Length; i++)
{
GameObject skillInfo = PhotonNetwork.Instantiate(UIImagePrefab.name, transform.position, Quaternion.identity);
PhotonView skillInfoView = skillInfo.GetComponent<PhotonView>();
skillInfoView.RPC(nameof(SkillInfoUI.SetParentToPanel), RpcTarget.All, leftPanelContent);
// TODO : 카드 정보 입력
leftUIImage.Add(skillInfo);
}
}
if(rightCardList.Length > rightUIImage.Count)
{
for (int i = rightUIImage.Count; i < rightCardList.Length; i++)
{
GameObject skillInfo = PhotonNetwork.Instantiate(UIImagePrefab.name, transform.position, Quaternion.identity);
PhotonView skillInfoView = skillInfo.GetComponent<PhotonView>();
skillInfoView.RPC(nameof(SkillInfoUI.SetParentToPanel), RpcTarget.All, rightPanelContent);
// TODO : 카드 정보 입력
rightUIImage.Add(skillInfo);
}
}
}
}
using Photon.Pun;
using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>
/// 마우스를 갖다댔을 때 자신 및 상대의 스킬 정보를 출력하는 UI
/// </summary>
public class SkillInfoUI : MonoBehaviourPun, IPointerEnterHandler, IPointerExitHandler
{
public void OnPointerEnter(PointerEventData eventData)
{
// TODO : 카드 UI 표시
Debug.Log("Info Activate");
}
public void OnPointerExit(PointerEventData eventData)
{
// TODO : 카드 UI 비활성화
Debug.Log("Info Inactivate");
}
[PunRPC]
public void SetParentToPanel(Transform parent)
{
gameObject.transform.SetParent(parent.transform);
}
}
이와 같이 설정하면 오류가 난다.
Exception: Write failed. Custom type not found: UnityEngine.RectTransform
라는 오류가 나오게 되는데, 이는 Photon에서 해당 타입의 정보를 받아들이지 못한다는 것이다. 즉 유니티의 RectTransform이라는 정보를 네트워크 상으로 전달할 수가 없으므로, 결국 다른 방법으로 전달할 방법을 생각해야 한다.
이를 위해서 전달하고자 하는 대상에 PhotonView를 붙이고, 해당 PhotonView의 viewID를 가져오는 방식으로 해결했다.
using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StaticCardInfoUI : MonoBehaviour
{
[SerializeField] GameObject UIImagePrefab;
[SerializeField] Transform leftPanelContent;
[SerializeField] Transform rightPanelContent;
[SerializeField] List<GameObject> leftUIImage = new List<GameObject>();
[SerializeField] List<GameObject> rightUIImage = new List<GameObject>();
string[] leftCardList;
string[] rightCardList;
PhotonView leftPanelView;
PhotonView rightPanelView;
int leftPanelViewID;
int rightPanelViewID;
private PoolManager pools;
public PoolManager Pools => pools;
private void Awake()
{
pools = FindObjectOfType<PoolManager>();
pools.InitializePool(UIImagePrefab.name, UIImagePrefab, 3, 9);
leftPanelView = leftPanelContent.GetComponent<PhotonView>();
rightPanelView = rightPanelContent.GetComponent<PhotonView>();
leftPanelViewID = leftPanelView.ViewID;
rightPanelViewID = rightPanelView.ViewID;
}
private void OnEnable()
{
TestIngameManager.OnSkillObtained += ShowCardList;
}
private void OnDisable()
{
TestIngameManager.OnSkillObtained -= ShowCardList;
}
private void ShowCardList()
{
leftCardList = TestIngameManager.Instance.GetSkillInfo("Left");
rightCardList = TestIngameManager.Instance.GetSkillInfo("Right");
if(leftCardList.Length > leftUIImage.Count)
{
for(int i = leftUIImage.Count; i < leftCardList.Length; i++)
{
GameObject skillInfo = PhotonNetwork.Instantiate(UIImagePrefab.name, transform.position, Quaternion.identity);
PhotonView skillInfoView = skillInfo.GetComponent<PhotonView>();
skillInfoView.RPC(nameof(SkillInfoUI.SetParentToPanel), RpcTarget.All, leftPanelViewID);
// TODO : 카드 정보 입력
leftUIImage.Add(skillInfo);
}
}
if(rightCardList.Length > rightUIImage.Count)
{
for (int i = rightUIImage.Count; i < rightCardList.Length; i++)
{
GameObject skillInfo = PhotonNetwork.Instantiate(UIImagePrefab.name, transform.position, Quaternion.identity);
PhotonView skillInfoView = skillInfo.GetComponent<PhotonView>();
skillInfoView.RPC(nameof(SkillInfoUI.SetParentToPanel), RpcTarget.All, rightPanelViewID);
// TODO : 카드 정보 입력
rightUIImage.Add(skillInfo);
}
}
}
}
using Photon.Pun;
using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>
/// 마우스를 갖다댔을 때 자신 및 상대의 스킬 정보를 출력하는 UI
/// </summary>
public class SkillInfoUI : MonoBehaviourPun, IPointerEnterHandler, IPointerExitHandler
{
public void OnPointerEnter(PointerEventData eventData)
{
// TODO : 카드 UI 표시
Debug.Log("Info Activate");
}
public void OnPointerExit(PointerEventData eventData)
{
// TODO : 카드 UI 비활성화
Debug.Log("Info Inactivate");
}
[PunRPC]
public void SetParentToPanel(int parentViewID)
{
Transform parent = PhotonView.Find(parentViewID).transform;
transform.SetParent(parent);
}
}