using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using Object = UnityEngine.Object; using Random = UnityEngine.Random; // Cache setup props and recreate joints if different. // Joints reset // Break force by mass // Different break variation at every init // Runtime demolition jointing // Joint management during sim: // 1. ReCreate joint after few frames after break // + time delay // Preview for current force and iteration count, with color // Label for total amount of joints at start / existing amount // Set position based on size proportion // TODO runtime joint creation for few frames to stick with neibs for better friction // setup deform list at awake if Joints were precached namespace RayFire { [Serializable] public class RFJoint { public int id; public int df; // Amount of happened deformations // Components public ConfigurableJoint jn; public Rigidbody r1; public Rigidbody r2; // Save props public int br; public int an; public int dm; public RFJoint(int Id, int Br, int An, int Dm) { id = Id; br = Br; an = An; dm = Dm; } } [Serializable] public class RFJointProperties { public enum RFJointBreakType { Breakable = 0, Unbreakable = 1 } // Main properties public bool enable; public RFJointBreakType breakType = RFJointBreakType.Breakable; public int breakForce = 100; public int breakForceVar = 10; public bool forceByMass; public bool varInAwake; public int angleLimit = 10; public int angleLimitVar = 10; public int damper = 1000; // Deformation properties public bool deformEnable = false; public int deformCount = 20; public float stiffFrc = 0.75f; public int stiffAbs = 50; public int bend = 0; public int percentage = 100; public float weakening = 0.8f; public int initAmount; // Lists [NonSerialized] public List deformList; public List jointList = new List(); // Static public static SoftJointLimit jointLimit = new SoftJointLimit(); public static SoftJointLimitSpring spring = new SoftJointLimitSpring(); // Copy props public void CopyTo(RFJointProperties trg) { trg.enable = enable; trg.breakType = breakType; trg.breakForce = breakForce; trg.breakForceVar = breakForceVar; trg.forceByMass = forceByMass; trg.varInAwake = varInAwake; trg.angleLimit = angleLimit; trg.angleLimitVar = angleLimitVar; trg.damper = damper; trg.deformEnable = deformEnable; trg.deformCount = deformCount; trg.stiffFrc = stiffFrc; trg.stiffAbs = stiffAbs; trg.bend = bend; trg.percentage = percentage; trg.weakening = weakening; } /// ///////////////////////////////////////////////////////// /// Create /// ///////////////////////////////////////////////////////// // Create joint public static void CreateJoints (RFCluster cluster, RFJointProperties joints) { // Skip if already has joints if (joints.HasJoints == true) return; // Prepare empty list joints.EmptyList (); // Set unchecked state RFShard.SetUnchecked (cluster.shards); // Create joints int id = 0; foreach (var shard in cluster.shards) { foreach (var neib in shard.neibShards) { // Skip neib with created joints if (neib.check == true) continue; // Create joint RFJoint rfJoint = CreateJoint (shard, neib, joints, id++); // Collect joints.jointList.Add (rfJoint); /* // Create joint RFJoint rfJoint2 = CreateJoint (shard, neib, joints, id++); // Collect joints.jointList.Add (rfJoint2); // Create joint RFJoint rfJoint3 = CreateJoint (shard, neib, joints, id++); // Collect joints.jointList.Add (rfJoint3); */ } shard.check = true; } // Save init amount joints.initAmount = joints.jointList.Count; // Set unchecked state RFShard.SetUnchecked (cluster.shards); // Angular Motion SetAngularMotion(joints.angleLimit, joints.angleLimitVar, joints.jointList); // Angular Spring if (joints.damper > 0) SetSpring(joints.damper, joints.jointList); // Break Force if (joints.breakType == RFJointBreakType.Breakable) SetBreakForce (joints.breakForce, joints.breakForceVar, joints.jointList, joints.forceByMass); // Save final props for reset SaveProperties(joints.jointList); } // Create joint public static RFJoint CreateJoint(RFShard shard, RFShard neib, RFJointProperties joints, int id) { // Create joint RFJoint rfJoint = new RFJoint(id, joints.breakForce, joints.angleLimit, joints.damper); rfJoint.jn = shard.tm.gameObject.AddComponent(); rfJoint.r1 = shard.rb; rfJoint.r2 = neib.rb; // Setup joint rfJoint.jn.connectedBody = neib.rb; rfJoint.jn.enableCollision = false; rfJoint.jn.enablePreprocessing = true; // Set joint position and axis SetTransform (shard.tm, neib.tm, rfJoint.jn); // Position Motion SetPositionMotion (rfJoint.jn); return rfJoint; } /// ///////////////////////////////////////////////////////// /// Motion /// ///////////////////////////////////////////////////////// // Position Motion public static void SetPositionMotion (ConfigurableJoint joint) { joint.xMotion = ConfigurableJointMotion.Locked; joint.yMotion = ConfigurableJointMotion.Locked; joint.zMotion = ConfigurableJointMotion.Locked; } // Angular Motion public static void SetAngularMotion(float angleLimit, RFJoint joint) { if (angleLimit == 0) { joint.jn.angularXMotion = ConfigurableJointMotion.Locked; joint.jn.angularYMotion = ConfigurableJointMotion.Locked; joint.jn.angularZMotion = ConfigurableJointMotion.Locked; } else { joint.jn.angularXMotion = ConfigurableJointMotion.Limited; joint.jn.angularYMotion = ConfigurableJointMotion.Limited; joint.jn.angularZMotion = ConfigurableJointMotion.Limited; jointLimit.limit = angleLimit; joint.jn.angularYLimit = jointLimit; joint.jn.angularZLimit = jointLimit; } } // Angular Motion public static void SetAngularMotion(float angleLimit, int var, List jointList) { Random.InitState (0); if (jointList != null && jointList.Count > 0) for (int i = 0; i < jointList.Count; i++) if (jointList[i] != null) SetAngularMotion (angleLimit + (int)Random.Range(0, angleLimit * var / 100f), jointList[i]); } /// ///////////////////////////////////////////////////////// /// Damper /// ///////////////////////////////////////////////////////// // Joint spring public static void SetSpring(int damper, ConfigurableJoint joint) { spring.damper = damper; spring.spring = damper; joint.angularYZLimitSpring = spring; } // Joint spring public static void SetSpring(int damper, List jointList) { if (jointList != null && jointList.Count > 0) for (int i = 0; i < jointList.Count; i++) if (jointList[i] != null) SetSpring (damper, jointList[i].jn); } /// ///////////////////////////////////////////////////////// /// Break Force /// ///////////////////////////////////////////////////////// // Single Joint break force public static void SetBreakForce(int force, ConfigurableJoint joint) { joint.breakForce = force; } // Joint List break force public static void SetBreakForce(int force, List jointList) { if (jointList != null && jointList.Count > 0) for (int i = 0; i < jointList.Count; i++) if (jointList[i] != null) SetBreakForce (force, jointList[i].jn); } // Joint List break force variation public static void SetBreakForce(int force, int var, List jointList, bool byMass) { if (byMass == false) { Random.InitState (0); if (jointList != null && jointList.Count > 0) for (int i = 0; i < jointList.Count; i++) if (jointList[i] != null) SetBreakForce (force + Random.Range (0, var), jointList[i].jn); } else { } } /// ///////////////////////////////////////////////////////// /// Transform /// ///////////////////////////////////////////////////////// // Set joint position and axis public static void SetTransform (Transform tm1, Transform tm2, ConfigurableJoint joint) { // Position Vector3 vector = (tm2.position - tm1.position) / 2f; joint.anchor = tm1.InverseTransformVector (vector); //joint.anchor = tm1.InverseTransformVector (vector) + new Vector3 (Random.Range (-0.2f, 0.2f), Random.Range (-0.2f, 0.2f), Random.Range (-0.2f, 0.2f)); // Axis joint.axis = tm1.InverseTransformDirection (vector); } /// ///////////////////////////////////////////////////////// /// List /// ///////////////////////////////////////////////////////// public bool HasJoints { get { return jointList != null && jointList.Count > 0; } } public bool HasDeforms { get { return deformList != null && deformList.Count > 0; } } // Prepare empty list public void EmptyList () { if (jointList == null) jointList = new List(); else jointList.Clear(); } // Destroy all existing joints public void DestroyJoints() { if (HasJoints == true) { for (int i = 0; i < jointList.Count; i++) if (jointList[i] != null && jointList[i].jn) Object.DestroyImmediate (jointList[i].jn); jointList.Clear(); } } /// ///////////////////////////////////////////////////////// /// Properties /// ///////////////////////////////////////////////////////// // Save properties in joint class to reset later public static void SaveProperties(List joints) { for (int i = 0; i < joints.Count; i++) { joints[i].br = (int)joints[i].jn.breakForce; joints[i].an = (int)joints[i].jn.angularYLimit.limit; joints[i].dm = (int)joints[i].jn.angularYZLimitSpring.damper; } } /// ///////////////////////////////////////////////////////// /// Deformation /// ///////////////////////////////////////////////////////// // Create joint public static RFJoint DeformJoint(RFJoint joint, RFJointProperties joints) { // ReSet joint position and axis SetTransform (joint.r1.transform, joint.r2.transform, joint.jn); // Bending if (joints.bend > 0) SetAngularMotion (joint.an + joints.bend, joint); // Weakening if (joint.br > 0 && joints.weakening > 0) SetBreakForce ((int)(joint.jn.breakForce * (1f - joints.weakening)), joint.jn); // Iterate deformation joint.df++; return joint; } // Prepare deformation public static void SetDeformation(RayfireConnectivity scr) { if (scr.joints.deformEnable == true) { if (scr.joints.HasJoints == true) { scr.joints.deformList = new List(); for (int i = 0; i < scr.joints.jointList.Count; i++) if (scr.joints.percentage == 100 || scr.joints.percentage > Random.Range (1, 100)) scr.joints.deformList.Add (scr.joints.jointList[i]); scr.StartCoroutine (scr.joints.DeformationCor()); } } } // Joints deformation cor public IEnumerator DeformationCor() { // Deformation disabled if (deformEnable == false) yield break; // Has no joints if (HasJoints == false) yield break; // Repeat every frame while (deformEnable == true && deformList.Count > 0) { // Check joints current force for (int i = deformList.Count - 1; i >= 0; i--) { // Joint broken if (deformList[i].jn == null) { deformList.RemoveAt (i); continue; } // Skip if exceeded deformation count if (deformList[i].df >= deformCount) { deformList.RemoveAt (i); continue; } // Stiffness check if (breakType == RFJointBreakType.Breakable) { if (deformList[i].jn.currentForce.magnitude < breakForce * stiffFrc) continue; } else { if (deformList[i].jn.currentForce.magnitude < stiffAbs) continue; } // Reset joint tm DeformJoint (deformList[i], this); } yield return null; } } } }