diff --git a/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorerUI.prefab b/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorerUI.prefab index 37209044b8dde9b441ae64363a335c955b2b18e8..f61c17ed533c45e1d3e8a49d424988592ad37dff 100644 --- a/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorerUI.prefab +++ b/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorerUI.prefab @@ -78,6 +78,103 @@ MonoBehaviour: m_EditorClassIdentifier: m_HorizontalFit: 2 m_VerticalFit: 2 +--- !u!1 &196909836272952088 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 294307632257003305} + - component: {fileID: 3259288062294702885} + - component: {fileID: 5601304647299485279} + - component: {fileID: 2468798738105667105} + m_Layer: 5 + m_Name: Highlight + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &294307632257003305 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 196909836272952088} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2636669742664082113} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 110, y: 110} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &3259288062294702885 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 196909836272952088} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreLayout: 1 + m_MinWidth: -1 + m_MinHeight: -1 + m_PreferredWidth: -1 + m_PreferredHeight: -1 + m_FlexibleWidth: -1 + m_FlexibleHeight: -1 + m_LayoutPriority: 1 +--- !u!222 &5601304647299485279 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 196909836272952088} + m_CullTransparentMesh: 1 +--- !u!114 &2468798738105667105 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 196909836272952088} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.49803922, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!1 &2146237966352740759 GameObject: m_ObjectHideFlags: 0 @@ -87,6 +184,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 2636669742664082113} + - component: {fileID: 8821645608645168199} m_Layer: 5 m_Name: MainFact m_TagString: Untagged @@ -105,7 +203,8 @@ RectTransform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: [] + m_Children: + - {fileID: 294307632257003305} m_Father: {fileID: 3406631590813364790} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -114,6 +213,14 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 100, y: 100} m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &8821645608645168199 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2146237966352740759} + m_CullTransparentMesh: 1 --- !u!1 &5592893003942163674 GameObject: m_ObjectHideFlags: 0 @@ -253,11 +360,11 @@ MonoBehaviour: mainFactUI: {fileID: 2636669742664082113} factChildrenUI: {fileID: 9153153668998730241} linesUI: {fileID: 8009565381998387591} - factExplorerLineParents_Prefab: {fileID: 602666608487228118, guid: 6cdb6cc4d5af8db409745afbc201068b, + factSpotPrefab: {fileID: 7124463502404826001, guid: 04d12e0a0a5aa884c8c7dff56f4963f6, type: 3} - factExplorerLineChildren_Prefab: {fileID: 4365600632079246398, guid: a9fcb9d279efe6849a46855110d5e794, + parentLine: {fileID: 602666608487228118, guid: 6cdb6cc4d5af8db409745afbc201068b, type: 3} - factSpotPrefab: {fileID: 7124463502404826001, guid: 04d12e0a0a5aa884c8c7dff56f4963f6, + childLine: {fileID: 4903779976374899580, guid: 7f47b2a4ac81f444eb5e18b8efabc644, type: 3} --- !u!1 &7302347448387664938 GameObject: @@ -369,10 +476,10 @@ RectTransform: m_Father: {fileID: 3406631590813364790} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 100, y: 100} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &835707716098452998 MonoBehaviour: diff --git a/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line.prefab b/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line.prefab new file mode 100644 index 0000000000000000000000000000000000000000..bb48d9e20f42ef543e0260f8d94140fc6a7efb66 --- /dev/null +++ b/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line.prefab @@ -0,0 +1,93 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &4903779976374899580 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 911949952148793464} + - component: {fileID: -137166815004959870} + - component: {fileID: 2132054669896436896} + - component: {fileID: 4832178158615009880} + m_Layer: 0 + m_Name: FactExplorer_Line + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &911949952148793464 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4903779976374899580} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &-137166815004959870 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4903779976374899580} + m_CullTransparentMesh: 1 +--- !u!114 &2132054669896436896 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4903779976374899580} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreLayout: 1 + m_MinWidth: -1 + m_MinHeight: -1 + m_PreferredWidth: -1 + m_PreferredHeight: -1 + m_FlexibleWidth: -1 + m_FlexibleHeight: -1 + m_LayoutPriority: 1 +--- !u!114 &4832178158615009880 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4903779976374899580} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8ed745d97410d6740921398c899d9ec0, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.3137255, g: 0.3137255, b: 0.3137255, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + points: [] + width: 4 + roundCorners: 1 + roundStart: 1 + roundEnd: 1 + dashed: 0 + dashLength: 10 + dashSpacing: 5 diff --git a/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line.prefab.meta b/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line.prefab.meta new file mode 100644 index 0000000000000000000000000000000000000000..a597efb2d92cfd29cfd09245223a4f3f2e99b1cf --- /dev/null +++ b/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7f47b2a4ac81f444eb5e18b8efabc644 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line_Dashed.prefab b/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line_Dashed.prefab new file mode 100644 index 0000000000000000000000000000000000000000..092bb44b0a62cb76643d46682825cc4b5e9d7988 --- /dev/null +++ b/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line_Dashed.prefab @@ -0,0 +1,141 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &5499075457957573034 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_AnchorMax.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_AnchorMax.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_AnchorMin.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_AnchorMin.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 911949952148793464, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4832178158615009880, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: dashed + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4903779976374899580, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_Name + value: FactExplorer_Line_Dashed + objectReference: {fileID: 0} + - target: {fileID: 8455242915356568467, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_Materials.Array.size + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8455242915356568467, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_Positions.Array.size + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 8455242915356568467, guid: 7f47b2a4ac81f444eb5e18b8efabc644, + type: 3} + propertyPath: m_Materials.Array.data[0] + value: + objectReference: {fileID: 2100000, guid: 3be144593ef3bac42812ba90847c1cff, type: 2} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 7f47b2a4ac81f444eb5e18b8efabc644, type: 3} diff --git a/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line_Dashed.prefab.meta b/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line_Dashed.prefab.meta new file mode 100644 index 0000000000000000000000000000000000000000..3ef635f893ebc17fe25e3a9d038956dc00659607 --- /dev/null +++ b/Assets/Resources/Prefabs/UI/FactExplorer/FactExplorer_Line_Dashed.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 6cdb6cc4d5af8db409745afbc201068b +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UI/FactExplorer/FactExplorer.cs b/Assets/Scripts/UI/FactExplorer/FactExplorer.cs index a1583f8bda0fc8dc2f25cc3baa5fb38d45b6f7af..199362a79643f4377fe4564388b334ea5180b356 100644 --- a/Assets/Scripts/UI/FactExplorer/FactExplorer.cs +++ b/Assets/Scripts/UI/FactExplorer/FactExplorer.cs @@ -8,6 +8,7 @@ public class FactExplorer : MonoBehaviour { + #region InspectorVariables [Header("PrefabComponents")] [SerializeField] private Transform factParentsUI; [SerializeField] private Transform mainFactUI; @@ -16,13 +17,17 @@ public class FactExplorer : MonoBehaviour [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(); @@ -30,7 +35,6 @@ private void Update() public void Initialize(Fact fact) { - mainFact = fact; parentFacts = GetParentFacts(); childFacts = GetChildFacts(); @@ -40,6 +44,7 @@ public void Initialize(Fact fact) UpdateFactExplorerUI(); } + #endregion UnityMethods #region Implementation private List<Fact> GetParentFacts() @@ -72,18 +77,88 @@ 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() { - if ((Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) && - !RectTransformUtility.RectangleContainsScreenPoint(transform.GetComponent<RectTransform>(), Input.mousePosition, null)) + // 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); } } #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 + #region Helper /// <summary> /// Check all fields of a Fact for references to other facts and return their ids @@ -96,18 +171,5 @@ private static IEnumerable<string> GetReferencedPids(Fact f) .Where(field => field.FieldType == typeof(string) && field.Name.ToLower().StartsWith("pid")) ).Select(fi => (string)fi.GetValue(f)); } - - private void SpawnUIFacts(Transform uiParent, List<Fact> toSpawn) - { - uiParent.gameObject.DestroyAllChildren(); - 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; - } - } #endregion Helper } diff --git a/Assets/Scripts/UI/FactExplorer/UILine.cs b/Assets/Scripts/UI/FactExplorer/UILine.cs new file mode 100644 index 0000000000000000000000000000000000000000..b8c56f425ee7f92303d7a952a78280af87184581 --- /dev/null +++ b/Assets/Scripts/UI/FactExplorer/UILine.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using System.Linq; + +public class UILine : Graphic +{ + #region InspectorVariables + [Header("General")] + public List<Vector2> points = new(); + public float width = 5f; + + [Header("Rounding")] + public bool roundCorners = true; + public bool roundStart = false; + public bool roundEnd = false; + + [Header("Dashed")] + public bool dashed = false; + public int dashLength = 10; + public int dashSpacing = 5; + #endregion InspectorVariables + + #region UnityMethods + protected override void OnPopulateMesh(VertexHelper vh) + { + // clear old vertecies and triangles + vh.Clear(); + + // a line with less than 2 points does not make any sense => return + if (points.Count < 2) + return; + + if (dashed) + GenerateLinesDashed(vh); + else + GenerateLinesStandard(vh); + + if (roundStart) + CreateCircle(vh, points[0], width); + + if (roundEnd) + CreateCircle(vh, points[^1], width); + } + #endregion UnityMethods + + #region Implementation + + #region GenerateLines + private void GenerateLinesStandard(VertexHelper vh) + { + points.Zip(points.Skip(1), (a, b) => Tuple.Create(a, b)) + .ToList() + .ForEach(tup => CreateSegment(vh, tup.Item1, tup.Item2)); + + if (roundCorners) + { + // take all points except for start and end and apply rounding + points.Take(points.Count - 1).Skip(1).ToList() + .ForEach(p => CreateCircle(vh, p, width)); + } + } + + private void GenerateLinesDashed(VertexHelper vh) + { + float restLen = dashLength; + bool isDash = true; + var lines = points.Zip(points.Skip(1), (a, b) => Tuple.Create(a, b)).ToList(); + for (int i = 0; i < lines.Count; i++) + { + var tup = lines[i]; + Vector2 start = tup.Item1; + Vector2 end = tup.Item2; + Vector2 dir = (end - start).normalized; + + // main spacing algorithm + Vector2 current = start; + while (Vector2.Distance(current, end) >= restLen) + { + Vector2 segmentEnd = current + restLen * dir; + if (isDash) + CreateSegment(vh, current, segmentEnd); + current = segmentEnd; + isDash = !isDash; + restLen = isDash ? dashLength : dashSpacing; + } + + // is there a dash wrapping around a corner? + bool dashOverCorner = false; + + + float distLeft = Vector2.Distance(current, end); + if (!isDash) + restLen -= distLeft; + // dont fill remaining distance with dash, if it would be short (shorter than width/2) + else if (isDash && distLeft > width/2) + { + CreateSegment(vh, current, end); + restLen -= distLeft; + dashOverCorner = true; + } + + // discard rest of dash if it is too short (shorter than width/2) + if (isDash && restLen < width/2) + { + isDash = false; + restLen = dashSpacing; + dashOverCorner = false; // dash does not wrap around corner as dashSpacing will follow + } + + // only create round corners if roundCorners are enabled and there is a dash wrapping around the corner and not last corner (aka end) + if (roundCorners && dashOverCorner && i != lines.Count-1) + CreateCircle(vh, end, width); + } + } + #endregion GenerateLines + + #region CreateLine + private void CreateSegment(VertexHelper vh, Vector2 start, Vector2 end) + { + //start += (Vector2)transform.position; + //end += (Vector2)transform.position; + + UIVertex vertex = UIVertex.simpleVert; + vertex.color = color; + + Vector2 dir = end - start; + Vector2 perp = Vector2.Perpendicular(dir).normalized; + Vector2 off = perp * width / 2; + + vertex.position = new Vector3(start.x + off.x, start.y + off.y); + vh.AddVert(vertex); + vertex.position = new Vector3(end.x + off.x, end.y + off.y); + vh.AddVert(vertex); + vertex.position = new Vector3(end.x - off.x, end.y - off.y); + vh.AddVert(vertex); + vertex.position = new Vector3(start.x - off.x, start.y - off.y); + vh.AddVert(vertex); + + int offset = vh.currentVertCount - 4; + vh.AddTriangle(0 + offset, 1 + offset, 2 + offset); + vh.AddTriangle(2 + offset, 3 + offset, 0 + offset); + } + #endregion CreateLine + + #region Rounding + private void CreateCircle(VertexHelper vh, Vector2 center, float diameter, int sideCount = 20) + { + //center += (Vector2)transform.position; + + UIVertex vertex = UIVertex.simpleVert; + vertex.color = color; + + Vector3[] vertices = GetCirclePoints(diameter/2, sideCount, center).Union(new Vector3[] { center }).ToArray(); + vertices.ToList().ForEach(vert => { + vertex.position = vert; + vh.AddVert(vertex); + } + ); + + int startVert = vh.currentVertCount - vertices.Length; + for (int i = 0; i < vertices.Length - 1; i++) + { + vh.AddTriangle( + vh.currentVertCount - 1, // center + startVert + i, + startVert + ((i + 1) % (vertices.Length - 1)) + ); + } + return; + } + + protected static Vector3[] GetCirclePoints(float circleRadius, int pointCount, Vector3 offset) + { + Vector3[] circle = new Vector3[pointCount]; + float slice = (2f * Mathf.PI) / pointCount; + for (int i = 0; i < pointCount; i++) + { + float angle = i * slice; + circle[i] = new Vector3(circleRadius * Mathf.Sin(angle), circleRadius * Mathf.Cos(angle)) + offset; + } + return circle; + } + + #endregion Rounding + + #endregion Implementation +} diff --git a/Assets/Scripts/UI/FactExplorer/UILine.cs.meta b/Assets/Scripts/UI/FactExplorer/UILine.cs.meta new file mode 100644 index 0000000000000000000000000000000000000000..1633aef80654570e14d5b765d0e78267fbc8f29a --- /dev/null +++ b/Assets/Scripts/UI/FactExplorer/UILine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ed745d97410d6740921398c899d9ec0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: