Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Unity Code Help

Started by LeftyGuitar May 18, 2015 at 8:55 PM 20 replies 7.1k views
Original Post
LeftyGuitar
LeftyGuitar

Hello,

I am having trouble with my code and using Unity. I want to make it so that when you click the mouse button, a ball is fired and hits a target. I think I am getting pretty close. The only error coming up is Object reference not set to an instance of an object. I'm not sure what exactly is causing this. I'll post my code. It says its coming from line 37, which is rbBall.AddForce(trans.forward * Speed); I'm not sure what is causing this to be. Note that this game is 2D, but since the ball is shot from the camera, the ball is set to be shot from the Z axis and has 3D physics rigidbody attached to it.


using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class PlayerBall : MonoBehaviour {

	public Text Dunks; //text used to show dunks
	//public Transform ballPrefab; //ball position
	public float Speed = 1000f; //speed of ball
	GameObject BallPrefab;
	private int iDunks; //number of dunks
	public Transform trans;
	private int Balls = 4;
	private Texture2D ballimg;

	void Start () {

		//Dunks.text = "";
		iDunks = 0;
		BallPrefab = GetComponent<GameObject>();
		trans = GameObject.Find("Player").transform;
		BallPrefab = GameObject.Find("Player");
		ballimg = Resources.Load("Player/ball.png") as Texture2D;
	}
	
	// Update is called once per frame
	void Update () {

		float h = Input.GetAxis ("Horizontal") * Time.deltaTime * Speed;
		float v = Input.GetAxis ("Vertical") * Time.deltaTime * Speed;

		if (Input.GetMouseButton (0) || Input.GetButton ("Fire1") ) 
		{
			Rigidbody rbBall;
			BallPrefab.SetActive(true);
			rbBall = Instantiate(BallPrefab,trans.position,trans.rotation) as Rigidbody;
			rbBall.AddForce(trans.forward * Speed);	

			iDunks++;
			Balls--;

			Dunks.text = iDunks.ToString(); 
		}
	}

}

btower
btower

I'm not 100% sure if this is it (I haven't tested it), but if you're using Unity 5 then Instantiate will not return what you expect in your code. Instantiate will return an object of type Transform, from which you can use the GetComponent() method to get the actual Rigidbody you are looking for.

So instead of this:


Rigidbody rbBall;
rbBall = Instantiate(BallPrefab,trans.position,trans.rotation) as Rigidbody;
rbBall.AddForce(trans.forward * Speed);	

Use this:


Rigidbody rbBall;
var ballTransform = Instantiate(BallPrefab,trans.position,trans.rotation) as Transform;
rbBall = ballTransform.GetComponent<Rigidbody>();
rbBall.AddForce(trans.forward * Speed);	
ferrous
ferrous

I think that's pretty close, but I would expect Instantiate to return a GameObject and not a transform. Though it might work anyway. You could always test by attaching a debugger to btower's code and seeing what the actual type of the thing is that is returned by Instantiate. (It says Object in the API, but obviously your wanting to cast it to something more useful to you.)

btower
btower

I think that's pretty close, but I would expect Instantiate to return a GameObject and not a transform. Though it might work anyway. You could always test by attaching a debugger to btower's code and seeing what the actual type of the thing is that is returned by Instantiate. (It says Object in the API, but obviously your wanting to cast it to something more useful to you.)

I would say so too, but I saw a video of a Unity 5 primer this week where the programmer actually makes the same mistake, explains, fixes and tests it. See

LeftyGuitar
LeftyGuitar

I'm not 100% sure if this is it (I haven't tested it), but if you're using Unity 5 then Instantiate will not return what you expect in your code. Instantiate will return an object of type Transform, from which you can use the GetComponent() method to get the actual Rigidbody you are looking for.

So instead of this:


Rigidbody rbBall;
rbBall = Instantiate(BallPrefab,trans.position,trans.rotation) as Rigidbody;
rbBall.AddForce(trans.forward * Speed);	

Use this:


Rigidbody rbBall;
var ballTransform = Instantiate(BallPrefab,trans.position,trans.rotation) as Transform;
rbBall = ballTransform.GetComponent<Rigidbody>();
rbBall.AddForce(trans.forward * Speed);	

Thanks, I tried this and it still didn't work. The Ball is a 2D image, but I have 3D physics attached to it, since it needs be thrown as if its coming from the camera or Z axis.

btower
btower

In that case it seems like you forgot to actually fill in the BallPrefab variable using the Inspector window (You should assign a prefab to the variable). BallPrefab must be declared as a public field (not a property or a method) for it to actually appear in the Inspector window.

How to do this is also explained in the video I linked earlier.

LeftyGuitar
LeftyGuitar

Well I have made it a public gameobject and I have set it to the prefab, but I am still getting the same error. I've included a pic to show. I even added in BallPrefab.setActive(true), as its set to false by default, I need the ball to be hidden until the user clicks the mouse button and the ball is thrown.

ferrous
ferrous

Did you remove the two places in the code where you were setting the BallPrefab in Start()? Also, you really should walk through in the debugger, even the standard, but crappy but included Monodevelop IDE can help you figure out what is null and why.

BallPrefab = GetComponent<GameObject>();
BallPrefab = GameObject.Find("Player");

LeftyGuitar
LeftyGuitar

Did you remove the two places in the code where you were setting the BallPrefab in Start()? Also, you really should walk through in the debugger, even the standard, but crappy but included Monodevelop IDE can help you figure out what is null and why.

BallPrefab = GetComponent<GameObject>();
BallPrefab = GameObject.Find("Player");

I've removed those from start, but now I'm getting the variable BallPrefab of PlayerBall has not been assigned.

ferrous
ferrous

Hmm, I'd double check you still have it assigned from the editor, as per your screenshot, it looks like it's been set. But you may have set it while the editor was in "Play" mode, and any changes made at that time are undone once you back out.

LeftyGuitar
LeftyGuitar

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class PlayerBall : MonoBehaviour {

	public Text Dunks; //text used to show dunks
	//public Transform ballPrefab; //ball position
	public float Speed = 1000f; //speed of ball
	public GameObject BallPrefab;
	private int iDunks; //number of dunks
	public Transform trans;
	private int Balls = 4;
	private Texture2D ballimg;

	void Start () {

		//Dunks.text = "";
		iDunks = 0;
		//BallPrefab = GetComponent<GameObject>();
		trans = GameObject.Find("Player").transform;
		//BallPrefab = GameObject.Find("Player");
		ballimg = Resources.Load("Player/ball.png") as Texture2D;
	}
	
	// Update is called once per frame
	void Update () {

		float h = Input.GetAxis ("Horizontal") * Time.deltaTime * Speed;
		float v = Input.GetAxis ("Vertical") * Time.deltaTime * Speed;

		if (Input.GetMouseButton (0) || Input.GetButton ("Fire1") ) 
		{
			Rigidbody rbBall;
			BallPrefab.SetActive(true);
			var ballTransform = Instantiate(BallPrefab,trans.position,trans.rotation) as Transform;
			rbBall = ballTransform.GetComponent<Rigidbody>();
			rbBall.AddForce(trans.forward * Speed);		

			iDunks++;
			Balls--;

			Dunks.text = iDunks.ToString(); 
		
			if(Balls == 0)
			{
			}
		}
	}

}

Ok I got the ball to show up when you click, but as soon as it does, it comes up with object reference not set to an instance of an object. I have a gameobject player which is the parent of the gameobject ball. The ball prefab is the ball image I want to shoot. As soon as it comes into contact with the target is when the error appears.

ferrous
ferrous

So you're saying "object reference not set to an instance of an object. " comes up when your object hits an object? Sounds like it's probably code other than what you've posted that is causing the problem.

Or it could be that your "Dunks" text object is null.

I'll reiterate once more, you really ought to step through this with a debugger, it's much faster than waiting on responses from us =)

LeftyGuitar
LeftyGuitar

rbBall = ballTransform.GetComponent<Rigidbody>();

It says the error is coming from line 37, which is the code posted above. I am trying to go over and see what is causing it to say object reference not set to an instance of an object.

bartm4n
bartm4n

The scripting manual is quite clear about this; Instantiate is going to return an exact clone of the object that you pass to it. Therefore, if you pass it a GameOject, you will get a GameObject in return.

If you were to step through the code with a debugger, you would find that ballTransform is null as you currently have it coded.

From MSDN:

The as operator is like a cast operation. However, if the conversion isn't possible, as returns null instead of raising an exception.

Please make the following adjustments:


var ballTransform = Instantiate(BallPrefab,trans.position,trans.rotation);

rbBall = ballTransform.GetComponent<Rigidbody>();
rbBall.AddForce(trans.forward * Speed);	
LeftyGuitar
LeftyGuitar

The scripting manual is quite clear about this; Instantiate is going to return an exact clone of the object that you pass to it. Therefore, if you pass it a GameOject, you will get a GameObject in return.

If you were to step through the code with a debugger, you would find that ballTransform is null as you currently have it coded.

From MSDN:

The as operator is like a cast operation. However, if the conversion isn't possible, as returns null instead of raising an exception.

Please make the following adjustments:


var ballTransform = Instantiate(BallPrefab,trans.position,trans.rotation);

rbBall = ballTransform.GetComponent<Rigidbody>();
rbBall.AddForce(trans.forward * Speed);	

Thanks, but if I remove the as Transform, then the next line becomes an error, because BallTransform dosen't have the .getComponent method. I'll try running it through the debugger.

bartm4n
bartm4n

Then the compiler isn't able to accurately type the var, and you need to declare it explicitly as what it is: a GameObject.

Using var is just lazy in my opinion, at least in situations like this.

bartm4n
bartm4n

I mean just think about it...

BallPrefab is a GameObject that has a Rigidbody component attached to it.

You want to instantiate a copy of BallPrefab, and then access the Rigidbody component of that copy for modification.

A GameObject will never be able to cast to a Rigidbody; that is just not how the relationship between the objects works. That is also why they provide methods for accessing the components of a GameObject.

Let's pretend for a moment that you were able to successfully cast a GameObject to a Rigidbody, why would you need to then use GetComponent() to apply force instead of just applying it directly to your instantiated object?

Why in the world you would want to loosely type your variable when you know exactly what it is going to be is beyond me but that is a completely different subject.

LeftyGuitar
LeftyGuitar

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class PlayerBall : MonoBehaviour {

	public Text Dunks; //text used to show dunks
	//public Transform ballPrefab; //ball position
	public float Speed = 1000f; //speed of ball
	public GameObject BallPrefab;
	private int iDunks; //number of dunks
	public Transform trans;
	private int Balls = 4;
	private Texture2D ballimg;
	private Transform ballTransform;

	void Start () {

		//Dunks.text = "";
		iDunks = 0;
		//BallPrefab = GetComponent<GameObject>();
		trans = GameObject.Find("Player").transform;
		//BallPrefab = GameObject.Find("Player");
		ballimg = Resources.Load("Player/ball.png") as Texture2D;
	}
	
	// Update is called once per frame
	void Update () {

		float h = Input.GetAxis ("Horizontal") * Time.deltaTime * Speed;
		float v = Input.GetAxis ("Vertical") * Time.deltaTime * Speed;

		if (Input.GetMouseButtonDown (0) || Input.GetButtonDown ("Fire1") ) 
		{
			Rigidbody rbBall;
			BallPrefab.SetActive(true);
			rbBall = Instantiate(BallPrefab,trans.position,trans.rotation) as Rigidbody;
			rbBall = ballTransform.GetComponent<Rigidbody>();
			rbBall.AddForce(trans.forward * Speed);	

			Speed -= 100.0f;
			if(Speed == 0.0f)
			{
				BallPrefab.SetActive(false);
			}

			iDunks++;
			Balls--;
			
			Dunks.text = iDunks.ToString(); 
		
			if(Balls == 0)
			{
			}
		}
	}
}

Alright I've made a minor adjustment to the code. I just can't figure out what I'm doing wrong. The ball does show up when you click, but then the object reference not set to an instance of an object error comes up.

bartm4n
bartm4n

At this point, since the answer is given to you above, I suggest returning to the Unity tutorials as well as looking at some general C# tutorials.

Good luck......

RalemProductions
RalemProductions

	// Update is called once per frame
	void Update () {

		float h = Input.GetAxis ("Horizontal") * Time.deltaTime * Speed;
		float v = Input.GetAxis ("Vertical") * Time.deltaTime * Speed;

		if (Input.GetMouseButtonDown (0) || Input.GetButtonDown ("Fire1") ) 
		{
			GameObject newBall = (GameObject)Instantiate(BallPrefab,trans.position,trans.rotation); 
            		newBall.SetActive(true);
			Rigidbody rBall = newBall.AddComponent<Rigidbody>();  //add rigidbody to rBall in code
            		//rBall.mass = 10;                                    //rigidBody can be edited here  
			rbBall.AddForce(trans.forward * Speed);	              //add the force using rigidbody  

			Speed -= 100.0f;
			if(Speed == 0.0f)
			{
                		newBall.SetActive(false);
			}

			iDunks++;
			Balls--;
			
			Dunks.text = iDunks.ToString(); 
		
			if(Balls == 0)
			{
			}
		}
	}
}

Hi, this should work. Instead of messing with a prefab I like to just create the components in code and edit them there. That way things can't be messed up in your inspector. Just make sure you remove the rigidbody component from your BallPrefab.

If you want to have your rigidbody on your prefab that section of code should look like this.


        GameObject newBall = (GameObject)Instantiate(BallPrefab, trans.position, trans.rotation);
        newBall.SetActive(true);
        Rigidbody rbBall = newBall.GetComponent<Rigidbody>();
        rbBall.AddForce(trans.forward * Speed);	

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.