using System.Collections.Generic; using UnityEngine; public class JointBlock : MonoBehaviour { [Header("Joint Configuration")] [SerializeField] private JointBlockType jointType = JointBlockType.FixedPivot; [SerializeField] private float jointLength = 1f; [Header("Joint Ends")] [SerializeField] private SocketPoint socketEnd1; // One end of the pivot [SerializeField] private SocketPoint socketEnd2; // Other end of the pivot [Header("Physics")] [SerializeField] private bool usePhysics = true; [SerializeField] private float jointBreakForce = Mathf.Infinity; [SerializeField] private float jointBreakTorque = Mathf.Infinity; public List connectedBlocks = new List(); private List createdJoints = new List(); private bool isPlaced = false; private JointPlacementHandler jointPlacementHandler; void Awake() { jointPlacementHandler = JointPlacementHandler.getInstance(); // Auto-find socket ends if not assigned if (socketEnd1 == null || socketEnd2 == null) { SocketPoint[] sockets = GetComponentsInChildren(); if (sockets.Length >= 2) { socketEnd2 = sockets[0]; socketEnd1 = sockets[sockets.Length - 1]; } } // Configure socket types if (socketEnd1 != null) socketEnd1.GetType(); // Should be JointEnd type if (socketEnd2 != null) socketEnd2.GetType(); } public bool TryPlaceJoint(List targetSockets, bool isGhost = false) { if (isPlaced) { Debug.LogWarning("Joint already placed!"); return false; } // Validate we have compatible sockets if (targetSockets.Count < 1) { Debug.LogWarning("Need at least 1 target socket"); return false; } // For a pivot with 2 ends, we need 2 target sockets (one for each end) if (socketEnd1 == null && socketEnd2 == null) { Debug.LogWarning("Pivot needs at least 2 target sockets"); return false; } // Snap position and rotation AlignToSockets(targetSockets, isGhost); // Create physics joints if needed if (usePhysics && !isGhost) { CreatePhysicsJoints(targetSockets); } // Mark as placed and occupied if (!isGhost) { isPlaced = true; } return true; } private void AlignToSockets(List targetSockets, bool isGhost = false) { List alignedSockets = jointPlacementHandler.FindAllSocketsAlignedBetween(targetSockets[0], targetSockets[targetSockets.Count - 1]); if (alignedSockets.Count > 0 && !isGhost) { CreatePhysicsJoints(alignedSockets); // MarkSocketsOccupied(alignedSockets); } } public void CreatePhysicsJoint(GameObject targetObj, SocketPoint socket) { AttachableBlock targetBlock = targetObj.GetComponent(); if (connectedBlocks.Contains(targetBlock) || targetBlock == null) return; Rigidbody jointRb = GetComponent(); if (jointRb == null) jointRb = gameObject.AddComponent(); Rigidbody targetRb = targetObj.GetComponent(); if (targetRb == null) targetRb = targetObj.AddComponent(); // Create appropriate joint type Joint joint = CreateJointByType(targetRb, socket); if (joint != null) { createdJoints.Add(joint); connectedBlocks.Add(targetBlock); targetBlock.RegisterJoint(this); } } private void CreatePhysicsJoints(List targetSockets) { foreach (SocketPoint socket in targetSockets) { GameObject targetObj = socket.transform.root.gameObject; CreatePhysicsJoint(targetObj, socket); } } private Joint CreateJointByType(Rigidbody connectedBody, SocketPoint socket) { switch (jointType) { case JointBlockType.FixedPivot: return CreateFixedJoint(connectedBody); case JointBlockType.HingePivot: return CreateHingeJoint(connectedBody, socket); case JointBlockType.ConfigurablePivot: return CreateConfigurableJoint(connectedBody, socket); default: return CreateFixedJoint(connectedBody); } } private FixedJoint CreateFixedJoint(Rigidbody connectedBody) { FixedJoint joint = gameObject.AddComponent(); joint.connectedBody = connectedBody; joint.breakForce = jointBreakForce; joint.breakTorque = jointBreakTorque; return joint; } private HingeJoint CreateHingeJoint(Rigidbody connectedBody, SocketPoint socket) { HingeJoint joint = gameObject.AddComponent(); joint.connectedBody = connectedBody; joint.axis = transform.InverseTransformDirection(transform.forward); // Rotate around pivot axis joint.anchor = transform.InverseTransformPoint(socket.GetTransform().position); joint.breakForce = jointBreakForce; joint.breakTorque = jointBreakTorque; return joint; } private ConfigurableJoint CreateConfigurableJoint(Rigidbody connectedBody, SocketPoint socket) { ConfigurableJoint joint = gameObject.AddComponent(); joint.connectedBody = connectedBody; joint.anchor = transform.InverseTransformPoint(socket.GetTransform().position); joint.breakForce = jointBreakForce; joint.breakTorque = jointBreakTorque; // Configure as needed (this is customizable) joint.xMotion = ConfigurableJointMotion.Locked; joint.yMotion = ConfigurableJointMotion.Locked; joint.zMotion = ConfigurableJointMotion.Locked; joint.angularXMotion = ConfigurableJointMotion.Free; joint.angularYMotion = ConfigurableJointMotion.Locked; joint.angularZMotion = ConfigurableJointMotion.Locked; return joint; } private void MarkSocketsOccupied(List targetSockets) { foreach (SocketPoint socket in targetSockets) { socket.SetJointOccupied(this.gameObject); } } public void RemoveJoint() { // Destroy all created joints foreach (Joint joint in createdJoints) { if (joint != null) Destroy(joint); } createdJoints.Clear(); // Unregister from blocks foreach (AttachableBlock block in connectedBlocks) { if (block != null) block.UnregisterJoint(this); } connectedBlocks.Clear(); isPlaced = false; } public JointBlockType GetJointType() => jointType; public SocketPoint GetSocketEnd1() => socketEnd1; public SocketPoint GetSocketEnd2() => socketEnd2; public bool IsPlaced() => isPlaced; } public enum JointBlockType { FixedPivot, // Rigid connection (like a fixed axle) HingePivot, // Rotating connection (like a door hinge) ConfigurablePivot // Custom configurable joint }