//Put this script on your blue cube.
using System.Collections;
using UnityEngine;
using MLAgents;
public class PushAgentBasic : Agent
{
///
/// The ground. The bounds are used to spawn the elements.
///
public GameObject ground;
public GameObject area;
///
/// The area bounds.
///
[HideInInspector]
public Bounds areaBounds;
PushBlockAcademy m_Academy;
///
/// The goal to push the block to.
///
public GameObject goal;
///
/// The block to be pushed to the goal.
///
public GameObject block;
///
/// Detects when the block touches the goal.
///
[HideInInspector]
public GoalDetect goalDetect;
public bool useVectorObs;
Rigidbody m_BlockRb; //cached on initialization
Rigidbody m_AgentRb; //cached on initialization
Material m_GroundMaterial; //cached on Awake()
RayPerception m_RayPer;
float[] m_RayAngles = { 0f, 45f, 90f, 135f, 180f, 110f, 70f };
string[] m_DetectableObjects = { "block", "goal", "wall" };
///
/// We will be changing the ground material based on success/failue
///
Renderer m_GroundRenderer;
void Awake()
{
m_Academy = FindObjectOfType(); //cache the academy
}
public override void InitializeAgent()
{
base.InitializeAgent();
goalDetect = block.GetComponent();
goalDetect.agent = this;
m_RayPer = GetComponent();
// Cache the agent rigidbody
m_AgentRb = GetComponent();
// Cache the block rigidbody
m_BlockRb = block.GetComponent();
// Get the ground's bounds
areaBounds = ground.GetComponent().bounds;
// Get the ground renderer so we can change the material when a goal is scored
m_GroundRenderer = ground.GetComponent();
// Starting material
m_GroundMaterial = m_GroundRenderer.material;
SetResetParameters();
}
public override void CollectObservations()
{
if (useVectorObs)
{
var rayDistance = 12f;
AddVectorObs(m_RayPer.Perceive(rayDistance, m_RayAngles, m_DetectableObjects, 0f, 0f));
AddVectorObs(m_RayPer.Perceive(rayDistance, m_RayAngles, m_DetectableObjects, 1.5f, 0f));
}
}
///
/// Use the ground's bounds to pick a random spawn position.
///
public Vector3 GetRandomSpawnPos()
{
var foundNewSpawnLocation = false;
var randomSpawnPos = Vector3.zero;
while (foundNewSpawnLocation == false)
{
var randomPosX = Random.Range(-areaBounds.extents.x * m_Academy.spawnAreaMarginMultiplier,
areaBounds.extents.x * m_Academy.spawnAreaMarginMultiplier);
var randomPosZ = Random.Range(-areaBounds.extents.z * m_Academy.spawnAreaMarginMultiplier,
areaBounds.extents.z * m_Academy.spawnAreaMarginMultiplier);
randomSpawnPos = ground.transform.position + new Vector3(randomPosX, 1f, randomPosZ);
if (Physics.CheckBox(randomSpawnPos, new Vector3(2.5f, 0.01f, 2.5f)) == false)
{
foundNewSpawnLocation = true;
}
}
return randomSpawnPos;
}
///
/// Called when the agent moves the block into the goal.
///
public void ScoredAGoal()
{
// We use a reward of 5.
AddReward(5f);
// By marking an agent as done AgentReset() will be called automatically.
Done();
// Swap ground material for a bit to indicate we scored.
StartCoroutine(GoalScoredSwapGroundMaterial(m_Academy.goalScoredMaterial, 0.5f));
}
///
/// Swap ground material, wait time seconds, then swap back to the regular material.
///
IEnumerator GoalScoredSwapGroundMaterial(Material mat, float time)
{
m_GroundRenderer.material = mat;
yield return new WaitForSeconds(time); // Wait for 2 sec
m_GroundRenderer.material = m_GroundMaterial;
}
///
/// Moves the agent according to the selected action.
///
public void MoveAgent(float[] act)
{
var dirToGo = Vector3.zero;
var rotateDir = Vector3.zero;
var action = Mathf.FloorToInt(act[0]);
// Goalies and Strikers have slightly different action spaces.
switch (action)
{
case 1:
dirToGo = transform.forward * 1f;
break;
case 2:
dirToGo = transform.forward * -1f;
break;
case 3:
rotateDir = transform.up * 1f;
break;
case 4:
rotateDir = transform.up * -1f;
break;
case 5:
dirToGo = transform.right * -0.75f;
break;
case 6:
dirToGo = transform.right * 0.75f;
break;
}
transform.Rotate(rotateDir, Time.fixedDeltaTime * 200f);
m_AgentRb.AddForce(dirToGo * m_Academy.agentRunSpeed,
ForceMode.VelocityChange);
}
///
/// Called every step of the engine. Here the agent takes an action.
///
public override void AgentAction(float[] vectorAction, string textAction)
{
// Move the agent using the action.
MoveAgent(vectorAction);
// Penalty given each step to encourage agent to finish task quickly.
AddReward(-1f / agentParameters.maxStep);
}
///
/// Resets the block position and velocities.
///
void ResetBlock()
{
// Get a random position for the block.
block.transform.position = GetRandomSpawnPos();
// Reset block velocity back to zero.
m_BlockRb.velocity = Vector3.zero;
// Reset block angularVelocity back to zero.
m_BlockRb.angularVelocity = Vector3.zero;
}
///
/// In the editor, if "Reset On Done" is checked then AgentReset() will be
/// called automatically anytime we mark done = true in an agent script.
///
public override void AgentReset()
{
var rotation = Random.Range(0, 4);
var rotationAngle = rotation * 90f;
area.transform.Rotate(new Vector3(0f, rotationAngle, 0f));
ResetBlock();
transform.position = GetRandomSpawnPos();
m_AgentRb.velocity = Vector3.zero;
m_AgentRb.angularVelocity = Vector3.zero;
SetResetParameters();
}
public void SetGroundMaterialFriction()
{
var resetParams = m_Academy.resetParameters;
var groundCollider = ground.GetComponent();
groundCollider.material.dynamicFriction = resetParams["dynamic_friction"];
groundCollider.material.staticFriction = resetParams["static_friction"];
}
public void SetBlockProperties()
{
var resetParams = m_Academy.resetParameters;
//Set the scale of the block
m_BlockRb.transform.localScale = new Vector3(resetParams["block_scale"], 0.75f, resetParams["block_scale"]);
// Set the drag of the block
m_BlockRb.drag = resetParams["block_drag"];
}
public void SetResetParameters()
{
SetGroundMaterialFriction();
SetBlockProperties();
}
}