Skip to content
Snippets Groups Projects
FactExplorer.cs 6.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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
    
        private Fact mainFact;
        private List<Fact> parentFacts;
        private List<Fact> childFacts;
    
        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))}");
    
    
            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)))
    
    
        private void MoveToPreferredPosition(Vector3 prefPos)
        {
            LayoutRebuilder.ForceRebuildLayoutImmediate(transform.GetComponent<RectTransform>());
    
            var deltaPos = mainFactUI.position - prefPos;
            transform.position -= deltaPos;
    
            // clamp position, so that no parts of the FactExplorer are out of screen
    
            RectTransform rect = GetComponent<RectTransform>();
            RectTransform canvasRect = GetComponentInParent<Canvas>().transform.GetComponent<RectTransform>();
    
            var sizeDelta = canvasRect.sizeDelta - rect.sizeDelta;
            var panelPivot = rect.pivot;
            var position = rect.anchoredPosition;
            position.x = Mathf.Clamp(position.x, -sizeDelta.x * panelPivot.x, sizeDelta.x * (1 - panelPivot.x));
            position.y = Mathf.Clamp(position.y, -sizeDelta.y * panelPivot.y, sizeDelta.y * (1 - panelPivot.y));
            rect.anchoredPosition = position;
    
        #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