2D Respawn Problem?

Hello guys,

I have been making a 2D platformer using the Brackeys 2D tutorial on YouTube, which I am starting to make changes to, because, some of the things he has done didn’t sit well with me. So, I’ve gone off the rails a bit from his tutorial and have ran into some problems because of it - and I don’t have much free time to work on it, so, it’s starting to drag on a bit - anyway it’s hard for me to tell what the errors are and how to fix them.

The problem this post entails is the respawning of the player when this error occurs:

“UnassignedReferenceException: The variable bulletThingy of Weapons_V has not been assigned.
You probably need to assign the bulletThingy variable of the Weapons_V script in the inspector.”

Now, the character can jump around and shot just fine before the respawn, so, I assume everything is assigned correctly beforehand, so, I really don’t understand how to resolve the error.

So if anyone can help resolving this problem it would be most appreciated.

Thankyou and Regards,
CK.

show your script, baby :smile: I mean, its hard to see, what is wrong without it

OK, fair enough :slight_smile:

I suppose I will have to show two scripts one dealing with the actual player class - named Player_V - with a class hard-coded inside it, that is one of the things that really doesn’t sit well with me. And a class called GameMaster_V that controls the actual killing of the character and respawning - it’s another thing that I would like to change.

Both these scripts have copious amounts of comments to try and help with my understanding and following of the code that Brackeys is presenting.

I hope that you may be able to help correct the error.

The scripts are as follows, first the Player_V:

using UnityEngine;
using System.Collections;

public class Player_V : MonoBehaviour {

    //to be able to access public variables in the "class" PlayerStats and see them
    //in the inspector - probably needs it for other reasons as well (???)
    //we need to use: [System.Serializable] before the PlayerStats class
    [System.Serializable]

    //a class within a class
    //I really don't understand why we would want to do this
    //I could understand it if we were using Inheritance
    //and creating an instance of another class - I will do it this way for the sake
    //of being congruous with the tutorial.This way I won't run into too many headaches
    //when things don't work properly - which always happens
    //This way it will be somewhat easier - and won't end up hitting my head against a brickwall

    //a class within a class with just a single variable ?!? - a bit stange
    //perhaps it will have more vars & functions in the future
    public class PlayerStats{

        public int playerHealth = 100;

    }//end class PlayerStats


    //make a declaration to the new class with an instance so we can
    //acess the variable and make it "PUBLIC" so it can be "SEEN" in the Inspector
    public PlayerStats playerStats = new PlayerStats();

    public int fallBoundary = -20;


    public void Update(){
        if (this.transform.position.y <= fallBoundary) {
            print ("We have a BIG problem - you have fallen down!");
            PlayerDamage(1000);
            }
   
    }//end Update()

    public void PlayerDamage(int damageAmount){
        playerStats.playerHealth -= damageAmount;
        //I suppose we check for playerHealth being equal or less than zero now?
        if (playerStats.playerHealth <= 0) {
            print ("Sorry ... but, you are finished here!");
            //pass the player gameObject to KillPlayer
            GameMaster_V.KillPlayer(this);

            }//end if

    }//end PlayerDamage

}//end class Player_V*/

Now the Game_Master_V:

using UnityEngine;
using System.Collections;

public class GameMaster_V : MonoBehaviour {


    //now we are doing something preety strange creating a class that will persist
    //throughout the whole game that will act somewhat like a place for
    //global variable that can be accessed from all functions apparently - using static
    //1st create the instance that will be used
    public static GameMaster_V gm;

    //vars
    public static bool isPlayerActive = true;

    //create 2 transform vars for respawning; ome for time; and one for dying sound; one prefab with particle
    public Transform PlayerPrefab;
    public Transform RespawnPoint;
    public float spawnDelay = 2.8f;
    public Transform particlePlay;

    //public audioclips
    public AudioClip respawnSound;
    public AudioClip playerDie;
    AudioSource audio;



    public static void GetResult(){
        print("Get result 1");
        }

    //2nd create the instance of the class
    public void Start(){
        //check if null and at StartUP it should be :P
        if (gm == null) {
            print ("It's null - wtf");
            //anyway if it does create one using the example in GetComponentExample.txt
            //so we can call "gm" as gameObject we "must" make it a prefab!?!
            //gm = gameObject.GetComponent.<GameMaster_V>(); //didn't work
            //gm = gameObject.GetComponent<GameMaster_V>(); //did work!?! hmmmm
            //Brackeys code
            gm = GameObject.FindGameObjectWithTag ("GM").GetComponent<GameMaster_V>();

            audio = GetComponent<AudioSource>();


            print ("CREATE:  isPlayerActive: "+isPlayerActive);
            }
    }//end start
  

    //We are going to set up a "Kill Player" function in this GameMaster class -
    //instead of in the Player script class - where I would put is ii knew how to do it
    //However we are doing it this way!!!
    public static void KillPlayer(Player_V player){
        //TODO - player death sound here
        //gm.audio.PlayOneShot(playerDie, 1.0f);
        //this works
        gm.GetComponent<AudioSource> ().Play ();
  
  
        Destroy (player.gameObject);
        isPlayerActive = false;
        print ("KILL Player:  isPlayerActive: " + isPlayerActive);
        gm.StartCoroutine (gm.RespawnPlayer ());

    }//End KillPlayer function


    //All the above text about Kill Player should be repeated here
    //but am saving my sanity :)
    //Co-routine
    public IEnumerator RespawnPlayer(){
        //TODO respawn sound
        //make unity WaitForSeconds for 2.5 seconds
        yield return new WaitForSeconds (spawnDelay);

        //TODO respawn sound
        //audio = GetComponent<AudioSource>();
        audio.PlayOneShot(respawnSound, 1.0f);

        //Instantiate a playerPrefab after it has been destroyed
        Instantiate (PlayerPrefab, RespawnPoint.position, RespawnPoint.rotation);

        //Instantiate Particle System for respawning player
        //normally we would create the particle system and play like this
        //GetComponent.<ParticleSystem>().Play();
        //so we are using a prefab instantiating and then destroying it!
        //using the 'clone' system
        Transform clone = Instantiate(particlePlay, RespawnPoint.position , RespawnPoint.rotation)as Transform;

        //now delete the clone after playing to stop it staying in the Heirachy
        //and eating up resources.
        //However cannot use:
        //Destroy (clone, 3f);
        //as this will try and destroy a transform which is not allowed in Unity,
        //but, we can use
        Destroy (clone.gameObject, 3f);

        //tried telling CameraFollowScript that player object is "active"
        //and it didn't work :(
        print("Respawn!!!");
        isPlayerActive = true;
        print ("RESPAWN:  isPlayerActive: " + isPlayerActive);

    }//end RespawnPlayer


}//end class GameMaster_V*/

i think, problem is somewhere in script Weapons_V

can be, that player prefab has reference to one gameobject in the scene (and not to prefab). And if you instantiate the player prefab, then one gameobject of this prefab is missed. So check the prefab too (drag&drop it in scene, and check in editor for missed variables).

or, something is wrong, befor you respawn the player. Maybe one gameobject is rferenced to player (it could be main camera, or enemy), and if you destroy the player, then you got the error. Try to comment Instantiate lines (i mean, disable it 66. //gm.StartCoroutine (gm.RespawnPlayer ())

Sorry about that should’ve realised you would want the Weapons script - I’ll post that below. It’s pretty long though with all the comments. Not too much actual code though :).

I did comment out those lines above line 66 in GameMaster - it didn’t help, but, I am surprised they didn’t throw an error! I have no idea what I was thinking when I coded that :sweat_smile:.

I’m pretty sure that I don’t have any prefabs in the scene, so, I don’t think that would be the cause of the error, as I said the script runs fine until the player respawns. The bullet does access a script at 181: to make it move. Would that cause the error?

Weapons Script:

using UnityEngine;
using System.Collections;

//Basically a script/class to deal with:
//1. Raycasting - well not anymore thank goodness
//2. to detect a hit when the "fire1" button is hit or the left mouse button :)
//3. set bullet movement and call appropriate routine

public class Weapons_V : MonoBehaviour {

    //fireRate var to determine is the object/weapon is single burst or continiuos fire
    //using Unity Docs version this number should be quite small
    //works on seconds (also fraction of seconds)
    public float fireRate = 0;    //default value for single burst weapons

    //var for the damage weapon will do - deal with enemy health
    //TODO: make hitDamage to enemy actually work
    public float hitDamage = 10;        //default value - can be changed
    public LayerMask whatLayerToHit;

    private float timeToFire;            //a var to allow for bursts a a timed pace
    private Transform firePoint;        //var to the "FirePoint" game object

    //spawning FX section
    private float timeToSpawnFX = 0;    //lets see if this works
    private float FXspawnRate    = .1f;    //same here again use float get rid of brackeys division in the code


    //Var to store our BulletTrail Prefab
    public Transform BulletTrailPrefab;
    public Transform MuzzleFlashPrefab;

    //turn BulletTrailPrefab into a rigidbody 2d object
    public Rigidbody2D projectile;
  
  
    //var to help control shooting rate - burst or single shot
    private float nextFire = 0;

  
    private Rigidbody2D clone1;    //component gotten in Awake() - will this work??


    //trying something new making the Vector2 vars
    //persist in memory by making them public/private
    //private 'should' work in this sense - should :)
    private Vector2 mousePos;
    private Vector2 firePointPosition;
    private Vector2 bulletDirection;

    public GameObject bulletThingy;
    GameObject go;

    //setup to get the laser_shot sound
    AudioSource audio;
    public AudioClip laserShot;

    // Use this for initialization
    void Awake () {

        Rigidbody2D clone1 = GetComponent<Rigidbody2D> ();
        GameObject go = GetComponent<GameObject> ();

        firePoint = transform.FindChild ("FirePoint");

        //null checking routine
        if(firePoint==null){
            Debug.LogError("FirePoint fraked up!!!");
            }

        //get component for audio
        audio = GetComponent<AudioSource>();

    }//end Awake()



  
    // Update is called once per frame
    void Update () {
        //deal with weapons - single burst or multiple
        //outer if
        if (fireRate == 0) {
            //Shoot ();
            //inner if
            if(Input.GetButtonDown("Fire1")){
                Shoot();
                } //end inner if
            }//end outer if for the else command
        else{
            //inner if in the else function also using &&
            if (Input.GetButton("Fire1") && Time.time > nextFire) {

                //this below is unity docs example
                nextFire = Time.time + fireRate;
                //this is Brakeys code
                //timeToFire = Time.time + 1 / fireRate;
                Shoot ();
                //print ("Shit");
            }
        }

    }//end Update()



    void Shoot(){
        print("We are Shooting!");
        //store our mouse's X and Y co-ords in a Vector2 variable
        //for the raycasting
        //nb we are using World co-ords because the screen co-oords are very different and
        //  the makers of Unity decided to invert them for some strange reason
        //Vector2 mousePosition = new Vector2(Camera.main.ScreenToWorldPoint (Input.mousePosition).x,
         //                                   Camera.main.ScreenToWorldPoint (Input.mousePosition).y
          //                                  );
        //maybe a much better way to get the mouse co-ords
        //much, much better and easier to boot :)  Yes it works!!!
        /*Vector2*/ mousePos = Camera.main.ScreenToWorldPoint (Input.mousePosition);


        //set the position to a new var of the X and Y co-ords of firePoint transform
        //thus eliminating the Z co-ord which we don't need
        //that is pretty cool
        Vector2 firePointPosition = new Vector2 (firePoint.position.x, firePoint.position.y);

        //now for the actual RayCast
        //RaycastHit2D hit = Physics2D.Raycast (firePointPosition, (mousePosition - firePointPosition),100, whatLayerToHit);
        //newer version
        RaycastHit2D hit = Physics2D.Raycast (firePointPosition, (mousePos - firePointPosition),100, whatLayerToHit);
      
        if (Time.time >= timeToSpawnFX) {
            ShotFX ();
            timeToSpawnFX =Time.time + FXspawnRate;
            }
        //ShotFX ();
        //put in function to spawn the BulletTrailPrefab
        //right here :P

        //show the Ray for testing purposes - Brackeys is using DrawLine for this - nil thios agam
        //make ray go ToString edges of screen by using * 100
        ////Debug.DrawLine (firePointPosition, (mousePosition - firePointPosition)*100, Color.cyan);
        //Debug.DrawRay (firePointPosition, (mousePosition - firePointPosition), Color.green);
        //newer version
        Debug.DrawRay (firePointPosition, (mousePos - firePointPosition), Color.green);


        //hit stuff this stuff is done away with
        if(hit.collider != null){
            Debug.DrawLine (firePointPosition, hit.point, Color.red);
            //when ray hits on object on selected layermask make line red
            //like the drawline code
            //Debug.DrawRay (firePointPosition, (mousePosition - firePointPosition), Color.red);
            //Debug.Log("We hit the: "+hit.collider.name+ "and did: "+hitDamage+ " damage!");

            }//end outer if
             
        }//end Shoot()



    //A 'void' function to create/instantiate the BulletTrailPrefab
    //and MuzzleFlashPrefab and make them visible
    //make
    void ShotFX(){

        //play the sound???
        audio.PlayOneShot(laserShot, 1.0f);

        //get bullet direction
        // from PGJ
        bulletDirection = (mousePos - firePointPosition).normalized;

        //GameObject go = Instantiate (bulletThingy, firePointPosition, Quaternion.identity) as GameObject;
        /*GameObject*/
        go = Instantiate (bulletThingy, firePoint.position, firePoint.rotation) as GameObject;


        //using Method Three as provided by: pgj
        //stored in document: Bullet Movement.odt
        //Directory:  This PC -> Documents
        go.GetComponent<MoveTrail_V> ().SetDirection (bulletDirection);



        Transform clone2;
        //clone2 = Instantiate (BulletTrailPrefab, firePoint.position, firePoint.rotation) as Transform;


        Transform clon3 = Instantiate (MuzzleFlashPrefab, firePoint.position, firePoint.rotation) as Transform;
        clon3.parent = firePoint;

        //set up random size for the 'muzzle flash'
        float size = Random.Range (.6f, 1f);

        //don't need to worry about Z rotation
        clon3.localScale = new Vector3(size, size, size);
        Destroy (clon3.gameObject, .04f);

        }//end ShotFX



}//end class Weapons_V

I actually found the error I have no idea how to resolve it!!!

I normally run the project (“game”) using “Maximize on Play”, so, I decided to run it in small mode just to see what is going on in Inspector because of what the error said and the Player object disappears from the Heirarchy - I would believe that is from the Destroy function that you right correctly found in line 66. And in it’s place a:

PlayerPrefab (Clone)

is created and the needed variable is indeed blank as are all the other variables (not surprisingly). I should’ve realized that would be the error but I suppose I was living in a cocoon.

The question I suppose is how do I proceed, or, correct that from occurring?

EDIT - I just found out that I’m not the only person having problems with this tutorial and number of people have also had problems using his code.

if you didnot got error in line 66, then, i think, the error is in the prefab.

  1. public GameObject bulletThingy; is this one prefab ? or something in the scene ?

prefab should be ok, if not, you should find this object through script, as your player clone is created Something like:

void Start {
bulletThingy = GameObject.FindGameObjectWithTag (“hereTagOfBulletThingy”);
}

bulletThingy may need one new tag.

and check again your prefab. Edit all variable in it. Check all other gameobjects, wich are referenced to the player prefab. They should be all prefab too, or find them in prefabs script, with lines, as

void Start {
bulletThingy = GameObject.FindGameObjectWithTag (“hereTagOfBulletThingy”);
}

I’m pretty sure that the bullet is a prefab object, as sure as I can be with this tutorial which is still confusing me in parts - so, I did try the code you had just in case I was missing something and it threw up an error immediately about the object being unreferenced which I suppose would be correct if it’s not a game object in the scene.

So, I’m pretty much fed up with using the code as is - I’m going to try and change it up a little by not destroying the Object until all the lives are used up, something I’ll have to try and add as well as move the sprite back up to the “Respawn” point somehow - maybe translate/position will work (???)

try: transform.position = (x, y, z);

you can upload your project, we can check them (if you dont have something “privacy” in it :smile: )

hahaha - hell no, no need to hide any code :p. Everything I’ve done is basically from the tutorial and some code from a forum user PGJ, if I remember his name correctly.

Anyway, I have been working on the modified code and have ran into quite an odd problem. It involves the calling of the new modified RespawnPlayer() in a co-routine as I need to pause the running of it while the player dying sound is played before recommencing.

No when I call it all havoc breaks loose, the particle system and respawn sound keeps on playing over and over and over (aprox 130 times) and I don’t understand why. The player.position is reset in the first active line in the respawn code so the condition in the calling Update() function should then be false ie position.y is greater than - 20 preventing it from being called again.

I still haven’t modified the damage portion of the code or lives, but, I think that would be relatively straightforward.

So, if you would be able to tell me what is wrong and/or provide some suggestions with the code I would be most grateful - again sorry about the length.

Here is the class:

using UnityEngine;
using System.Collections;

public class Player_V : MonoBehaviour {

    //to be able to access public variables in the "class" PlayerStats and see them
    //in the inspector we need to first use: [System.Serializable]
    [System.Serializable]

    //a class within a class
    //I really don't understand why we would want to do this
    //I could understand it if we were using Inheritance
    //also set instance of the class

    //a class within a class within a class - a bit strange
    public class PlayerStats{

        public int playerHealth = 100;
        public int playerLives = 3;

    }//end class PlayerStats


    //make a declaration to the new class with an instance so we can
    //access the variables and make it "PUBLIC" so it can be "SEEN" in the Inspector
    public PlayerStats playerStats = new PlayerStats();

    public int fallBoundary = -20;
    public float spawnDelay = 2.8f;

    public AudioClip respawnSound;
    public AudioClip playerDie;
    AudioSource audio;

    public Transform particlePlay;
    public Transform RespawnPoint;


    public void Update(){
        if (this.transform.position.y <= fallBoundary) {
            print ("Transform: more than meets the Eye - literally ");

            //play dying sound *once*
            GetComponent<AudioSource> ().Play ();

            //all this does now is print a line to console
            //will eventually get rid of it later
            GameMaster_V.PlayerFallen(this);


            StartCoroutine (RespawnPlayer () );

            //this code works sort of moved into a new RespawnPlayer from GameMaster
            //commenting out causes all sorts of havoc with RespawnPlayer()
            //with it in - causes a major delay between sprite and respawn particles
            //transform.position = new Vector3(5.86f, 1.36f, 0.0f);


            PlayerDamage(0);
            }
  
    }//end Update()



    public IEnumerator RespawnPlayer(){
        //TODO respawn sound
        //make unity WaitForSeconds for 2.5 seconds
        yield return new WaitForSeconds (spawnDelay);


        //using transform.position to direct position instead of cloning
        //this doesn't seem to work
        transform.position = new Vector3(5.86f, 1.36f, 0.0f);

        //TODO respawn sound
        //audio = GetComponent<AudioSource>();
        GetComponent<AudioSource>().PlayOneShot(respawnSound, 1.0f);


        //so we are using a prefab instantiating and then destroying it!
        //using the 'clone' system
        Transform clone = Instantiate(particlePlay, RespawnPoint.position , RespawnPoint.rotation)as Transform;
      
        //now delete the clone after playing to stop it staying in the Heirachy
        //and eating up resources.
        Destroy (clone.gameObject, 3f);
      
    }//end RespawnPlayer
    //*/


    public void PlayerDamage(int damageAmount){
        playerStats.playerHealth -= damageAmount;
        //I suppose we check for playerHealth being equal or less than zero now?
        if (playerStats.playerLives <= 0) {
            print ("Sorry ... but, you are finished here!");
            //pass the player gameObject to KillPlayer
            GameMaster_V.KillPlayer(this);

            }//end if

    }//end PlayerDamage

}//end class Player_V*/

i could never understand those coroutine :slight_smile:

i think, you can make boolean for only one call of the respawn. Something like so:
9. bool isDead = false;
40. if (this.transform.position.y <= fallBoundary && !isDead) {
50. isDead = true;
88. isDead = false;

1 Like

Your solution did work - sort of :p. With the coroutine enabled after I ran off a platform the player kept on respawning continually - I had to laugh. So, I just made the RespawnPlayer function a normal void function instead of a coroutine and it worked properly (finally), although without the momentary pause that I was after.

But it does work now and that is more than I can say for the tutorial as it was. I suppose I could always stick a for loop in there that causes a momentary pause. I might start up a thread about coroutines to find out how they work exactly :face_with_spiral_eyes:.

So, thankyou greatly for all your time, patience and help. It really was very much appreciated.

1 Like

maybe Invoke for delay ?

  1. Invoke (“RespawnPlayer”, spawnDelay);

It’s true what they say “great minds think alike” :p.

I came across that method yesterday whilst trolling through questions on co-routines and pausing and tried it out immediately and the Invoke had the exact same effect as the co-routine so I’ll have to post a separate question on that because I really don’t understand that.

I’m not afraid to ask the dumb questions :smile:.

Thanks again for all the help - it’s like I’ve had my own personal tutor :sunglasses:.

try 40. if (transform.position.y <= fallBoundary) {
and then coroutine or invoke

perhaps the gameobject’s reference to itself makes all wrong :slight_smile:

or 74. this.transform.position = new Vector3(5.86f, 1.36f, 0.0f);

i am learning unity too, so i can not understand other scripts very good (i mean, if i didnot wrote them) :smile: