feat: connection hover ingame

This commit is contained in:
LordDemonix 2025-09-17 23:38:11 +02:00
parent 4417315086
commit 5d5e7caa49
12 changed files with 136028 additions and 126710 deletions

File diff suppressed because it is too large Load diff

View file

@ -32,7 +32,7 @@ public static class EditorCustom
{
menu.AddItem(new GUIContent("Connect Selected Nodes"), false, () =>
{
GameManager.Connection con = gm.connections.Find(con =>
Connection con = gm.GetConnections().Find(con =>
(con.nodeA == selectedNodes[0] && con.nodeB == selectedNodes[1]) ||
(con.nodeA == selectedNodes[1] && con.nodeB == selectedNodes[0]));
if (con != null)
@ -114,11 +114,6 @@ public static class EditorCustom
GUILayout.BeginHorizontal();
if (GUILayout.Button("Fetch all Nodes"))
gm.FetchAllNodes();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
@ -178,6 +173,9 @@ public static class EditorCustom
GameManager gm = FindFirstObjectByType<GameManager>();
if (gm == null) return;
var nodes = gm.GetNodes();
var connections = gm.GetConnections();
GameObject selected = Selection.activeGameObject;
bool allowHover = false;
@ -186,13 +184,13 @@ public static class EditorCustom
else
{
Node node = selected?.GetComponent<Node>();
if (node != null && gm.nodes.Contains(node))
if (node != null && nodes.Contains(node))
allowHover = true;
}
if (!allowHover) return;
if (gm.connections == null || gm.connections.Count == 0) return;
if (connections == null || connections.Count == 0) return;
Camera cam = SceneView.lastActiveSceneView.camera;
if (cam == null) return;
@ -205,14 +203,14 @@ public static class EditorCustom
bool cancelHover = false;
// Hover-Ermittlung Connections
for (int i = 0; i < gm.connections.Count; i++)
for (int i = 0; i < connections.Count; i++)
{
gm.connections[i].hovered = false;
connections[i].hovered = false;
if (cancelHover)
continue;
var con = gm.connections[i];
var con = connections[i];
Vector3 a = cam.WorldToScreenPoint(con.nodeA.transform.position);
Vector3 b = cam.WorldToScreenPoint(con.nodeB.transform.position);
@ -237,7 +235,7 @@ public static class EditorCustom
if (!cancelHover)
gm.connections[closestIdx].hovered = true;
connections[closestIdx].hovered = true;
// Klick-Behandlung
@ -253,7 +251,7 @@ public static class EditorCustom
{
if (closestIdx == clickedConIdx && !cancelHover)
{
gm.connections[closestIdx].allowed = !gm.connections[closestIdx].allowed;
connections[closestIdx].allowed = !connections[closestIdx].allowed;
e.Use(); // Event als verarbeitet markieren
}

View file

@ -0,0 +1,173 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &5901756779161544019
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5273476297312813278}
- component: {fileID: 6872516714774548143}
- component: {fileID: 8344422628654918908}
m_Layer: 0
m_Name: Connection
m_TagString: Connection
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &5273476297312813278
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5901756779161544019}
serializedVersion: 2
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_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &6872516714774548143
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5901756779161544019}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 29829ce4af9aff044ad7eebd3d6c49d6, type: 3}
m_Name:
m_EditorClassIdentifier:
nodeA: {fileID: 0}
nodeB: {fileID: 0}
allowed: 1
state: 0
hovered: 0
lineRenderer: {fileID: 8344422628654918908}
--- !u!120 &8344422628654918908
LineRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5901756779161544019}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 0
m_LightProbeUsage: 0
m_ReflectionProbeUsage: 0
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: c670d9c5cf5963b4e9ca485b3528ea58, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_Positions:
- {x: 18.915602, y: 7.7591124, z: -1.49875}
- {x: 18.616209, y: 8.429085, z: -1.6240057}
- {x: 18.292265, y: 9.087942, z: -1.7471195}
- {x: 17.944199, y: 9.734813, z: -1.8679296}
- {x: 17.57247, y: 10.368848, z: -1.9862761}
- {x: 17.177568, y: 10.98921, z: -2.1020038}
m_Parameters:
serializedVersion: 3
widthMultiplier: 1
widthCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0.3
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0.3
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
colorGradient:
serializedVersion: 2
key0: {r: 0.4627451, g: 0.4627451, b: 0.4627451, a: 1}
key1: {r: 0.4627451, g: 0.4627451, b: 0.4627451, a: 1}
key2: {r: 0, g: 0, b: 0, a: 0}
key3: {r: 0, g: 0, b: 0, a: 0}
key4: {r: 0, g: 0, b: 0, a: 0}
key5: {r: 0, g: 0, b: 0, a: 0}
key6: {r: 0, g: 0, b: 0, a: 0}
key7: {r: 0, g: 0, b: 0, a: 0}
ctime0: 0
ctime1: 65535
ctime2: 0
ctime3: 0
ctime4: 0
ctime5: 0
ctime6: 0
ctime7: 0
atime0: 0
atime1: 65535
atime2: 0
atime3: 0
atime4: 0
atime5: 0
atime6: 0
atime7: 0
m_Mode: 0
m_ColorSpace: -1
m_NumColorKeys: 2
m_NumAlphaKeys: 2
numCornerVertices: 0
numCapVertices: 0
alignment: 0
textureMode: 0
textureScale: {x: 1, y: 1}
shadowBias: 0.5
generateLightingData: 0
m_MaskInteraction: 0
m_UseWorldSpace: 1
m_Loop: 0
m_ApplyActiveColorSpace: 1

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 76310260ccf4705488abca2936825a8d
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -25,7 +25,7 @@ RectTransform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1882714990603045140}
m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
m_LocalRotation: {x: 0.067298695, y: -0.6646487, z: 0.06030975, w: 0.7416709}
m_LocalPosition: {x: 0, y: 0, z: 1.96}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
@ -282,7 +282,7 @@ SphereCollider:
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 2
m_Radius: 1
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &6039747573752144155
GameObject:
@ -332,6 +332,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
Owner: -1
Units: 0
id: 0
unitText: {fileID: 7499881475324519762}
materialOwnerSelf: {fileID: 2100000, guid: 06d3b3201213c004a95a21afd8cdeb93, type: 2}
materialOwnerOther: {fileID: 2100000, guid: 6fd755fe2122d2443a364b2c2a3a8291, type: 2}

View file

@ -66,6 +66,35 @@ public class CameraOrbit : MonoBehaviour
var obj = hit.transform.parent.GetComponent<Node>();
obj.hovered = true;
}
else
{
GameManager gm = FindFirstObjectByType<GameManager>();
Connection closestCon = null;
float closestDist = 0.5f; // maximale Abstand in Welt-Einheiten
foreach (var con in gm.GetConnections())
{
Vector3 a = con.nodeA.transform.position;
Vector3 b = con.nodeB.transform.position;
Vector3 ab = b - a;
float t = Mathf.Clamp01(Vector3.Dot(hit.point - a, ab) / ab.sqrMagnitude);
Vector3 proj = a + t * ab;
float dist = Vector3.Distance(hit.point, proj);
if (dist < closestDist)
{
closestDist = dist;
closestCon = con;
}
}
if (closestCon != null)
{
closestCon.hovered = true;
}
}
}
}
}

View file

@ -0,0 +1,46 @@
using UnityEngine;
[ExecuteAlways]
public class Connection : MonoBehaviour
{ public enum BuildState { EMPTY, DECONSTRUCTING, CONSTRUCTING, BUILT };
public Node nodeA, nodeB;
public bool allowed = true;
public BuildState state = BuildState.EMPTY;
public bool hovered = false;
public LineRenderer lineRenderer;
public void SetConnect()
{
if (!allowed) return;
nodeA.connected.Add(nodeB);
nodeB.connected.Add(nodeA);
}
public void DelConnect()
{
nodeA.connected.Remove(nodeB);
nodeB.connected.Remove(nodeA);
}
private void Update()
{
if (Application.isPlaying && !allowed)
lineRenderer.enabled = false;
else
{
float width = (hovered ? 0.6f : 0.3f) * (allowed ? 1f : 0.3f);
lineRenderer.startColor = allowed ? nodeA.transform.GetChild(0).GetComponent<Renderer>().sharedMaterial.color : new Color(0.2f, 0.2f, 0.2f);
lineRenderer.endColor = allowed ? nodeB.transform.GetChild(0).GetComponent<Renderer>().sharedMaterial.color : new Color(0.2f, 0.2f, 0.2f);
lineRenderer.startWidth = width;
lineRenderer.endWidth = width;
lineRenderer.SetGreatCircleArc(nodeA.transform.position, nodeB.transform.position, 5, 20.5f);
lineRenderer.enabled = true;
}
hovered = false;
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 29829ce4af9aff044ad7eebd3d6c49d6

View file

@ -13,6 +13,7 @@ using static UnityEngine.GraphicsBuffer;
public class GameManager : MonoBehaviour
{
public Transform ConnectionParent;
public GameObject ConnectionPrefab;
public Transform NodeParent;
public GameObject NodePrefab;
@ -30,38 +31,8 @@ public class GameManager : MonoBehaviour
[HideInInspector] public int hoverRadiusNode = 50;
[HideInInspector] public int selectedLevel = -1;
[SerializeField] [HideInInspector] public List<Connection> connections = new List<Connection>();
[SerializeField] [HideInInspector] public List<Node> nodes = new List<Node>();
[SerializeField] [HideInInspector] public List<LevelData> levels = new List<LevelData>();
[Serializable]
public class Connection
{
public enum BuildState { EMPTY, DECONSTRUCTING, CONSTRUCTING, BUILT};
public Node nodeA, nodeB;
public bool allowed = true;
public BuildState state = BuildState.EMPTY;
public bool hovered = false;
public LineRenderer lineRenderer;
public void SetConnect()
{
if (!allowed) return;
nodeA.connected.Add(nodeB);
nodeB.connected.Add(nodeA);
}
public void DelConnect()
{
nodeA.connected.Remove(nodeB);
nodeB.connected.Remove(nodeA);
}
}
[Serializable]
public class LevelData
{
@ -100,38 +71,26 @@ public class GameManager : MonoBehaviour
DontDestroyOnLoad(gameObject);
connections.ForEach(obj => obj.DelConnect());
connections.ForEach(obj => obj.SetConnect());
GetConnections().ForEach(obj => obj.DelConnect());
GetConnections().ForEach(obj => obj.SetConnect());
}
private void Update()
{
foreach (var con in connections)
{
if (Application.isPlaying && !con.allowed)
{
con.lineRenderer.enabled = false;
continue;
}
}
float width = (con.hovered ? 0.6f : 0.3f) * (con.allowed ? 1f : 0.3f);
public List<Node> GetNodes() => NodeParent.GetComponentsInChildren<Node>().ToList();
public List<Connection> GetConnections() => ConnectionParent.GetComponentsInChildren<Connection>().ToList();
con.lineRenderer.startColor = con.allowed ? con.nodeA.transform.GetChild(0).GetComponent<Renderer>().sharedMaterial.color : new Color(0.2f, 0.2f, 0.2f);
con.lineRenderer.endColor = con.allowed ? con.nodeB.transform.GetChild(0).GetComponent<Renderer>().sharedMaterial.color : new Color(0.2f, 0.2f, 0.2f);
con.lineRenderer.startWidth = width;
con.lineRenderer.endWidth = width;
public void OnNodesDragged(Node nodeA, Node nodeB)
{
con.lineRenderer.SetGreatCircleArc(con.nodeA.transform.position, con.nodeB.transform.position, 5, 20.5f);
con.lineRenderer.enabled = true;
}
}
public void GenerateAlongSphere()
{
connections.Clear();
nodes.Clear();
for (int i = NodeParent.childCount - 1; i >= 0; i--)
DestroyImmediate(NodeParent.GetChild(i).gameObject);
@ -152,17 +111,17 @@ public class GameManager : MonoBehaviour
Vector3 pos = new Vector3(x, y, z) * radius;
var auto = PrefabUtility.InstantiatePrefab(NodePrefab, NodeParent) as GameObject;
auto.GetComponent<Node>().id = i;
auto.transform.localPosition = pos;
nodes.Add(auto.GetComponent<Node>());
}
}
public void GenerateConnections()
{
connections.Clear();
for (int i = ConnectionParent.childCount - 1; i >= 0; i--)
DestroyImmediate(ConnectionParent.GetChild(i).gameObject);
foreach (LineRenderer line in ConnectionParent.GetComponentsInChildren<LineRenderer>())
DestroyImmediate(line.gameObject);
var nodes = GetNodes();
foreach (Node nodeA in nodes)
{
@ -175,7 +134,7 @@ public class GameManager : MonoBehaviour
if (nodeA == nodeB || dist > maxConnectionLength)
continue;
foreach (Connection con in connections)
foreach (Connection con in GetConnections())
{
if ((con.nodeA == nodeA && con.nodeB == nodeB) || (con.nodeA == nodeB && con.nodeB == nodeA))
{
@ -192,31 +151,12 @@ public class GameManager : MonoBehaviour
}
}
public void FetchAllNodes()
{
Node[] allNodes = NodeParent.GetComponentsInChildren<Node>();
nodes = allNodes.ToList();
nodeCount = allNodes.Length;
}
public void AddConnection(Node nodeA, Node nodeB, bool allowed = true)
{
var dummy = new GameObject("dummy");
dummy.transform.SetParent(ConnectionParent);
var newCon = new Connection
{
nodeA = nodeA,
nodeB = nodeB,
allowed = allowed,
lineRenderer = dummy.AddComponent<LineRenderer>()
};
newCon.lineRenderer.enabled = false;
newCon.lineRenderer.material = new Material(Shader.Find("Sprites/Default"));
newCon.lineRenderer.positionCount = 3;
connections.Add(newCon);
var newCon = PrefabUtility.InstantiatePrefab(ConnectionPrefab, ConnectionParent).GetComponent<Connection>();
newCon.nodeA = nodeA;
newCon.nodeB = nodeB;
newCon.allowed = allowed;
}
public void LoadLevelData(int index)
@ -227,9 +167,6 @@ public class GameManager : MonoBehaviour
return;
}
connections.Clear();
nodes.Clear();
for (int i = NodeParent.childCount - 1; i >= 0; i--)
DestroyImmediate(NodeParent.GetChild(i).gameObject);
@ -237,22 +174,33 @@ public class GameManager : MonoBehaviour
DestroyImmediate(line.gameObject);
foreach(LevelData.NodeData nodeData in levels[index].nodes)
for(int i = 0; i < levels[index].nodes.Count; i++)
{
var nodeData = levels[index].nodes[i];
var auto = PrefabUtility.InstantiatePrefab(NodePrefab, NodeParent) as GameObject;
auto.transform.localPosition = nodeData.position;
nodes.Add(auto.GetComponent<Node>());
auto.GetComponent<Node>().Owner = nodeData.owner;
auto.GetComponent<Node>().id = i;
}
var currentNodes = GetNodes();
int idx = 0;
foreach (Node node in currentNodes)
node.id = idx++;
currentNodes = GetNodes();
foreach (LevelData.ConnectionData conData in levels[index].connections)
{
AddConnection(nodes[conData.nodeAIndex], nodes[conData.nodeBIndex], conData.allowed);
AddConnection(currentNodes[conData.nodeAIndex], currentNodes[conData.nodeBIndex], conData.allowed);
Debug.Log(conData.nodeAIndex + " - " + conData.nodeBIndex);
}
selectedLevel = index;
minConnectionLength = levels[index].minConnectionLength;
maxConnectionLength = levels[index].maxConnectionLength;
nodeCount = nodes.Count;
nodeCount = currentNodes.Count;
}
public void SaveLevelData(int index = -1)
@ -263,7 +211,7 @@ public class GameManager : MonoBehaviour
data.maxConnectionLength = maxConnectionLength;
// Nodes speichern
foreach (var node in nodes)
foreach (var node in GetNodes())
{
data.nodes.Add(new LevelData.NodeData
{
@ -273,16 +221,14 @@ public class GameManager : MonoBehaviour
}
// Connections speichern
foreach (var con in connections)
foreach (var con in GetConnections())
{
int idxA = nodes.IndexOf(con.nodeA);
int idxB = nodes.IndexOf(con.nodeB);
if (idxA >= 0 && idxB >= 0)
if (con.nodeA.id >= 0 && con.nodeB.id >= 0)
{
data.connections.Add(new LevelData.ConnectionData
{
nodeAIndex = idxA,
nodeBIndex = idxB,
nodeAIndex = con.nodeA.id,
nodeBIndex = con.nodeB.id,
allowed = con.allowed
});
}

View file

@ -11,6 +11,7 @@ public class Node : MonoBehaviour
[SerializeField]
public int Owner = -1;
public int Units;
public int id;
public TMP_Text unitText;
public Material materialOwnerSelf, materialOwnerOther, materialOwnerNone, materialHover;
@ -18,6 +19,8 @@ public class Node : MonoBehaviour
public bool hovered;
public static Node pressedNode = null;
void Awake()
{
UpdateColor();
@ -39,7 +42,7 @@ public class Node : MonoBehaviour
Debug.Log($"Largest Nacho: {count.Count}");
}
unitText.enabled = hovered;
unitText.text = Units.ToString();
unitText.transform.forward = Camera.main.transform.forward;
@ -60,16 +63,13 @@ public class Node : MonoBehaviour
if (gm == null || Application.isPlaying)
return;
List<GameManager.Connection> looseConnections = gm.connections.FindAll(c => c.nodeA == this || c.nodeB == this) ?? new();
List<Connection> looseConnections = gm.GetConnections().FindAll(c => c.nodeA == this || c.nodeB == this) ?? new();
foreach (GameManager.Connection c in looseConnections)
foreach (Connection c in looseConnections)
{
if (c.lineRenderer != null)
DestroyImmediate(c.lineRenderer.gameObject);
gm.connections.Remove(c);
}
gm.FetchAllNodes();
}
public void UpdateTransform()

View file

@ -28,5 +28,5 @@ public class Player : MonoBehaviour
}
public List<Node> GetOwnedNodes() => GameManager.Instance.nodes.FindAll(n => n.Owner == this.id);
public List<Node> GetOwnedNodes() => GameManager.Instance.GetNodes().FindAll(n => n.Owner == this.id);
}

View file

@ -5,6 +5,7 @@ TagManager:
serializedVersion: 3
tags:
- Node
- Connection
layers:
- Default
- TransparentFX