|
|
|
|
|
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); |
|
} |
|
} |
|
} |
|
|