using System; using System.Collections.Generic; using System.Dynamic; using Unity.VisualScripting; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UIElements; using static UnityEngine.GraphicsBuffer; [ExecuteAlways] public class GameManager : MonoBehaviour { public Transform ConnectionParent; public Transform NodeParent; public GameObject NodePrefab; [HideInInspector] public static GameManager instance; [HideInInspector] public float maxConnectionLength = 6; [HideInInspector] public int nodeCount = 100; [HideInInspector] public int hoverRadius = 50; [SerializeField] [HideInInspector] public List connections = new List(); [HideInInspector] public List nodes = new List(); [Serializable] public class Connection { public Node nodeA, nodeB; public bool allowed = true; public bool hovered = false; public LineRenderer lineRenderer; } public void GenerateAlongSphere() { connections.Clear(); nodes.Clear(); for (int i = NodeParent.childCount - 1; i >= 0; i--) DestroyImmediate(NodeParent.GetChild(i).gameObject); float radius = 20f; float goldenRatio = (1f + Mathf.Sqrt(5f)) / 2f; float angleIncrement = 2f * Mathf.PI * goldenRatio; for (int i = 0; i < nodeCount; i++) { float t = (float)i / nodeCount; // von 0 bis 1 float inclination = Mathf.Acos(1f - 2f * t); float azimuth = angleIncrement * i; float x = Mathf.Sin(inclination) * Mathf.Cos(azimuth); float y = Mathf.Sin(inclination) * Mathf.Sin(azimuth); float z = Mathf.Cos(inclination); Vector3 pos = new Vector3(x, y, z) * radius; var auto = PrefabUtility.InstantiatePrefab(NodePrefab, NodeParent) as GameObject; auto.transform.localPosition = pos; nodes.Add(auto.GetComponent()); } } public void GenerateConnections() { connections.Clear(); foreach (LineRenderer line in ConnectionParent.GetComponentsInChildren()) DestroyImmediate(line.gameObject); foreach (Node nodeA in nodes) { if (nodeA == null) continue; foreach (Node nodeB in nodes) { if (nodeB == null) continue; bool conExists = false; if (nodeA == nodeB || Math.Abs(Vector3.Distance(nodeA.transform.position, nodeB.transform.position)) > maxConnectionLength) continue; foreach (Connection con in connections) { if ((con.nodeA == nodeA && con.nodeB == nodeB) || (con.nodeA == nodeB && con.nodeB == nodeA)) { conExists = true; break; } } if (!conExists) { var dummy = new GameObject("dummy"); dummy.transform.SetParent(ConnectionParent); var newCon = new Connection { nodeA = nodeA, nodeB = nodeB, lineRenderer = dummy.AddComponent() }; newCon.lineRenderer.enabled = false; newCon.lineRenderer.material = new Material(Shader.Find("Sprites/Default")); newCon.lineRenderer.positionCount = 3; connections.Add(newCon); } } } } private void Update() { foreach (var con in connections) { float width = (con.hovered ? 0.6f : 0.3f) * (con.allowed ? 1f : 0.3f); con.lineRenderer.startColor = con.allowed ? con.nodeA.transform.GetChild(0).GetComponent().sharedMaterial.color : new Color(0.2f, 0.2f, 0.2f); con.lineRenderer.endColor = con.allowed ? con.nodeB.transform.GetChild(0).GetComponent().sharedMaterial.color : new Color(0.2f, 0.2f, 0.2f); con.lineRenderer.startWidth = width; con.lineRenderer.endWidth = width; con.lineRenderer.SetGreatCircleArc(con.nodeA.transform.position, con.nodeB.transform.position, 5, 20.5f); con.lineRenderer.enabled = true; } } } #if UNITY_EDITOR [CustomEditor(typeof(GameManager))] public class GameManagerEditor : Editor { int clickedConIdx = -1; public override void OnInspectorGUI() { DrawDefaultInspector(); GameManager gm = (GameManager)target; GUILayout.Label("Generation Parameters", EditorStyles.boldLabel); EditorGUI.BeginChangeCheck(); gm.nodeCount = EditorGUILayout.IntSlider("Node Count", gm.nodeCount, 0, 200); gm.maxConnectionLength = EditorGUILayout.Slider("Max Connection Length", gm.maxConnectionLength, 0, 100); if (EditorGUI.EndChangeCheck()) { gm.GenerateAlongSphere(); gm.GenerateConnections(); SceneView.RepaintAll(); } GUILayout.Space(10); GUILayout.Label("Connection Gizmos", EditorStyles.boldLabel); gm.hoverRadius = EditorGUILayout.IntSlider("Hover Radius", gm.hoverRadius, 0, 100); GUILayout.BeginHorizontal(); if (GUILayout.Button("Generate Nodes")) { gm.GenerateAlongSphere(); SceneView.RepaintAll(); } if (GUILayout.Button("Generate Connections")) { gm.GenerateConnections(); SceneView.RepaintAll(); } if (GUILayout.Button("Generate Both")) { gm.GenerateAlongSphere(); gm.GenerateConnections(); SceneView.RepaintAll(); } GUILayout.EndHorizontal(); } private void OnSceneGUI() { Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual; GameManager gm = (GameManager)target; if (gm.connections == null || gm.connections.Count == 0) return; Camera cam = SceneView.lastActiveSceneView.camera; if (cam == null) return; Vector2 mouse = Event.current.mousePosition; mouse.y = cam.pixelHeight - mouse.y; Vector3 mousePos = new Vector3(mouse.x, mouse.y, 0); int closestIdx = -1; float closestDist = float.MaxValue; // Hover-Ermittlung for (int i = 0; i < gm.connections.Count; i++) { gm.connections[i].hovered = false; var con = gm.connections[i]; Vector3 a = cam.WorldToScreenPoint(con.nodeA.transform.position); Vector3 b = cam.WorldToScreenPoint(con.nodeB.transform.position); Vector3 ab = b - a; Vector3 am = mousePos - a; float t = Mathf.Clamp01(Vector3.Dot(am, ab) / ab.sqrMagnitude); Vector3 proj = a + t * ab; float dist = Vector3.Distance(mousePos, proj); if (dist < closestDist && dist < gm.hoverRadius) { closestDist = dist; closestIdx = i; } } if(closestIdx >= 0) gm.connections[closestIdx].hovered = true; // Klick-Behandlung Event e = Event.current; if (e.type == EventType.MouseDown && e.button == 0 && closestIdx >= 0) { clickedConIdx = closestIdx; } if (e.type == EventType.MouseUp && e.button == 0) { if (closestIdx == clickedConIdx && closestIdx != -1) { gm.connections[closestIdx].allowed = !gm.connections[closestIdx].allowed; e.Use(); // Event als verarbeitet markieren } clickedConIdx = -1; } // SceneView kontinuierlich aktualisieren SceneView.RepaintAll(); } } #endif public static class LineRendererExtensions { // Zeichnet eine Quadratic Bezier Kurve (Start, Control, End) mit N Punkten public static void SetQuadraticBezier(this LineRenderer line, Vector3 start, Vector3 control, Vector3 end, int segments) { line.positionCount = segments + 1; for (int i = 0; i <= segments; i++) { float t = i / (float)segments; // Quadratic Bezier Formel Vector3 point = (1 - t) * (1 - t) * start + 2 * (1 - t) * t * control + t * t * end; line.SetPosition(i, point); } } public static void SetGreatCircleArc(this LineRenderer line, Vector3 start, Vector3 end, int segments, float radius) { // Normalize to sphere radius Vector3 startNorm = start.normalized * radius; Vector3 endNorm = end.normalized * radius; line.positionCount = segments + 1; for (int i = 0; i <= segments; i++) { float t = i / (float)segments; // spherical linear interpolation (slerp) between start and end Vector3 point = Vector3.Slerp(startNorm, endNorm, t); line.SetPosition(i, point); } } }