4
\$\begingroup\$

Ok so, here is the problem.

I am doing a multiplayer 2D game using UNET.

I have a script that kind of helps a player select an avatar to play with. The avatars are listed as a scrollview of buttons. These buttons invoke the function setAvatar(int id) that sets a player's avatar passing in an ID as argument. The function just sets the player's avatar using ID as index in a list of avatars.

Now the problem is this player is a prefab which is instantiated at the start of the playscene for Player A and Player B. So the setAvatar(int sprid) function assumes I am referring to the prefab whenever I use "gameObject"

So, I decided to store the local gameObject in a temporary variable.

Here's the full code,

public class Player2DController : NetworkBehaviour // Attached to player prefab
{
    public float moveForce = 2;
    Rigidbody2D myBody;
    Transform mainCamera;
    Shooting shscript;
    int i;
    public Transform joystick;
    GameObject temp;

    public Sprite[] spr;

    void Start ()
    {
        if (!isLocalPlayer) 
        {
            Destroy (this);
            return;
        }

        temp = gameObject; // storing local gameobject in temp

        joystick = GameObject.Find ("MobileJoystick").transform;
        shscript = GetComponent<Shooting> ();
        mainCamera = Camera.main.transform;
        RotateCamera ();
        myBody = GetComponent<Rigidbody2D> ();

        if (isClient) 
        {
            i = -1;
        }
        if (isServer) 
        {
            i = 1;
        }

    }



    public void RotateCamera()
    {
        if (isClient)
        {
            mainCamera.rotation = Quaternion.Euler (new Vector3 (0f, 0f, 180f));
        }
        if (isServer) 
        {
            mainCamera.rotation = Quaternion.Euler (new Vector3 (0f, 0f, 0f));
        }
    }

    void FixedUpdate ()
    {
        if (PauseMenu.isOn) 
        {
            return;
        }
        Vector2 moveVec = new Vector2(CrossPlatformInputManager.GetAxis("Horizontal"),CrossPlatformInputManager.GetAxis("Vertical"))* moveForce;

        myBody.AddForce (moveVec * i);

        if (CrossPlatformInputManager.GetButton("Shoot"))
        {
            shscript.CmdFire ();
        }

    }


    public void setAvatar(int id) // called through the buttons in the avatar list
    {
        temp.GetComponent<SpriteRenderer> ().sprite = spr [id];//null pointer exception
    }
}

This code doesn't work. I get a null pointer exception on temp variable saying it is not assigned. But how can this be? I clearly assign it to active gameObject in Start() method. The only reason I can think of why this is happening is that since the button acts on the player prefab, it creates a separate instance of the script for the player prefab that doesn't get executed. So, when it invokes setAvatar(), it has a temp which is not initialized.

So that's the whole problem. Since this didn't work, I tried renaming the players as clientPlayer and serverPlayer and then accessing them using GameObject.Find(name).GetComponent<SpriteRenderer>().sprite. But this had no effect and no errors. I have no idea why.

So any ideas on how to go about this?

\$\endgroup\$
3
  • \$\begingroup\$ share your full code \$\endgroup\$
    – Umair M
    Oct 3, 2016 at 16:06
  • \$\begingroup\$ Hi, thanks for replying. I added the full code as requested. \$\endgroup\$
    – user90742
    Oct 4, 2016 at 4:59
  • \$\begingroup\$ It looks like you're setting temp on the spawned instance, but the buttons are calling setAvatar on the prefab — these two instances of the object each have their own copy of that variable, so setting one doesn't influence the other. You can change that by making the temp variable static (shared between instances), but it may be neater and more scalable to introduce something like a PlayerManager script the buttons can talk to instead. \$\endgroup\$
    – DMGregory
    Nov 5, 2016 at 13:54

1 Answer 1

0
\$\begingroup\$

It might be the case that the following part get executed and temp is never assigned to gameObject:

if (!isLocalPlayer) 
{
    Destroy (this);
    return;
}

So make sure that you check for null before utilising this reference:

if(temp != null)
    temp.GetComponent<SpriteRenderer> ().sprite = spr [id];
\$\endgroup\$
9
  • \$\begingroup\$ So, then what if temp is null? The code will have no effect. And anyways that's not the issue. I am pretty sure. You see when I do a "Debug.Log(gameObject.name)" it returns the gameObject's name in "Start()" but in the "setAvatar()"method, it returns returns the prefab's name. \$\endgroup\$
    – user90742
    Oct 4, 2016 at 10:53
  • \$\begingroup\$ Well temp.GetComponent<SpriteRenderer> ().sprite = spr [id]; this line can have several reasons to throw NRE. temp can be null or it may not have SpriteRenderer Attached, or sprite in array at given index might be null. Try to use debugger and watch every value. \$\endgroup\$
    – Umair M
    Oct 4, 2016 at 12:48
  • \$\begingroup\$ No, the error clearly says "temp is unassigned". But regardless i checked as you said. I debugged all the values in the method. I also added a Debug for temp gameObject at the start of the method. Now the error is in that line. So, it clearly means the exception is thrown on the "temp" gameObject. \$\endgroup\$
    – user90742
    Oct 4, 2016 at 13:05
  • \$\begingroup\$ OK. get rid of temp and just call `GetComponent<SpriteRenderer> ().sprite = spr [id]; \$\endgroup\$
    – Umair M
    Oct 4, 2016 at 16:04
  • \$\begingroup\$ I tell you again now. Script to change avatar is attached to the player prefab. The network manager spawns these as the hostPlayer and the clientPlayer as two separate instances of the player prefab. It means that each of these instances have a separate instance of the script attached to them. Added to these is the original instance i.e the prefab which has the original instance of the script as separate instance to act on it. \$\endgroup\$
    – user90742
    Oct 5, 2016 at 4:31

Not the answer you're looking for? Browse other questions tagged .