using System.Collections; using System.Collections.Generic; //using Codice.Utils; using UnityEngine; public class JointPlacementHandler : MonoBehaviour { private static JointPlacementHandler instance; [SerializeField] private float socketSearchRadius = 1f; [SerializeField] private LayerMask blockLayerMask; public static JointPlacementHandler getInstance() => instance; void Awake() { if (instance == null) instance = this; else Destroy(gameObject); } /// /// Finds all sockets located on the line segment between two specific sockets. /// Useful for identifying all "holes" a pivot passes through in a stack of links. /// public List FindAllSocketsAlignedBetween(SocketPoint start, SocketPoint end) { List alignedSockets = new List(); Vector3 startPos = start.GetTransform().position; Vector3 endPos = end.GetTransform().position; Collider[] candidates; if (startPos == endPos) { // Use a Sphere search to find everything around the pos candidates = Physics.OverlapSphere(startPos, socketSearchRadius, blockLayerMask); } else { // Use a Capsule search to find everything in the "tunnel" between the two ends candidates = Physics.OverlapCapsule(startPos, endPos, socketSearchRadius, blockLayerMask); } foreach (Collider col in candidates) { // Don't detect sockets on the joint itself if (col.transform.root == transform.root) continue; SocketPoint[] socketsInBlock = col.GetComponentsInChildren(); foreach (SocketPoint p in socketsInBlock) { // Skip the start and end points themselves if (p == start || p == end) continue; // 2. Mathematically check if the socket lies on the line segment if (IsPointOnSegment(startPos, endPos, p.GetTransform().position, 0.1f)) { alignedSockets.Add(p); } } } // remove start and end from it if (alignedSockets.Contains(start)) alignedSockets.Remove(start); if (alignedSockets.Contains(end)) alignedSockets.Remove(end); return alignedSockets; } /// /// Helper to check if a point P is on the line segment AB within a certain tolerance /// private bool IsPointOnSegment(Vector3 A, Vector3 B, Vector3 P, float tolerance) { Vector3 ab = B - A; Vector3 ap = P - A; // Project point P onto the line AB to find its "position" along the line (t) float t = Vector3.Dot(ap, ab) / ab.sqrMagnitude; // Check if the projection is within the segment (between 0 and 1) if (t < 0 || t > 1) return false; // Calculate the distance from the point to the line Vector3 nearestPointOnSegment = A + t * ab; float distToLine = Vector3.Distance(P, nearestPointOnSegment); return distToLine <= tolerance; } /// /// Finds compatible sockets for a joint block to connect to /// public List FindTargetSocketsForJoint(JointBlock jointBlock, Vector3 searchPosition) { List validSockets = new List(); SocketPoint end1 = jointBlock.GetSocketEnd1(); SocketPoint end2 = jointBlock.GetSocketEnd2(); if (end1 == null) { Debug.LogWarning("Joint block has no socket ends!"); return validSockets; } // Find all nearby sockets Collider[] nearbyColliders = Physics.OverlapSphere(searchPosition, socketSearchRadius, blockLayerMask); List nearbySockets = new List(); foreach (Collider col in nearbyColliders) { // Don't detect sockets on the joint itself if (col.transform.root == jointBlock.transform.root) continue; SocketPoint[] sockets = col.GetComponentsInChildren(); foreach (SocketPoint socket in sockets) { if (socket.CanAccept(end1, true)) { nearbySockets.Add(socket); } } } if (nearbySockets.Count == 0) { Debug.Log("No compatible sockets found nearby"); return validSockets; } SocketPoint nearestSocket = FindClosestSocket(searchPosition, nearbySockets); if (nearestSocket != null) validSockets.Add(nearestSocket); // For a two-ended joint (like a pivot/axle) if (end2 != null) { // Find the two closest compatible sockets SocketPoint socket2 = FindSocketAtJointEnd(end1, end1, end2); if (socket2 != null) validSockets.Add(socket2); } else { // Single-ended joint - just find closest socket SocketPoint closest = FindClosestSocket(end1.GetTransform().position, nearbySockets); if (closest != null) validSockets.Add(closest); } return validSockets; } private SocketPoint FindSocketAtJointEnd(SocketPoint firstSocket, SocketPoint end1, SocketPoint end2) { // 1. Calculate the required distance (the physical length of the joint) float jointLength = Vector3.Distance(end1.GetTransform().position, end2.GetTransform().position); // 2. Define the search direction (the negative normal of the first socket) Vector3 searchDirection = -firstSocket.GetNormal(); // 3. Calculate exactly where the second end of the joint should land Vector3 targetPosition = firstSocket.GetTransform().position + (searchDirection * jointLength); // 4. Look for sockets near that target position Collider[] candidates = Physics.OverlapSphere(targetPosition, socketSearchRadius, blockLayerMask); SocketPoint bestMatch = null; float closestDistToTarget = float.MaxValue; foreach (Collider col in candidates) { // Don't connect the second end to the same block as the first end // (Unless your design specifically allows 180-degree internal loops) if (col.transform.root == firstSocket.GetTransform().root) continue; SocketPoint[] sockets = col.GetComponentsInChildren(); foreach (SocketPoint socket in sockets) { // Skip the first socket and occupied ones if (socket == firstSocket || socket.IsOccupied()) continue; // Check if this socket can accept the 'end2' type if (!socket.CanAccept(end2, true)) continue; // Optional: Check if the socket's normal is facing the right way // (e.g., the second socket should face the SAME way as the first for a pass-through) float angleMatch = Vector3.Dot(socket.GetNormal(), firstSocket.GetNormal()); if (angleMatch < 0.9f) continue; // Only accept if normals are roughly aligned float distToTarget = Vector3.Distance(socket.GetTransform().position, targetPosition); if (distToTarget < closestDistToTarget) { closestDistToTarget = distToTarget; bestMatch = socket; } } } return bestMatch; } private SocketPoint FindClosestSocket(Vector3 position, List sockets) { SocketPoint closest = null; float minDistance = float.MaxValue; foreach (SocketPoint socket in sockets) { float distance = Vector3.Distance(position, socket.GetTransform().position); if (distance < minDistance) { minDistance = distance; closest = socket; } } return closest; } }