2D Rogue-like Object Reference

I’ve been expanding upon the 2D Rogue-Like Example given by Unity and I have recently come across an issue in creating a projectile.

            else if (Input.GetKeyDown(KeyCode.Space))
            {
                //A simple test used to check in the input is recognized
                print ("Space Key was pressed!");

                //Creating bolt gameObject that will travel to enemy (planning to change this soon)
                Instantiate(bolt, new Vector3 (boltSpawn.x + 1, boltSpawn.y + 1, 0f) , Quaternion.identity);

143:          Bolt.MoveBolt();

                //Ensuring that the turn ends for the player's chosen action
                GameManager.instance.playersTurn = false;
            }

I receive an error on line 143 that states "An object reference is required to access non-static member ‘Bolt.MoveBolt()’. I believe my issue my come with a lack of experience and knowledge in dealing with C#.

2172270–143751–Player.cs (11.8 KB)
2172270–143752–Bolt.cs (622 Bytes)

Instantiate returns the object instantiated. So you should keep a track of the returned value and use MoveBolt on the latter.

I don’t quite understand what you are getting at. Could you provide an example of what you are suggesting?

What you are doing in the code above is making the new bolt, not bothering to keep track of what it is (the value that Instantiate returns), and that is normally a fine thing to do, unless you then want to do something specific with the new bolt.

HOWEVER, your Bolt.MoveBolt() is an attempt to access a static function in the Bolt class, not an attempt to move the specific bolt that you just created.

This is more along the lines of what you want:

// create a copy of "bolt" and call it NewlyCreatedBolt
GameObject NewlyCreatedBolt = Instantiate(bolt, new Vector3 (boltSpawn.x + 1, boltSpawn.y + 1, 0f) , Quaternion.identity) as GameObject;

// Now tell it to .MoveBolt();
NewlyCreatedBolt.MoveBolt();
GameObject boltObject = Intantiate(bolt);
Bolt boltScriptAccess = boltObject.GetComponent<Bolt>();
boltScriptAccess.MoveBolt();

You cannot access functions from a script directly from the gameObject you need to get the script from the object first.

Excellent! Thank you all for clarifying. I rewrote some of the code and was able to produce a bolt on the screen but I have not been able to get it to move. The problem shows up right at boltScriptAccess.MoveBolt();

            public Bolt boltScriptAccess;

            else if (Input.GetKeyDown(KeyCode.Space))
            {
                //A simple test used to check in the input is recognized
                print ("Space Key was pressed!");

                //Creating bolt gameObject that will travel to enemy (planning to change this soon)
                GameObject boltInstance = Instantiate(bolt, new Vector3 (boltSpawn.x, boltSpawn.y, 0f) , Quaternion.identity) as GameObject;
                boltScriptAccess = boltInstance.GetComponent<Bolt>();

                print("Bolt Instantiated");

Ln: 147         boltScriptAccess.MoveBolt();
                print("Before SetActive(false)");
                boltInstance.gameObject.SetActive(false);

                //Ensuring that the turn ends for the player's chosen action
                GameManager.instance.playersTurn = false;
                print("After turn ends");
            }

In the console it states
“NullReferenceException: Object reference not set to an instance of an object
Player.Update () (at Assets/Scripts/Player.cs:147)”

So I scrolled over to my player prefab and noticed an empty spot but have not been able to successfully fill it. I’ve tried a few different prefabs that I made, but nothing is being accepted and I occasionally get the error “Unsupported type Vecotr3f” though I am using some 2D components and sprites.

Here is a screen shot:

By making the public Bolt boltScriptAccess; field, that requires that you drag a gameobject that HAS a Bolt script already attached to it. Until you do that, every attempt to access the Bolt public field, unless you set it to something elsewhere, will result in a null reference exception.

As a matter of preference, if you have a prefab that you have created and you want to instantiate THAT when you instantiate the bolt, you would generally expose an editor field, usually either a public GameObject or else a public UnityEngine.Object field and name it something like BoltPrefab, and that would be the first argument to your Instantiate() function.

Instantiate will happily make a copy of it and return you the game object. From there you can acquire the Bolt script already on the prefab (if there is one) by using the .GetComponent() method on the newly -returned instantiated gameobject.

This is where I am now: “Object reference not set to an instance of an object” but it happens when the code enters into the Move function within the MovingObject class. Again, a screen shot is attached.

I did add a gameObject (BoltSprite) that has the Bolt script added to it within the “Bolt” section. When I press the space bar it produces “(C# Script Image) BoltSprite(Clone) (Bolt)” within the “Bolt Script Access” section. All within the Player prefab.

This is a lot more frustrating than I originally thought it would be.

@Kurt-Dekker
I thought I was following what you were saying but it wasn’t working and kept stating the error I mentioned before and I couldn’t figure out why, until, I added base.Start() into void MoveBolt() in Bolt.cs. After that point, everything worked just fine.

Could you, or anybody who knows and is willing, explain why that changed everything?

Bolt.cs

    public void MoveBolt (int xDir, int yDir)
    {
        //Calling Start() from MovingObject.cs to add the required things to the projectile
        base.Start();
        //print("In void MoveBolt");
        AttemptMove <Enemy> (xDir, yDir);            //Attempting to move the projectile
    }

MovingObject.cs

    protected virtual void Start ()
    {
        boxCollider = GetComponent<BoxCollider2D> ();
        rb2D = GetComponent<Rigidbody2D> ();
        inverseMoveTime = 1f / moveTime;
    }

The base class needs to have Start() run to initialize the boxCollider and the rb2D. If the script uses either of those in MoveBolt() then it will give you a null reference exception.

Excellent! Thank you for clarifying that. Could you show me where the script uses either of those components, and what other components are needed to be initialized for a gameObject?

All the initializing is already done for you, you are only grabbing a reference so you can use it in your scripts which is usually done in Awake or Start. Take a look at the script and find where those components are, they shouldn’t be too hard to find.

I’m pretty sure I know where they are. Under MovingObject.cs is the Start() function.

    protected virtual void Start ()
    {
        boxCollider = GetComponent<BoxCollider2D> ();
        rb2D = GetComponent<Rigidbody2D> ();
        inverseMoveTime = 1f / moveTime;
    }

What exactly is this doing, and what else does this need to apply to?

The first two lines in Start are getting references to those components.
The third line is calculating the inverse of moveTime, this is to cache that value so it doesn’t need to be recalculated every time its needed, saving some cpu cycles(division can take up quite a few cpu cycles.)

Without a copy of MovingObject.cs I have no idea why a BoxCollider2D is being referenced for.
Rigidbody2D is used for movement, and every script that controls a physical object needs to reference it pretty much.

Is MovingObject.cs from a plugin or tutorial?

MovingObject.cs was from a tutorial (I typed it in while following the tutorial).

using UnityEngine;
using System.Collections;

public abstract class MovingObject : MonoBehaviour {

    public float moveTime = 0.1f;
    public LayerMask blockingLayer;

    private BoxCollider2D boxCollider;
    private Rigidbody2D rb2D;
    private float inverseMoveTime;

    // Use this for initialization
    protected virtual void Start ()
    {
        boxCollider = GetComponent<BoxCollider2D> ();
        rb2D = GetComponent<Rigidbody2D> ();
        inverseMoveTime = 1f / moveTime;
    }

    protected bool Move (int xDir, int yDir, out RaycastHit2D hit)
    {
        Vector2 start = transform.position;
        Vector2 end = start + new Vector2 (xDir, yDir);
        //print("boxCollider.enabled=false");
        boxCollider.enabled = false;
        //print("Physics2D");
        hit = Physics2D.Linecast (start, end, blockingLayer);
        //print("boxCollider.enabled=true");
        boxCollider.enabled = true;
        //print("bool Move, before SmoothMovement");
        if (hit.transform == null)
        {
            print(hit.transform);
            StartCoroutine(SmoothMovement (end));
            return true;
        }

        return false;
    }

    protected IEnumerator SmoothMovement (Vector3 end)
    {
        //print("In SmoothMovement");
        float sqrRemainingDistance = (transform.position - end).sqrMagnitude;

        while (sqrRemainingDistance > float.Epsilon)
        {
            Vector3 newPosition = Vector3.MoveTowards (rb2D.position, end, inverseMoveTime * Time.deltaTime);
            rb2D.MovePosition(newPosition);
            sqrRemainingDistance = (transform.position - end).sqrMagnitude;
            yield return null;
        }
    }

    protected virtual void AttemptMove <T> (int xDir, int yDir)
        where T : Component
    {
        //print("void AttemptMove");
        RaycastHit2D hit;
        bool canMove = Move(xDir, yDir, out hit);

        if (hit.transform == null)
            return;

        T hitComponent = hit.transform.GetComponent<T>();

        if (!canMove && hitComponent != null)
            OnCantMove(hitComponent);
    }

    protected abstract void OnCantMove <T> (T component)
        where T : Component;
}

I see. Seems a little over kill if you ask me. Definitely not a beginners tutorial.

I still don’t understand why they need a reference to BoxCollider2D, its disabling it performing a raycast then enabling it all in one frame. This isn’t needed if the object is assigned to a different layer then blockingLayer.

How much do you know about object oriented programming?

I was focusing a lot in programming when I was younger, using C/C++. I have some experience in UNIX, Linux, and Python as well. Not much? Maybe more than others?

What I suggest is to give yourself a small project to work on, something very simple. Not modifying other code, but a start from scratch project. Like moving a box on the screen with the arrow keys and work from there. That way you can see exactly how everything is working and why/when things are needed. I find that modifying others code to be problematic if you don’t understand how it works. Once you understand how it works then it should be a breeze to modify it but you still need to take the time to understand it. I started learning Unity that way, the Learn section can help you with how each part works or there are small project tutorials that you can follow. Other than that I can’t suggest anything else.

I usually learn pretty well by playing with code so I can watch it in action, but I will take your advice and start on a blank slate for now while going through the Learn section. I appreciate your help and advice and I hope to see you around some time. If there is anything I can help you with at some time, contact me. I’m enthusiastic and a fast learner. :wink:

I took some time and have been working on a very simple “shoot the target” project for a while, and it has helped to clarify some things for me. Thank you again for the suggestion. I am replying to this message to reach out and ask if you are willing to accept a mentee and/or work with somebody that is still considered a beginner. I want to work with another and to learn.