| | |
| |
|
| | using System.Collections; |
| | using UnityEngine; |
| | using Unity.MLAgents; |
| | using Unity.Barracuda; |
| | using Unity.MLAgents.Actuators; |
| | using Unity.MLAgents.Sensors; |
| | using Unity.MLAgentsExamples; |
| |
|
| | public class WallJumpAgent : Agent |
| | { |
| | |
| | int m_Configuration; |
| | |
| | public NNModel noWallBrain; |
| | |
| | public NNModel smallWallBrain; |
| | |
| | public NNModel 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 = FindObjectOfType<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.velocity = Vector3.MoveTowards( |
| | rb.velocity, 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.velocity = 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.velocity = 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); |
| | } |
| | } |
| | } |
| |
|