Hello Everyone and Happy Holidays,
Just now I removed a seemingly unnecessary game objected called “empty” from my environment and commented respected commands in the script out. The specific code lines simply set a tag “active” and “untag” to the empty game object. I figured that I do not need this empty game object anymore as I used it for testing. I removed and commented the respective lines out (line: 29,126,203,224) in the main scrip at the bottom. However, when running the simulation now I receive the following error:
NullReferenceException: Object reference not set to an instance of an object
ReacherAgent.CollectObservations (Unity.MLAgents.Sensors.VectorSensor sensor) (at Assets/ML-Agents/Examples/Reacher/Scripts/ReacherAgent.cs:66)
I then examine line 66 of the Reacher Agent.cs, which collect the position of each active target:
sensor.AddObservation(active.transform.parent.localPosition);
This however seems fine to me.
Then I thought, maybe the coroutine used to wait out a few frames could cause this error. I set the number of frames to wait for the next action to zero, but I still obtain this error. Any suggestion on whats going on here and how I could fix it ?
Main Script:
[code=CSharp]using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Random = UnityEngine.Random;
public class Game_Manager : MonoBehaviour
{
public GameObject agent;
public GameObject hand;
// empty array of size 4 to collect target objects
public GameObject[] targetObjects = new GameObject[4];
// 4x4 matrix to initialize and determine activation status of targets
Matrix4x4 sequence = new Matrix4x4();
int gameSequence = 0;
int failCounter = 0;
public bool random_sequence = false;
Color temp;
float rewardToGive = 1;
public GameObject parent;
// Inter Stimulus Interval
private IEnumerator coroutine;
//public GameObject empty;
void Start()
{
}
public void init()
{
coroutine = CoroutineAction();
// targetObjects = GameObject.FindGameObjectsWithTag("Target");
int c = 0;
foreach (Transform child in parent.transform)
{
if (child.tag == "Target")
{
targetObjects[c] = child.gameObject;
c++;
}
}
if (random_sequence == false)
{
// set empty game object "Random_Gen_Target" as parent of Agent
agent.transform.parent = gameObject.transform;
sequence = Matrix4x4.identity;
//sets the GoalOn Object of first sphere to Target Active
setFirstActive();
}
else if (random_sequence == true)
{
sequence = Yates.Shuffle(Matrix4x4.identity);
Debug.Log(sequence);
setFirstActive();
}
}
// Initialize First Target activation:
// Iterates over the first row and finds col i = 1
private void setFirstActive()
{
for (int i = 0; i < 4; i++)
{
if (sequence[0, i] == 1)
{
// if condition fulfilled, set target to active
targetObjects[i].transform.GetChild(0).gameObject.tag = "Active";
//targetObjects[i].transform.GetChild(1).name = "Active";
temp = targetObjects[i].GetComponent<Renderer>().material.color;
targetObjects[i].GetComponent<Renderer>().material.color = new Color(224, 224, 224);
}
}
}
// for each round check which target is active
int checkActive(int round)
{
for (int j = 0; j < 4; j++)
{
if (sequence[round, j] == 1)
{
return j;
}
}
return 0;
}
public void triggered(GameObject touchedSphere)
{
// if more than than 1 target object
if (targetObjects.Length > 1)
{
// Debug.Log("Touched = " + touchedSphere);
// Debug.Log("active Object = " + targetObjects[checkActive(gameSequence)]);
if (touchedSphere == targetObjects[checkActive(gameSequence)])
{
// and reward if correct target has been touched
//agent.GetComponent<ReacherAgent>().AddReward(1.0f);
agent.GetComponent<ReacherAgent>().AddReward(rewardToGive);
Debug.Log("rewarded by " + rewardToGive);
gameSequence++;
rewardToGive = 1.0f;
failCounter = 0;
// sets the tag of GoalOn objct of the touchedSphere to Untagged
touchedSphere.transform.GetChild(0).gameObject.tag = "Untagged";
// touchedSphere.transform.GetChild(1).name = "Unactive";
// set the color of the target back to its original
touchedSphere.GetComponent<Renderer>().material.color = temp;
// empty.tag = "Active";
StartCoroutine(CoroutineAction());
}
else
{
// if wrong sphere fail counter increments
failCounter++;
//TODO: fail increment per frame. needs to be reduced to 1 touch per collision
}
}
}
public GameObject getActive()
{
return GameObject.FindGameObjectsWithTag("Active")[0];
}
private void initializeNewRound()
{
sequence = Matrix4x4.identity;
sequence = Yates.Shuffle(sequence);
setFirstActive();
//Debug.Log(sequence);
}
private void initializeFixedRound()
{
sequence = Matrix4x4.identity;
setFirstActive();
//Debug.Log(sequence);
}
void Update()
{
// every frame find and collect Targets
// targetObjects = GameObject.FindGameObjectsWithTag("Target");
int c = 0;
foreach (Transform child in parent.transform)
{
if (child.tag == "Target")
{
targetObjects[c] = child.gameObject;
c++;
}
}
rewardToGive = rewardToGive * 0.99f;
rewardToGive = Math.Max(0.1f, rewardToGive);
float noise = Noise.gauss(0,0.01);
// empty.transform.localPosition = new Vector3(0 + noise, -4 + noise, 0 + noise);
}
// Counts frames for the intertrial interval
public static class WaitFor
{
public static IEnumerator Frames(int frameCount)
{
while (frameCount > 0)
{
frameCount--;
yield return null;
}
}
}
// Coroutine defines the scheduling or execution time of code during the intertrial interval
public IEnumerator CoroutineAction()
{
if (gameSequence > 3)
{
gameSequence = 0;
failCounter = 0;
yield return StartCoroutine(WaitFor.Frames(0)); // wait for 20 frames
//empty.tag = "Untagged";
//Debug.Log(gameSequence);
// Start a new round
// initializeNewRound();
if (random_sequence == false)
{
initializeFixedRound();
}
else if (random_sequence == true)
{
initializeNewRound();
}
}
// else if it is not the last ball
else
{
yield return StartCoroutine(WaitFor.Frames(0)); // wait for 20 frames
//empty.tag = "Untagged";
// Debug.Log(gameSequence);
int active = checkActive(gameSequence);
// sets the tag of the next balls GoalOn objct Active
targetObjects[active].transform.GetChild(0).gameObject.tag = "Active";
// targetObjects[active].transform.GetChild(1).name = "Active";
// save its color to temp
temp = targetObjects[checkActive(gameSequence)].GetComponent<Renderer>().material.color;
// let it light up
targetObjects[active].GetComponent<Renderer>().material.color = new Color(224, 224, 224);
}
}
}