-
Tobias Schöner authoredTobias Schöner authored
FactExplorer.cs 6.34 KiB
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using System.Linq;
using UnityEngine.UI;
public class FactExplorer : MonoBehaviour
{
#region InspectorVariables
[Header("PrefabComponents")]
[SerializeField] private Transform factParentsUI;
[SerializeField] private Transform mainFactUI;
[SerializeField] private Transform factChildrenUI;
[SerializeField] private Transform linesUI;
[Header("Prefabs")]
[SerializeField] private GameObject factSpotPrefab;
[SerializeField] private GameObject parentLine;
[SerializeField] private GameObject childLine;
#endregion InspectorVariables
#region Variables
private Fact mainFact;
private List<Fact> parentFacts;
private List<Fact> childFacts;
#endregion Variables
#region UnityMethods
private void Update()
{
DestroyIfClickedOutside();
}
public void Initialize(Fact fact, Vector3 factPosition)
{
mainFact = fact;
parentFacts = GetParentFacts();
childFacts = GetChildFacts();
Debug.Log($"Parents of {mainFact.Label}: {string.Join(", ", parentFacts.Select(cf => cf.Label))}");
Debug.Log($"Children of {mainFact.Label}: {string.Join(", ", childFacts.Select(cf => cf.Label))}");
UpdateFactExplorerUI();
MoveToPreferredPosition(factPosition);
}
#endregion UnityMethods
#region Implementation
private List<Fact> GetParentFacts()
{
_ = StageStatic.stage.factState.safe_dependencies(mainFact.Id, out var parentFactIds);
return parentFactIds.Distinct().Select(factId => StageStatic.stage.factState[factId]).Where(f => f != mainFact).ToList();
}
private List<Fact> GetChildFacts()
{
return mainFact.getDependentFactIds().Distinct().Select(factId => StageStatic.stage.factState[factId]).ToList();
}
private void UpdateFactExplorerUI()
{
SpawnUIFacts(factParentsUI, parentFacts);
SpawnUIFacts(mainFactUI, new List<Fact>() { mainFact });
SpawnUIFacts(factChildrenUI, childFacts);
// Force rebuild of FactExplorer layout, since the positions of the factObjects will be wrong otherwise
LayoutRebuilder.ForceRebuildLayoutImmediate(transform.GetComponent<RectTransform>());
SpawnParentLines(factParentsUI.gameObject, mainFactUI);
SpawnChildLines(factChildrenUI.gameObject, mainFactUI);
}
private void DestroyIfClickedOutside()
{
// Destroy on tab press or left click outside of FactExplorer
if (Input.GetKeyDown(KeyCode.Tab)
|| (Input.GetMouseButtonDown(0) && !RectTransformUtility.RectangleContainsScreenPoint(transform.GetComponent<RectTransform>(), Input.mousePosition, null)))
{
Destroy(gameObject);
}
}
private void MoveToPreferredPosition(Vector3 prefPos)
{
LayoutRebuilder.ForceRebuildLayoutImmediate(transform.GetComponent<RectTransform>());
// calculate opimal position
var deltaPos = mainFactUI.position - prefPos;
transform.position -= deltaPos;
// clamp position, so that no parts of the FactExplorer are out of screen
RectTransform feRect = GetComponent<RectTransform>();
Vector2 apos = feRect.anchoredPosition;
apos.x = Mathf.Clamp(apos.x, 0, Screen.width - feRect.sizeDelta.x);
apos.y = Mathf.Clamp(apos.y, -Screen.height + feRect.sizeDelta.y, 0);
feRect.anchoredPosition = apos;
}
#endregion Implementation
#region Spawner
private void SpawnUIFacts(Transform uiParent, List<Fact> toSpawn)
{
// if uiParent has no children: deactivate it
if (toSpawn.Count == 0)
uiParent.gameObject.SetActive(false);
foreach (Fact f in toSpawn)
{
var spot = Instantiate(factSpotPrefab, uiParent);
// TODO: this link to DisplayFacts is not ideal: maybe refactor to SciptableObject or such
var display = f.instantiateDisplay(DisplayFacts.prefabDictionary[f.GetType()], spot.transform);
display.transform.localPosition = Vector3.zero;
}
}
private void SpawnParentLines(GameObject parent, Transform mainFactUI)
{
var mainTransform = mainFactUI.GetComponent<RectTransform>();
var factWidth = mainTransform.rect.width;
// transform.positions are weird due to LayoutGroups => manually calculate offset
float xOffset = -factParentsUI.GetComponent<RectTransform>().rect.width / 2 + factWidth / 2;
float yOffset = transform.GetComponent<VerticalLayoutGroup>().spacing;
parent.ForAllChildren(par =>
{
// position at the bottom center of par rect
var position = par.transform.TransformPoint(new Vector2(0, par.GetComponent<RectTransform>().rect.yMin));
var line = Instantiate(parentLine, position, Quaternion.identity, par.transform);
var uiLine = line.GetComponent<UILine>();
uiLine.points = new List<Vector2>() { Vector2.zero, new Vector2(-xOffset, -yOffset) };
xOffset += factWidth + factParentsUI.GetComponent<HorizontalLayoutGroup>().spacing;
});
}
private void SpawnChildLines(GameObject parent, Transform mainFactUI)
{
var mainTransform = mainFactUI.GetComponent<RectTransform>();
var factWidth = mainTransform.rect.width;
// transform.positions are weird due to LayoutGroups => manually calculate offset
float xOffset = -factChildrenUI.GetComponent<RectTransform>().rect.width / 2 + factWidth / 2;
float yOffset = -transform.GetComponent<VerticalLayoutGroup>().spacing;
parent.ForAllChildren(par =>
{
// position at the top center of par rect
var position = par.transform.TransformPoint(new Vector2(0, par.GetComponent<RectTransform>().rect.yMax));
var line = Instantiate(childLine, position, Quaternion.identity, par.transform);
var uiLine = line.GetComponent<UILine>();
uiLine.points = new List<Vector2>() {
Vector2.zero,
new Vector2(0, -yOffset/2),
new Vector2(-xOffset, -yOffset/2),
new Vector2(-xOffset, -yOffset)
};
xOffset += factWidth + factChildrenUI.GetComponent<HorizontalLayoutGroup>().spacing;
});
}
#endregion Spawner
}