| |
|
|
| using System.Collections; |
| using UnityEngine; |
| using Unity.MLAgents; |
| using Unity.InferenceEngine; |
| using Unity.MLAgents.Actuators; |
| using Unity.MLAgents.Sensors; |
| using Unity.MLAgentsExamples; |
| using Random = UnityEngine.Random; |
|
|
| public class WallJumpAgent : Agent |
| { |
| |
| int m_Configuration; |
| |
| public ModelAsset noWallBrain; |
| |
| public ModelAsset smallWallBrain; |
| |
| public ModelAsset bigWallBrain; |
|
|
| public GameObject ground; |
| public GameObject spawnArea; |
| Bounds m_SpawnAreaBounds; |
|
|
|
|
| public GameObject goal; |
| public GameObject shortBlock; |
| public GameObject wall; |
| Rigidbody m_ShortBlockRb; |
| Rigidbody m_AgentRb; |
| Material m_GroundMaterial; |
| Renderer m_GroundRenderer; |
| WallJumpSettings m_WallJumpSettings; |
|
|
| public float jumpingTime; |
| public float jumpTime; |
| |
| |
| public float fallingForce; |
| |
| public Collider[] hitGroundColliders = new Collider[3]; |
| Vector3 m_JumpTargetPos; |
| Vector3 m_JumpStartingPos; |
|
|
| string m_NoWallBehaviorName = "SmallWallJump"; |
| string m_SmallWallBehaviorName = "SmallWallJump"; |
| string m_BigWallBehaviorName = "BigWallJump"; |
|
|
| EnvironmentParameters m_ResetParams; |
|
|
| public override void Initialize() |
| { |
| m_WallJumpSettings = FindAnyObjectByType<WallJumpSettings>(); |
| m_Configuration = Random.Range(0, 5); |
|
|
| m_AgentRb = GetComponent<Rigidbody>(); |
| m_ShortBlockRb = shortBlock.GetComponent<Rigidbody>(); |
| m_SpawnAreaBounds = spawnArea.GetComponent<Collider>().bounds; |
| m_GroundRenderer = ground.GetComponent<Renderer>(); |
| m_GroundMaterial = m_GroundRenderer.material; |
|
|
| spawnArea.SetActive(false); |
|
|
| m_ResetParams = Academy.Instance.EnvironmentParameters; |
|
|
| |
| var modelOverrider = GetComponent<ModelOverrider>(); |
| if (modelOverrider.HasOverrides) |
| { |
| noWallBrain = modelOverrider.GetModelForBehaviorName(m_NoWallBehaviorName); |
| m_NoWallBehaviorName = ModelOverrider.GetOverrideBehaviorName(m_NoWallBehaviorName); |
|
|
| smallWallBrain = modelOverrider.GetModelForBehaviorName(m_SmallWallBehaviorName); |
| m_SmallWallBehaviorName = ModelOverrider.GetOverrideBehaviorName(m_SmallWallBehaviorName); |
|
|
| bigWallBrain = modelOverrider.GetModelForBehaviorName(m_BigWallBehaviorName); |
| m_BigWallBehaviorName = ModelOverrider.GetOverrideBehaviorName(m_BigWallBehaviorName); |
| } |
| } |
|
|
| |
| public void Jump() |
| { |
| jumpingTime = 0.2f; |
| m_JumpStartingPos = m_AgentRb.position; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public bool DoGroundCheck(bool smallCheck) |
| { |
| if (!smallCheck) |
| { |
| hitGroundColliders = new Collider[3]; |
| var o = gameObject; |
| Physics.OverlapBoxNonAlloc( |
| o.transform.position + new Vector3(0, -0.05f, 0), |
| new Vector3(0.95f / 2f, 0.5f, 0.95f / 2f), |
| hitGroundColliders, |
| o.transform.rotation); |
| var grounded = false; |
| foreach (var col in hitGroundColliders) |
| { |
| if (col != null && col.transform != transform && |
| (col.CompareTag("walkableSurface") || |
| col.CompareTag("block") || |
| col.CompareTag("wall"))) |
| { |
| grounded = true; |
| break; |
| } |
| } |
| return grounded; |
| } |
| else |
| { |
| RaycastHit hit; |
| Physics.Raycast(transform.position + new Vector3(0, -0.05f, 0), -Vector3.up, out hit, |
| 1f); |
|
|
| if (hit.collider != null && |
| (hit.collider.CompareTag("walkableSurface") || |
| hit.collider.CompareTag("block") || |
| hit.collider.CompareTag("wall")) |
| && hit.normal.y > 0.95f) |
| { |
| return true; |
| } |
|
|
| return false; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| void MoveTowards( |
| Vector3 targetPos, Rigidbody rb, float targetVel, float maxVel) |
| { |
| var moveToPos = targetPos - rb.worldCenterOfMass; |
| var velocityTarget = Time.fixedDeltaTime * targetVel * moveToPos; |
| if (float.IsNaN(velocityTarget.x) == false) |
| { |
| rb.linearVelocity = Vector3.MoveTowards( |
| rb.linearVelocity, velocityTarget, maxVel); |
| } |
| } |
|
|
| public override void CollectObservations(VectorSensor sensor) |
| { |
| var agentPos = m_AgentRb.position - ground.transform.position; |
|
|
| sensor.AddObservation(agentPos / 20f); |
| sensor.AddObservation(DoGroundCheck(true) ? 1 : 0); |
| } |
|
|
| |
| |
| |
| |
| public Vector3 GetRandomSpawnPos() |
| { |
| var randomPosX = Random.Range(-m_SpawnAreaBounds.extents.x, |
| m_SpawnAreaBounds.extents.x); |
| var randomPosZ = Random.Range(-m_SpawnAreaBounds.extents.z, |
| m_SpawnAreaBounds.extents.z); |
|
|
| var randomSpawnPos = spawnArea.transform.position + |
| new Vector3(randomPosX, 0.45f, randomPosZ); |
| return randomSpawnPos; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| IEnumerator GoalScoredSwapGroundMaterial(Material mat, float time) |
| { |
| m_GroundRenderer.material = mat; |
| yield return new WaitForSeconds(time); |
| m_GroundRenderer.material = m_GroundMaterial; |
| } |
|
|
| public void MoveAgent(ActionSegment<int> act) |
| { |
| AddReward(-0.0005f); |
| var smallGrounded = DoGroundCheck(true); |
| var largeGrounded = DoGroundCheck(false); |
|
|
| var dirToGo = Vector3.zero; |
| var rotateDir = Vector3.zero; |
| var dirToGoForwardAction = act[0]; |
| var rotateDirAction = act[1]; |
| var dirToGoSideAction = act[2]; |
| var jumpAction = act[3]; |
|
|
| if (dirToGoForwardAction == 1) |
| dirToGo = (largeGrounded ? 1f : 0.5f) * 1f * transform.forward; |
| else if (dirToGoForwardAction == 2) |
| dirToGo = (largeGrounded ? 1f : 0.5f) * -1f * transform.forward; |
| if (rotateDirAction == 1) |
| rotateDir = transform.up * -1f; |
| else if (rotateDirAction == 2) |
| rotateDir = transform.up * 1f; |
| if (dirToGoSideAction == 1) |
| dirToGo = (largeGrounded ? 1f : 0.5f) * -0.6f * transform.right; |
| else if (dirToGoSideAction == 2) |
| dirToGo = (largeGrounded ? 1f : 0.5f) * 0.6f * transform.right; |
| if (jumpAction == 1) |
| if ((jumpingTime <= 0f) && smallGrounded) |
| { |
| Jump(); |
| } |
|
|
| transform.Rotate(rotateDir, Time.fixedDeltaTime * 300f); |
| m_AgentRb.AddForce(dirToGo * m_WallJumpSettings.agentRunSpeed, |
| ForceMode.VelocityChange); |
|
|
| if (jumpingTime > 0f) |
| { |
| m_JumpTargetPos = |
| new Vector3(m_AgentRb.position.x, |
| m_JumpStartingPos.y + m_WallJumpSettings.agentJumpHeight, |
| m_AgentRb.position.z) + dirToGo; |
| MoveTowards(m_JumpTargetPos, m_AgentRb, m_WallJumpSettings.agentJumpVelocity, |
| m_WallJumpSettings.agentJumpVelocityMaxChange); |
| } |
|
|
| if (!(jumpingTime > 0f) && !largeGrounded) |
| { |
| m_AgentRb.AddForce( |
| Vector3.down * fallingForce, ForceMode.Acceleration); |
| } |
| jumpingTime -= Time.fixedDeltaTime; |
| } |
|
|
| public override void OnActionReceived(ActionBuffers actionBuffers) |
|
|
| { |
| MoveAgent(actionBuffers.DiscreteActions); |
| if ((!Physics.Raycast(m_AgentRb.position, Vector3.down, 20)) |
| || (!Physics.Raycast(m_ShortBlockRb.position, Vector3.down, 20))) |
| { |
| SetReward(-1f); |
| EndEpisode(); |
| ResetBlock(m_ShortBlockRb); |
| StartCoroutine( |
| GoalScoredSwapGroundMaterial(m_WallJumpSettings.failMaterial, .5f)); |
| } |
| } |
|
|
| public override void Heuristic(in ActionBuffers actionsOut) |
| { |
| var discreteActionsOut = actionsOut.DiscreteActions; |
| if (Input.GetKey(KeyCode.D)) |
| { |
| discreteActionsOut[1] = 2; |
| } |
| if (Input.GetKey(KeyCode.W)) |
| { |
| discreteActionsOut[0] = 1; |
| } |
| if (Input.GetKey(KeyCode.A)) |
| { |
| discreteActionsOut[1] = 1; |
| } |
| if (Input.GetKey(KeyCode.S)) |
| { |
| discreteActionsOut[0] = 2; |
| } |
| discreteActionsOut[3] = Input.GetKey(KeyCode.Space) ? 1 : 0; |
| } |
|
|
| |
| void OnTriggerStay(Collider col) |
| { |
| if (col.gameObject.CompareTag("goal") && DoGroundCheck(true)) |
| { |
| SetReward(1f); |
| EndEpisode(); |
| StartCoroutine( |
| GoalScoredSwapGroundMaterial(m_WallJumpSettings.goalScoredMaterial, 2)); |
| } |
| } |
|
|
| |
| void ResetBlock(Rigidbody blockRb) |
| { |
| blockRb.transform.position = GetRandomSpawnPos(); |
| blockRb.linearVelocity = Vector3.zero; |
| blockRb.angularVelocity = Vector3.zero; |
| } |
|
|
| public override void OnEpisodeBegin() |
| { |
| ResetBlock(m_ShortBlockRb); |
| transform.localPosition = new Vector3( |
| 18 * (Random.value - 0.5f), 1, -12); |
| m_Configuration = Random.Range(0, 5); |
| m_AgentRb.linearVelocity = default(Vector3); |
| } |
|
|
| void FixedUpdate() |
| { |
| if (m_Configuration != -1) |
| { |
| ConfigureAgent(m_Configuration); |
| m_Configuration = -1; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void ConfigureAgent(int config) |
| { |
| var localScale = wall.transform.localScale; |
| if (config == 0) |
| { |
| localScale = new Vector3( |
| localScale.x, |
| m_ResetParams.GetWithDefault("no_wall_height", 0), |
| localScale.z); |
| wall.transform.localScale = localScale; |
| SetModel(m_NoWallBehaviorName, noWallBrain); |
| } |
| else if (config == 1) |
| { |
| localScale = new Vector3( |
| localScale.x, |
| m_ResetParams.GetWithDefault("small_wall_height", 4), |
| localScale.z); |
| wall.transform.localScale = localScale; |
| SetModel(m_SmallWallBehaviorName, smallWallBrain); |
| } |
| else |
| { |
| var height = m_ResetParams.GetWithDefault("big_wall_height", 8); |
| localScale = new Vector3( |
| localScale.x, |
| height, |
| localScale.z); |
| wall.transform.localScale = localScale; |
| SetModel(m_BigWallBehaviorName, bigWallBrain); |
| } |
| } |
| } |
|
|