5Y5T3M/Assets/Scripts/GameManager.cs

310 lines
9.8 KiB
C#
Raw Normal View History

2025-09-16 01:16:02 +02:00
using System;
using System.Collections.Generic;
2025-09-16 10:45:09 +02:00
using System.Dynamic;
2025-09-16 01:16:02 +02:00
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
2025-09-16 14:20:19 +02:00
using UnityEngine.UIElements;
2025-09-16 01:16:02 +02:00
using static UnityEngine.GraphicsBuffer;
[ExecuteAlways]
public class GameManager : MonoBehaviour
{
[HideInInspector]
public static GameManager instance;
2025-09-16 10:45:09 +02:00
public Transform NodeParent;
public GameObject NodePrefab;
2025-09-16 01:16:02 +02:00
[Header("Node Generation")]
[Range(0, 20)]
public float maxConnectionLength = 6;
[Range(0, 1000)]
public int nodeCount = 100;
[Range(0, 100)]
public float hoverRadius = 50;
2025-09-16 10:45:09 +02:00
[Space]
[Range(0,10)]
public int editorConnectionAllowedWidth = 3;
public Color editorConnectionAllowedColor = Color.white;
[Space]
[Range(0, 10)]
public int editorConnectionHoveredWidth = 2;
public Color editorConnectionHoveredColor = Color.gray;
[Space]
[Range(0,10)]
public int editorConnectionForbiddenWidth = 1;
public Color editorConnectionForbiddenColor = Color.black;
2025-09-16 01:16:02 +02:00
2025-09-16 00:08:50 +02:00
private List<Node> nodes = new List<Node>();
2025-09-16 10:45:09 +02:00
[SerializeField]
public List<Connection> connections = new List<Connection>();
[Serializable]
public class Connection
2025-09-16 00:08:50 +02:00
{
2025-09-16 01:16:02 +02:00
public Node nodeA, nodeB;
public bool allowed;
2025-09-16 14:20:19 +02:00
public LineRenderer lineRenderer;
~Connection()
{
DestroyImmediate(lineRenderer.transform.parent);
}
2025-09-16 01:16:02 +02:00
}
public void GenerateAlongSphere()
2025-09-16 00:08:50 +02:00
{
connections.Clear();
nodes.Clear();
2025-09-16 01:16:02 +02:00
for (int i = NodeParent.childCount - 1; i >= 0; i--)
DestroyImmediate(NodeParent.GetChild(i).gameObject);
2025-09-16 00:08:50 +02:00
float radius = 20f;
float goldenRatio = (1f + Mathf.Sqrt(5f)) / 2f;
float angleIncrement = 2f * Mathf.PI * goldenRatio;
2025-09-16 01:16:02 +02:00
for (int i = 0; i < nodeCount; i++)
2025-09-16 00:08:50 +02:00
{
2025-09-16 01:16:02 +02:00
float t = (float)i / nodeCount; // von 0 bis 1
2025-09-16 00:08:50 +02:00
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;
2025-09-16 14:20:19 +02:00
var auto = PrefabUtility.InstantiatePrefab(NodePrefab, NodeParent) as GameObject;
2025-09-16 00:08:50 +02:00
auto.transform.localPosition = pos;
nodes.Add(auto.GetComponent<Node>());
}
2025-09-16 01:16:02 +02:00
}
public void GenerateConnections()
2025-09-16 00:08:50 +02:00
{
connections.Clear();
2025-09-16 10:45:09 +02:00
2025-09-16 01:16:02 +02:00
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)
2025-09-16 14:20:19 +02:00
{
var dummy = Instantiate(new GameObject(), transform);
dummy.name = "Dummy";
var newCon = new Connection
{
nodeA = nodeA,
nodeB = nodeB,
lineRenderer = dummy.AddComponent<LineRenderer>()
};
newCon.lineRenderer.enabled = false;
newCon.lineRenderer.material = new Material(Shader.Find("Sprites/Default"));
newCon.lineRenderer.positionCount = 3;
connections.Add(newCon);
}
2025-09-16 01:16:02 +02:00
}
2025-09-16 14:20:19 +02:00
2025-09-16 00:08:50 +02:00
}
2025-09-16 01:16:02 +02:00
}
2025-09-16 13:04:05 +02:00
#if UNITY_EDITOR
void OnDrawGizmos()
{
if (connections == null) return;
// Linien respektieren den Z-Buffer
Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
foreach (var con in connections)
{
if(!con.allowed) continue;
Handles.color = Color.red;
Handles.DrawLine(con.nodeA.transform.position, con.nodeB.transform.position);
}
}
2025-09-16 14:20:19 +02:00
#endif
private void Update()
{
foreach (var con in connections)
{
if (!con.allowed)
{
con.lineRenderer.enabled = false;
continue;
}
con.lineRenderer.enabled = true;
con.lineRenderer.startColor = con.nodeA.transform.GetChild(0).GetComponent<Renderer>().material.color;
con.lineRenderer.endColor = con.nodeB.transform.GetChild(0).GetComponent<Renderer>().material.color;
con.lineRenderer.startWidth = 0.3f;
con.lineRenderer.endWidth = 0.3f;
con.lineRenderer.SetGreatCircleArc(con.nodeA.transform.position, con.nodeB.transform.position, 5, 20.5f);
con.lineRenderer.enabled = true;
}
}
2025-09-16 10:45:09 +02:00
}
2025-09-16 01:16:02 +02:00
2025-09-16 10:45:09 +02:00
#if UNITY_EDITOR
[CustomEditor(typeof(GameManager))]
public class GameManagerEditor : Editor
{
int clickedConIdx = -1;
2025-09-16 01:16:02 +02:00
2025-09-16 10:45:09 +02:00
public override void OnInspectorGUI()
{
DrawDefaultInspector();
GameManager gm = (GameManager)target;
GUILayout.BeginHorizontal();
if (GUILayout.Button("Generate Sphere"))
gm.GenerateAlongSphere();
if (GUILayout.Button("Generate Connections"))
gm.GenerateConnections();
GUILayout.EndHorizontal();
if (GUILayout.Button("Generate"))
{
gm.GenerateAlongSphere();
gm.GenerateConnections();
}
2025-09-16 01:16:02 +02:00
}
2025-09-16 10:45:09 +02:00
private void OnSceneGUI()
2025-09-16 00:08:50 +02:00
{
2025-09-16 10:45:09 +02:00
Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
2025-09-16 01:16:02 +02:00
2025-09-16 10:45:09 +02:00
GameManager gm = (GameManager)target;
if (gm.connections == null || gm.connections.Count == 0) return;
2025-09-16 01:16:02 +02:00
Camera cam = SceneView.lastActiveSceneView.camera;
2025-09-16 10:45:09 +02:00
if (cam == null) return;
2025-09-16 00:08:50 +02:00
2025-09-16 10:45:09 +02:00
Vector2 mouse = Event.current.mousePosition;
mouse.y = cam.pixelHeight - mouse.y;
Vector3 mousePos = new Vector3(mouse.x, mouse.y, 0);
2025-09-16 00:08:50 +02:00
2025-09-16 10:45:09 +02:00
int closestIdx = -1;
2025-09-16 01:16:02 +02:00
float closestDist = float.MaxValue;
2025-09-16 10:45:09 +02:00
// Hover-Ermittlung
for (int i = 0; i < gm.connections.Count; i++)
2025-09-16 00:08:50 +02:00
{
2025-09-16 10:45:09 +02:00
var con = gm.connections[i];
2025-09-16 01:16:02 +02:00
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);
2025-09-16 10:45:09 +02:00
if (dist < closestDist && dist < gm.hoverRadius)
2025-09-16 01:16:02 +02:00
{
closestDist = dist;
2025-09-16 10:45:09 +02:00
closestIdx = i;
2025-09-16 01:16:02 +02:00
}
}
2025-09-16 10:45:09 +02:00
// Klick-Behandlung
Event e = Event.current;
if (e.type == EventType.MouseDown && e.button == 0 && closestIdx >= 0)
2025-09-16 01:16:02 +02:00
{
2025-09-16 10:45:09 +02:00
clickedConIdx = closestIdx;
2025-09-16 01:16:02 +02:00
}
2025-09-16 10:45:09 +02:00
if (e.type == EventType.MouseUp && e.button == 0)
2025-09-16 01:16:02 +02:00
{
2025-09-16 10:45:09 +02:00
if (closestIdx == clickedConIdx && closestIdx != -1)
{
gm.connections[closestIdx].allowed = !gm.connections[closestIdx].allowed;
e.Use(); // Event als verarbeitet markieren
}
2025-09-16 01:16:02 +02:00
2025-09-16 10:45:09 +02:00
clickedConIdx = -1;
2025-09-16 01:16:02 +02:00
}
2025-09-16 10:45:09 +02:00
// Zeichnen
for (int i = 0; i < gm.connections.Count; i++)
2025-09-16 01:16:02 +02:00
{
2025-09-16 10:45:09 +02:00
var con = gm.connections[i];
Handles.color = (i == closestIdx && !con.allowed) ? gm.editorConnectionHoveredColor : con.allowed ? gm.editorConnectionAllowedColor : gm.editorConnectionForbiddenColor;
float thickness = (i == closestIdx) ? gm.editorConnectionHoveredWidth : con.allowed ? gm.editorConnectionAllowedWidth : gm.editorConnectionForbiddenWidth;
Handles.DrawLine(con.nodeA.transform.position, con.nodeB.transform.position, thickness);
2025-09-16 00:08:50 +02:00
}
2025-09-16 01:16:02 +02:00
2025-09-16 10:45:09 +02:00
// SceneView kontinuierlich aktualisieren
SceneView.RepaintAll();
2025-09-16 01:16:02 +02:00
}
}
#endif
2025-09-16 14:20:19 +02:00
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);
}
}
}