using System.Collections.Generic; using UnityEngine; using UnityEngine.Serialization; namespace MLAgents { /// /// Used to store relevant information for acting and learning for each body part in agent. /// [System.Serializable] public class ArticulationBodyPart { public ArticulationBody arb; public Transform t; [HideInInspector] public Vector3 startingPos; [HideInInspector] public Quaternion startingRot; [Header("Ground & Target Contact")][Space(10)] public GroundContact groundContact; public TargetContact targetContact; [FormerlySerializedAs("thisJDController")] [HideInInspector] public ArticulatedJointDriveController thisJdController; [Header("Current Joint Settings")][Space(10)] public Vector3 currentEularJointRotation; [HideInInspector] public float currentStrength; public float currentXNormalizedRot; public float currentYNormalizedRot; public float currentZNormalizedRot; /// /// Apply torque according to defined goal `x, y, z` angle and force `strength`. /// public void SetJointTargetRotation(float x, float y, float z) { x = (x + 1f) * 0.5f; y = (y + 1f) * 0.5f; z = (z + 1f) * 0.5f; var xDrive = arb.xDrive; var yDrive = arb.yDrive; var zDrive = arb.zDrive; var xRot = Mathf.Lerp(xDrive.lowerLimit, xDrive.upperLimit, x); var yRot = Mathf.Lerp(yDrive.lowerLimit, yDrive.upperLimit, y); var zRot = Mathf.Lerp(zDrive.lowerLimit, zDrive.upperLimit, z); currentXNormalizedRot = Mathf.InverseLerp(xDrive.lowerLimit, xDrive.upperLimit, xRot); currentYNormalizedRot = Mathf.InverseLerp(yDrive.lowerLimit, yDrive.upperLimit, yRot); currentZNormalizedRot = Mathf.InverseLerp(zDrive.lowerLimit, zDrive.upperLimit, zRot); xDrive.target = xRot; yDrive.target = yRot; zDrive.target = zRot; arb.xDrive = xDrive; arb.yDrive = yDrive; arb.zDrive = zDrive; currentEularJointRotation = new Vector3(xRot, yRot, zRot); } public void SetJointStrength(float strength) { var xDrive = arb.xDrive; var yDrive = arb.yDrive; var zDrive = arb.zDrive; var rawVal = (strength + 1f) * 0.5f * thisJdController.maxJointForceLimit; xDrive.stiffness = yDrive.stiffness = zDrive.stiffness = thisJdController.maxJointSpring; xDrive.damping = yDrive.damping = zDrive.damping = thisJdController.jointDampen; xDrive.forceLimit = yDrive.forceLimit = zDrive.forceLimit = rawVal; arb.xDrive = xDrive; arb.yDrive = yDrive; arb.zDrive = zDrive; currentStrength = rawVal; } } public class ArticulatedJointDriveController : MonoBehaviour { [Header("Joint Drive Settings")][Space(10)] public float maxJointSpring; public float jointDampen; public float maxJointForceLimit; float m_FacingDot; [HideInInspector] public Dictionary bodyPartsDict = new Dictionary(); [HideInInspector] public List bodyPartsList = new List(); /// /// Reset BodyPart list and dictionary. /// public void Reset() { bodyPartsDict.Clear(); bodyPartsList.Clear(); } /// /// Create BodyPart object and add it to dictionary. /// public void SetupBodyPart(Transform t) { // Either parent(in case of legs) or game object itself(in case of body) must have ArticulationBody var arb = t.GetComponent(); if (arb == null) arb = t.parent.GetComponent(); var bp = new ArticulationBodyPart() { arb = arb, t = t, startingPos = t.position, startingRot = t.rotation }; // Add & setup the ground contact script bp.groundContact = t.GetComponent(); if (!bp.groundContact) { bp.groundContact = t.gameObject.AddComponent(); bp.groundContact.agent = gameObject.GetComponent(); } else { bp.groundContact.agent = gameObject.GetComponent(); } // Add & setup the target contact script bp.targetContact = t.GetComponent(); if (!bp.targetContact) { bp.targetContact = t.gameObject.AddComponent(); } bp.thisJdController = this; bodyPartsDict.Add(t, bp); bodyPartsList.Add(bp); } public static Transform FindBodyPartByName(Transform rootBody, string bodyPartName) { Queue queue = new Queue(); queue.Enqueue(rootBody); while (queue.Count > 0) { Transform child = queue.Dequeue(); if (child.name == bodyPartName) return child; foreach(Transform t in child) queue.Enqueue(t); } return null; } } }