ppo-Pyramids-Training
/
Project
/Assets
/ML-Agents
/Examples
/DungeonEscape
/Scripts
/DungeonEscapeEnvController.cs
using System.Collections; | |
using System.Collections.Generic; | |
using Unity.MLAgents; | |
using UnityEngine; | |
public class DungeonEscapeEnvController : MonoBehaviour | |
{ | |
[ | ]|
public class PlayerInfo | |
{ | |
public PushAgentEscape Agent; | |
[ | ]|
public Vector3 StartingPos; | |
[ | ]|
public Quaternion StartingRot; | |
[ | ]|
public Rigidbody Rb; | |
[ | ]|
public Collider Col; | |
} | |
[ | ]|
public class DragonInfo | |
{ | |
public SimpleNPC Agent; | |
[ | ]|
public Vector3 StartingPos; | |
[ | ]|
public Quaternion StartingRot; | |
[ | ]|
public Rigidbody Rb; | |
[ | ]|
public Collider Col; | |
public Transform T; | |
public bool IsDead; | |
} | |
/// <summary> | |
/// Max Academy steps before this platform resets | |
/// </summary> | |
/// <returns></returns> | |
[public int MaxEnvironmentSteps = 25000; | ]|
private int m_ResetTimer; | |
/// <summary> | |
/// The area bounds. | |
/// </summary> | |
[ | ]|
public Bounds areaBounds; | |
/// <summary> | |
/// The ground. The bounds are used to spawn the elements. | |
/// </summary> | |
public GameObject ground; | |
Material m_GroundMaterial; //cached on Awake() | |
/// <summary> | |
/// We will be changing the ground material based on success/failue | |
/// </summary> | |
Renderer m_GroundRenderer; | |
public List<PlayerInfo> AgentsList = new List<PlayerInfo>(); | |
public List<DragonInfo> DragonsList = new List<DragonInfo>(); | |
private Dictionary<PushAgentEscape, PlayerInfo> m_PlayerDict = new Dictionary<PushAgentEscape, PlayerInfo>(); | |
public bool UseRandomAgentRotation = true; | |
public bool UseRandomAgentPosition = true; | |
PushBlockSettings m_PushBlockSettings; | |
private int m_NumberOfRemainingPlayers; | |
public GameObject Key; | |
public GameObject Tombstone; | |
private SimpleMultiAgentGroup m_AgentGroup; | |
void Start() | |
{ | |
// Get the ground's bounds | |
areaBounds = ground.GetComponent<Collider>().bounds; | |
// Get the ground renderer so we can change the material when a goal is scored | |
m_GroundRenderer = ground.GetComponent<Renderer>(); | |
// Starting material | |
m_GroundMaterial = m_GroundRenderer.material; | |
m_PushBlockSettings = FindObjectOfType<PushBlockSettings>(); | |
//Reset Players Remaining | |
m_NumberOfRemainingPlayers = AgentsList.Count; | |
//Hide The Key | |
Key.SetActive(false); | |
// Initialize TeamManager | |
m_AgentGroup = new SimpleMultiAgentGroup(); | |
foreach (var item in AgentsList) | |
{ | |
item.StartingPos = item.Agent.transform.position; | |
item.StartingRot = item.Agent.transform.rotation; | |
item.Rb = item.Agent.GetComponent<Rigidbody>(); | |
item.Col = item.Agent.GetComponent<Collider>(); | |
// Add to team manager | |
m_AgentGroup.RegisterAgent(item.Agent); | |
} | |
foreach (var item in DragonsList) | |
{ | |
item.StartingPos = item.Agent.transform.position; | |
item.StartingRot = item.Agent.transform.rotation; | |
item.T = item.Agent.transform; | |
item.Col = item.Agent.GetComponent<Collider>(); | |
} | |
ResetScene(); | |
} | |
// Update is called once per frame | |
void FixedUpdate() | |
{ | |
m_ResetTimer += 1; | |
if (m_ResetTimer >= MaxEnvironmentSteps && MaxEnvironmentSteps > 0) | |
{ | |
m_AgentGroup.GroupEpisodeInterrupted(); | |
ResetScene(); | |
} | |
} | |
public void TouchedHazard(PushAgentEscape agent) | |
{ | |
m_NumberOfRemainingPlayers--; | |
if (m_NumberOfRemainingPlayers == 0 || agent.IHaveAKey) | |
{ | |
m_AgentGroup.EndGroupEpisode(); | |
ResetScene(); | |
} | |
else | |
{ | |
agent.gameObject.SetActive(false); | |
} | |
} | |
public void UnlockDoor() | |
{ | |
m_AgentGroup.AddGroupReward(1f); | |
StartCoroutine(GoalScoredSwapGroundMaterial(m_PushBlockSettings.goalScoredMaterial, 0.5f)); | |
print("Unlocked Door"); | |
m_AgentGroup.EndGroupEpisode(); | |
ResetScene(); | |
} | |
public void KilledByBaddie(PushAgentEscape agent, Collision baddieCol) | |
{ | |
baddieCol.gameObject.SetActive(false); | |
m_NumberOfRemainingPlayers--; | |
agent.gameObject.SetActive(false); | |
print($"{baddieCol.gameObject.name} ate {agent.transform.name}"); | |
//Spawn Tombstone | |
Tombstone.transform.SetPositionAndRotation(agent.transform.position, agent.transform.rotation); | |
Tombstone.SetActive(true); | |
//Spawn the Key Pickup | |
Key.transform.SetPositionAndRotation(baddieCol.collider.transform.position, baddieCol.collider.transform.rotation); | |
Key.SetActive(true); | |
} | |
/// <summary> | |
/// Use the ground's bounds to pick a random spawn position. | |
/// </summary> | |
public Vector3 GetRandomSpawnPos() | |
{ | |
var foundNewSpawnLocation = false; | |
var randomSpawnPos = Vector3.zero; | |
while (foundNewSpawnLocation == false) | |
{ | |
var randomPosX = Random.Range(-areaBounds.extents.x * m_PushBlockSettings.spawnAreaMarginMultiplier, | |
areaBounds.extents.x * m_PushBlockSettings.spawnAreaMarginMultiplier); | |
var randomPosZ = Random.Range(-areaBounds.extents.z * m_PushBlockSettings.spawnAreaMarginMultiplier, | |
areaBounds.extents.z * m_PushBlockSettings.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; | |
} | |
/// <summary> | |
/// Swap ground material, wait time seconds, then swap back to the regular material. | |
/// </summary> | |
IEnumerator GoalScoredSwapGroundMaterial(Material mat, float time) | |
{ | |
m_GroundRenderer.material = mat; | |
yield return new WaitForSeconds(time); // Wait for 2 sec | |
m_GroundRenderer.material = m_GroundMaterial; | |
} | |
public void BaddieTouchedBlock() | |
{ | |
m_AgentGroup.EndGroupEpisode(); | |
// Swap ground material for a bit to indicate we scored. | |
StartCoroutine(GoalScoredSwapGroundMaterial(m_PushBlockSettings.failMaterial, 0.5f)); | |
ResetScene(); | |
} | |
Quaternion GetRandomRot() | |
{ | |
return Quaternion.Euler(0, Random.Range(0.0f, 360.0f), 0); | |
} | |
void ResetScene() | |
{ | |
//Reset counter | |
m_ResetTimer = 0; | |
//Reset Players Remaining | |
m_NumberOfRemainingPlayers = AgentsList.Count; | |
//Random platform rot | |
var rotation = Random.Range(0, 4); | |
var rotationAngle = rotation * 90f; | |
transform.Rotate(new Vector3(0f, rotationAngle, 0f)); | |
//Reset Agents | |
foreach (var item in AgentsList) | |
{ | |
var pos = UseRandomAgentPosition ? GetRandomSpawnPos() : item.StartingPos; | |
var rot = UseRandomAgentRotation ? GetRandomRot() : item.StartingRot; | |
item.Agent.transform.SetPositionAndRotation(pos, rot); | |
item.Rb.velocity = Vector3.zero; | |
item.Rb.angularVelocity = Vector3.zero; | |
item.Agent.MyKey.SetActive(false); | |
item.Agent.IHaveAKey = false; | |
item.Agent.gameObject.SetActive(true); | |
m_AgentGroup.RegisterAgent(item.Agent); | |
} | |
//Reset Key | |
Key.SetActive(false); | |
//Reset Tombstone | |
Tombstone.SetActive(false); | |
//End Episode | |
foreach (var item in DragonsList) | |
{ | |
if (!item.Agent) | |
{ | |
return; | |
} | |
item.Agent.transform.SetPositionAndRotation(item.StartingPos, item.StartingRot); | |
item.Agent.SetRandomWalkSpeed(); | |
item.Agent.gameObject.SetActive(true); | |
} | |
} | |
} | |