Hi, I am a NOOB in RL world and I developed a simple game on which I wanted to test my skills. The game is available HERE (GitHub - tomitrescak/BaseAttackRL, Scene: BaseAttack). I have a base, which is attacked from five sides by jeeps. Your goals is to shoot all jeeps as they come:
The bullet has a “travel time” and “cadence”, so you can only shoot one bullet per second and you know if you hit something only after the bullet has reached its target. I ran the model, but results are very underwhelming. After some period of success, the soldier developed a behaviour where he shoots nothing in the corner:
Would you have time to look at that simple repo to see what am I missing?
FYI, this is my agent class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
public class SoldierAgent : Agent
{
private static Vector3 NullPosition = new Vector3(-1000, -1000, -1000);
public JeepSpawn[] spawns;
private Rigidbody2D rb;
private ShootCannon cannon;
private float angle = 0;
[SerializeField] private float rotationSpeed = 1;
private void Start()
{
this.rb = GetComponent<Rigidbody2D>();
this.cannon = GetComponent<ShootCannon>();
}
public override void CollectObservations(VectorSensor sensor)
{
// add all spawns
foreach (var spawn in spawns) {
sensor.AddObservation(spawn.Jeep ? spawn.Jeep.transform.position : NullPosition);
}
sensor.AddObservation(transform.rotation.eulerAngles);
}
public override void OnActionReceived(ActionBuffers actions)
{
// Debug.Log(actions.ContinuousActions[0]);
float currentAngle = -1 * actions.ContinuousActions[0] * rotationSpeed;
angle = Mathf.Clamp(currentAngle + angle, -90, 90);
rb.MoveRotation(angle);
// transform.localPosition += new Vector3(moveX, 0, moveZ) * Time.deltaTime * moveSpeed;
var shoot = actions.DiscreteActions[0];
if (shoot == 1 && this.cannon.CanShoot)
{
this.cannon.Shoot();
}
}
public override void WriteDiscreteActionMask(IDiscreteActionMask actionMask)
{
// prohibit shooting cannot during the period when it cannot be shot
actionMask.SetActionEnabled(0, 1, this.cannon.CanShoot);
}
public override void Heuristic(in ActionBuffers actionsOut)
{
//Debug.Log("Collecting Heuristics");
var continuoutActions = actionsOut.ContinuousActions;
continuoutActions[0] = Input.GetAxisRaw("Horizontal");
var discreteActions = actionsOut.DiscreteActions;
discreteActions[0] = Input.GetKey(KeyCode.Space) ? 1 : 0;
}
public override void OnEpisodeBegin()
{
// destroy jeeps
foreach (var b in spawns)
{
if (b.Jeep)
{
Destroy(b.Jeep);
}
b.enabled = false;
b.enabled = true;
}
this.angle = 0;
transform.rotation = Quaternion.identity;
}
}
And I set rewards in the collision of Jeep with other stuff. The higher reward comes to faster moving jeeps.
using UnityEngine;
public class CarMove : MonoBehaviour
{
[SerializeField] public float Speed = 1;
[SerializeField] private GameObject explosion;
public GameObject target;
Transform player;
Rigidbody2D rb;
public JeepSpawn spawn;
// Start is called before the first frame update
void Start()
{
player = target.transform;
rb = GetComponent<Rigidbody2D>();
transform.up = player.position - transform.position;
rb.velocity = transform.up * Time.deltaTime * Speed;
}
private void OnTriggerEnter2D(Collider2D collision)
{
// we shot the jeep
if (collision.gameObject.CompareTag("Bullet"))
{
// Debug.Log("Bullet trigger!");
var instance = Instantiate(explosion, transform.position, Quaternion.identity);
spawn.Player.AddReward(this.Speed);
spawn.enabled = true;
spawn.Jeep = null;
Destroy(instance, 1);
Destroy(collision.gameObject);
Destroy(gameObject, 0.5f);
}
// jeep crashed into base
else if (collision.gameObject.CompareTag("Player"))
{
spawn.Player.SetReward(-100);
spawn.Player.EndEpisode();
}
}
}

