- Home /
Nearest object code not falling back on alternate in OnGUI()
Hi guys,
I have a compass that points to the nearest checkpoint in my game. If all the checkpoints have been passed (and therefore removed), it's supposed to point to the finish loop. This is the relevant code:
function GetClosestObject(tag:String, altTag:String) : GameObject { var objectsWithTag = GameObject.FindGameObjectsWithTag(tag);
if (!objectsWithTag)
GameObject.FindGameObjectsWithTag(altTag);
var closestObject : GameObject;
for (var obj : GameObject in objectsWithTag)
{
if(!closestObject)
{
closestObject = obj;
}
//compares distances
if(Vector3.Distance(transform.position, obj.transform.position) <= Vector3.Distance(transform.position, closestObject.transform.position))
{
closestObject = obj;
}
}
return closestObject;
}
function OnGUI() {
target = GetClosestObject("Checkpoint", "Finish").transform;
...
}
It works fine as long as there is at least one checkpoint. However, when there is only the finish loop remaining (which has the tag "Finish"), the arrow dial disappears and prints the following console error:
NullReferenceException: Object reference not set to an instance of an object
What seems to be the problem? All the game objects have their tags set right in the scene.
MachCUBED DirectionArrow.OnGUI () (at Assets/Entities/Player/Character/Scripts/DirectionArrow.js:44)
I'm not familiar at all with the JavaScript approach, but can you actually do if (!objectsWithTag) ? If it's null, shouldn't it be if (objectsWithTag == null) ? Other than that, I can only suggest chainedlupine's response and to print stuff everywhere to find out what your array consists of.
Answer by IssamKhalil · May 14, 2012 at 03:53 AM
Try doing this:
function GetClosestObject(tag:String, altTag:String) : GameObject
{
var objectsWithTag;
Debug.Log("Finish loop:" + GameObject.FindGameObjectsWithTag(altTag));
objectsWithTag = GameObject.FindGameObjectsWithTag(tag);
if (objectsWithTag.Length == 0)
{
Debug.Log("Finding finish");
objectsWithTag = GameObject.FindGameObjectsWithTag(altTag);
}
var closestObject : GameObject;
for (var obj : GameObject in objectsWithTag)
{
if(!closestObject)
{
closestObject = obj;
}
//compares distances
if(Vector3.Distance(transform.position, obj.transform.position) <= Vector3.Distance(transform.position, closestObject.transform.position))
{
closestObject = obj;
}
}
return closestObject;
}
It got the following compile-time error:
Assets/Entities/Player/Character/Scripts/DirectionArrow.js(25,24): BCE0019: 'Length' is not a member of 'Object'.
At the following line:
if (objectsWithTag.Length == 0)
So I typed objectsWithTag like so:
var objectsWithTag : GameObject[];
It worked as expected, but with one caveat: the compass sometimes points in the wrong direction.
Final code:
#pragma strict
var cam : Camera; //Camera to use
var arrow: Texture2D; // drag the arrow texture here
var dialPos: Vector2;
private var target : Transform; //Target to point at (you could set this to any gameObject dynamically) private var targetPos : Vector3; //Target position on screen private var screen$$anonymous$$iddle : Vector3; //$$anonymous$$iddle of the screen
function Start () { }
function GetClosestObject(tag:String, altTag:String) : GameObject { var objectsWithTag : GameObject[];
Debug.Log("Finish loop:" + GameObject.FindGameObjectsWithTag(altTag));
objectsWithTag = GameObject.FindGameObjectsWithTag(tag);
if (objectsWithTag.Length == 0)
{
objectsWithTag = GameObject.FindGameObjectsWithTag(altTag);
}
// Check again if the length is 0
if (objectsWithTag.Length == 0)
{
return null;
}
var closestObject : GameObject;
for (var obj : GameObject in objectsWithTag)
{
if(!closestObject)
{
closestObject = obj;
}
//compares distances
if(Vector3.Distance(transform.position, obj.transform.position) <= Vector3.Distance(transform.position, closestObject.transform.position))
{
closestObject = obj;
}
}
return closestObject;
}
function OnGUI() {
target = GetClosestObject("Checkpoint", "Finish").transform;
if (target != null) { //Get the targets position on screen into a Vector3 targetPos = cam.WorldToScreenPoint (target.transform.position);
//Get the middle of the screen into a Vector3
screen$$anonymous$$iddle = Vector3(Screen.width/2, Screen.height/2, 0);
//Compute the angle from screen$$anonymous$$iddle to targetPos
var tarAngle = ($$anonymous$$athf.Atan2(targetPos.x-screen$$anonymous$$iddle.x,Screen.height-targetPos.y-screen$$anonymous$$iddle.y) * $$anonymous$$athf.Rad2Deg)+90;
if (tarAngle < 0) tarAngle +=360;
//Calculate the angle from the camera to the target
var targetDir = target.transform.position - cam.transform.position;
var forward = cam.transform.forward;
var angle = Vector3.Angle(targetDir, forward);
//If the angle exceeds 90deg inverse the rotation to point correctly
if(angle<90)
{
transform.localRotation = Quaternion.Euler(-tarAngle,90,270);
}
else
{
transform.localRotation = Quaternion.Euler(tarAngle,270,90);
}
// Draw compass
var centre = Vector2(dialPos.x + arrow.width / 2, dialPos.y + arrow.height / 2);
var saved$$anonymous$$atrix = GUI.matrix;
if (tarAngle != 0)
GUIUtility.RotateAroundPivot(angle, centre);
GUI.DrawTexture(Rect(centre.x - (arrow.width / 2), centre.y - (arrow.height / 2), arrow.width, arrow.height), arrow);
GUI.matrix = saved$$anonymous$$atrix;
}
}
Thanks $$anonymous$$halil!
Answer by chainedlupine · May 01, 2012 at 04:05 AM
Shouldn't that be...?
if (!objectsWithTag)
objectsWithTag = GameObject.FindGameObjectsWithTag(altTag);
chainedlupines answer should definitely work. $$anonymous$$aybe you failed to save before running? Also, no need to go through the whole function if we know there are no checkpoints left:
if (!objectsWithTag) {
var finish : GameObject = GameObject.FindWithTag("Finish");
return finish;
}
(that is, unless there are multiple "Finish" objects, if so, my mistake)
I did save it and it still didn't work. I then tried your code:
function GetClosestObject(tag:String, altTag:String) : GameObject { var objectsWithTag = GameObject.FindGameObjectsWithTag(tag);
if (!objectsWithTag)
{
var finish : GameObject = GameObject.FindWithTag("Finish");
return finish;
}
var closestObject : GameObject;
for (var obj : GameObject in objectsWithTag)
{
if(!closestObject)
{
closestObject = obj;
}
//compares distances
if(Vector3.Distance(transform.position, obj.transform.position) <= Vector3.Distance(transform.position, closestObject.transform.position))
{
closestObject = obj;
}
}
return closestObject;
}
And I still got this error:
NullReferenceException: Object reference not set to an instance of an object
DirectionArrow.OnGUI () (at Assets/Entities/Player/Character/Scripts/DirectionArrow.js:48)
did you try it as suggested above : if (objectsWithTag == null)? don't think that's it though... how about this:
add this to your script:
var finish : GameObject;
function Start() {
finish = GameObject.FindWithTag("Finish")
}
then hit play and see if the finish slot in the inspector gets set. otherwise, maybe the tag is spelled differently or not set to the object.. no other way this wouldn't work..
So how about starting to debug your script? What code is at line 48 in "DirectionArrow.js" ?
It's clearly that a reference you're using is null. Probably because FindWithTag returned null because it can't find an object with this tag. Are you sure your tag is correct and there is an active object tagged with this tag?
We don't have your code, your have to fight your logic errors or tagging mistakes yourself. @chainedlupines answer is correct from what we can see here.
Answer by Piflik · May 13, 2012 at 06:11 PM
Try this:
function GetClosestObject(tag:String, altTag:String) : GameObject
{
var objectsWithTag = null;
objectsWithTag = GameObject.FindGameObjectsWithTag(tag);
if (objectsWithTag == null)
{
Debug.Log("Finding finish");
objectsWithTag = GameObject.FindGameObjectsWithTag(altTag);
}
var closestObject : GameObject;
for (var obj : GameObject in objectsWithTag)
{
if(!closestObject)
{
closestObject = obj;
}
//compares distances
if(Vector3.Distance(transform.position, obj.transform.position) <= Vector3.Distance(transform.position, closestObject.transform.position))
{
closestObject = obj;
}
}
return closestObject;
}
It probably doesn't work, since FindGameObjectsWithTag would return 'null' anyway, but it's worth a try....
I tried it, and it still doesn't work. From my debugging, my problem appears to be that this block:
if (objectsWithTag == null)
{
Debug.Log("Finding finish");
objectsWithTag = GameObject.FindGameObjectsWithTag(altTag);
}
Isn't being executed. It appears that if objectsWithTag is set to a null value, the if statement isn't firing.
I have if statements with anything == null and they work...try printing objectsWithTag to the console before the loop.
If I make my code look like this:
function GetClosestObject(tag:String, altTag:String) : GameObject { var objectsWithTag;
Debug.Log("Finish loop:" + GameObject.FindGameObjectsWithTag(altTag));
objectsWithTag = GameObject.FindGameObjectsWithTag(tag);
if (objectsWithTag == null)
{
Debug.Log("Finding finish");
objectsWithTag = GameObject.FindGameObjectsWithTag(altTag);
}
var closestObject : GameObject;
for (var obj : GameObject in objectsWithTag)
{
if(!closestObject)
{
closestObject = obj;
}
//compares distances
if(Vector3.Distance(transform.position, obj.transform.position) <= Vector3.Distance(transform.position, closestObject.transform.position))
{
closestObject = obj;
}
}
return closestObject;
}
I get this result for my Finish loop debug message:
Finish loop:UnityEngine.GameObject[] UnityEngine.Debug:Log(Object) DirectionArrow:GetClosestObject(String, String) (at Assets/Entities/Player/Character/Scripts/DirectionArrow.js:21) DirectionArrow:OnGUI() (at Assets/Entities/Player/Character/Scripts/DirectionArrow.js:51)
So it's printing an array for the name of the finish loop. $$anonymous$$ore importantly, "Finding finish" does not show up when all the checkpoints are passed, indicating that the reason why it's not finding the finish loop is because the following code isn't being executed:
if (objectsWithTag == null)
{
Debug.Log("Finding finish");
objectsWithTag = GameObject.FindGameObjectsWithTag(altTag);
}
I'm still stumped. Why oisn't objectsWithTag being set to null when OnGUI() is called after all objects tagged "Checkpoint" are removed from the scene?
Your answer
Follow this Question
Related Questions
Setting Scroll View Width GUILayout 1 Answer
Help with sanity script loading death screen 1 Answer
How do I handle object selection and GUI response ingame? 5 Answers
Problems using NGUI for in-game options sliders 0 Answers
Unity3D Game Time 1 Answer