Variable isn't assigned, even though I have assigned it in the Start() method

I have created a boss game object with a script BossBulletSpawner attached to it.

public class BossBulletSpawner : MonoBehaviour
{
    [SerializeField]
    private GameObject BossBullet;

    private string[] bulletColors = { "Blue", "Red", "DarkBlue", "Green", "Pink", "Purple", "Red" };
    List<Sprite> bulletImages = new List<Sprite>(); //should turn this into a dictionary later on

    System.Random rnd = new System.Random();
    Vector3 bossPos;
    void Start()
    {
        foreach (string bulletColor in bulletColors)
        {
            Sprite img = Resources.Load<Sprite>("Sprites/Bullets/Bullet" + bulletColor);
            bulletImages.Add(img);
        }
        
        int maxIndex = bulletColors.Length;

        Vector3 bossPos = transform.position;
        bossPos.z -= 1;
        Instantiate(BossBullet, bossPos, transform.rotation);
        BossBullet.GetComponent<BulletBehavior>().setSprite(bulletImages[rnd.Next(0, maxIndex)]);
        BossBullet.GetComponent<BulletBehavior>().setDirSpeed(new Vector2(3, -3), 3); BossBullet.GetComponent<BulletBehavior>().setSprite(bulletImages[rnd.Next(0, maxIndex)]);
    }
}

I pass my bullet prefab to it. It works relatively well, bullet gets spawned on the screen with a random sprite. But I’ve got an issue with giving it speed and direction.

Here is my script attached to the bullet prefab:

public class BulletBehavior : MonoBehaviour
{
    Rigidbody2D bulletBody;
    void Start()
    {
        bulletBody = GetComponent<Rigidbody2D>();
    }
    public void setSprite(Sprite sprite)
    {
        gameObject.GetComponent<SpriteRenderer>().sprite = sprite;
    }
    public void setDirSpeed(Vector2 newDirection, float newSpeed)
    {

        bulletBody.velocity = newSpeed * newDirection;
        Debug.Log(newSpeed * newDirection);
        Debug.Log(bulletBody.velocity); 
    }
}

Even though I have clearly assigned a value to bulletBody in the Start() method I get the error “UnassignedReferenceException: The variable bulletBody of BulletBehavior has not been assigned.”

I’ve also tried to initialize bulletBody inside of the setDirSpeed method like so:

    public void setDirSpeed(Vector2 newDirection, float newSpeed)
    {
        Rigidbody2D bulletBody = GetComponent<Rigidbody2D>();
        bulletBody.velocity = newSpeed * newDirection;
        Debug.Log(newSpeed * newDirection);
        Debug.Log(bulletBody.velocity); 
    }

I’m not getting any errors using this method, but the velocity of my bullet doesn’t change. What have I done wrong?

This is an untested theory BUT:

Since you are instantiating then immediately assigning, I expect that Unity does not schedule the Start function on your new object to occur until after it’s finished executing the path it’s currently on.

Since Rigidbody2D is on the same game object as BulletBehaviour I think you can quite safely make bulletBody a serialized field instead of getting it on start.

If for any reason you can’t do that then I’d suggest making an initialise function where you pass in all the stuff you initialise it with; the sprite, direction and speed. Then you can get the bulletBody at the start of that and be sure that it’s definitely getting set before you use it.

If this was the issue then there’s no shame in getting stuck on this, knowing how unity schedules its magic functions is sometimes a bit of a dark art!

jackmw94 has a valid point for a start. You should not use Start when you initialize your own component. Use Awake to setup references to other components. Only use Start when you want to modify other components from this one so that ensures that Awake on those other components has already run.

Though I also agree that assigning the component to a serialized reference through the inspector would save you from some additional potential issues.

Apart from that your main issue is that you do not set the sprite and do not set velocity of the object you just created / instantiated. You actually modify the prefab and not the instance. Changing the sprite on the prefab won’t affect the already instantiated object but you would directly modify the prefab in your project so the next time you instantiate that prefab it would have a different sprite, the one from your last modification. While this “sort of” works for sprites, it certainly does not work for setting the velocity of the object. Prefabs are not part of the scene and do not participate in the physics engine at all. So setting the velocity on a prefab just doesn’t work. Apart from the fact that neither Start nor Awake has been called on the prefab, those are only called on objects in the scene, i.e. after it has been instantiated into the scene.

The Instantiate method returns the newly created instance. Note that Instantiate doesn’t have to be used on the gameObject reference to your prefab. You can also instantiate a reference to any component on the root object of the prefab. The advantage is that Instantiate will return the same type that was passed in as source object… You can simply declare your prefab variable like this:

[SerializeField]
private BulletBehavior BossBullet;

Now when you instantiate this prefab (Keep in mind when you change the type you have to reassign your prefab in the inspector) the instantiate method will return the BulletBehaviour instance of the newly created object. So there’s no need for GetComponent. Also it’s less error prone since you can not choose a prefab in the inspector that doesn’t have a BulletBehaviour component. Since your code requires such a behaviour anyways, this is a good way to avoid common mistakes and prevents user errors.

So your instantiate code should look something like that:

    BulletBehaviour bulletInst = Instantiate(BossBullet, bossPos, transform.rotation);
    bulletInst.setSprite(bulletImages[rnd.Next(0, maxIndex)]);
    bulletInst.setDirSpeed(new Vector2(3, -3), 3);

Keep in mind that this code does only work with the above mentioned modifications. So your BossBullet variable is of type BulletBehaviour and you renamed your Start method inside your BulletBehaviour to Awake.

ps: I don’t really understand your seperate direction and speed parameters. Your bullet won’t move at the provided speed if the provided direction is not normalized. In your case it is not normalized but has a length of about 4.24 units which you multiply by your provided speed of 3 which will result in a speed of about 12.72