Why is my transform null in Update?

Here’s a snippet of code that passes transform data to a projectile:

  private void OnTriggerEnter2D(Collider2D collider)
        {
            // Check that the collision is with the player and that it
            // ignores any collider that is considered a trigger.
            if(collider.gameObject.tag == "Player")
            {
                // The player character will multiple box colliders,
                // but we only want to interact with the players hurt box here.
                if(!collider.isTrigger)
                {
                    moveSpeed = 0;
                    
                    // If true, the player is further right on the x axis then the skunk. Else, it is to the left.
                    if(playerCharacter.transform.position.x > transform.position.x)
                    {
                        // If true, the skunk is facing the player. Else, it's facing away.
                        if(transform.right.x == transform.localScale.x)
                        {
                            Flip();
                            facingRight = false;
                        }
    
                        else 
                        {
                            facingRight = false; // In case it was previously true
                        }
                    }
    
                    else
                    {
                        // If true the skunk is facing the player. Else, it's facing away.
                        if(transform.right.x != transform.localScale.x)
                        {
                            Flip();
                            facingRight = true;  
                        }  
    
                        else
                        {
                            facingRight = true;
                        }
                    }
                    Debug.Log(playerCharacter.gameObject.transform.position);
                    skunkProjectile.FireProjectile(projectileSpawnPoint, playerCharacter.gameObject.transform);
                    
                }
            }
        }

And here is the code that is suppose to receive that information:

public class SkunkProjectile : Projectile
{
    private int xVelocity;
    private bool fireLeft;

    private Transform fireDirection;

    /**
        The fire projectile class creates an instance of a projectile and sets 
        the game object that fires the weapon.
        @param spawnPosition (Transform) - The position the projectile spawns at in local space.
        @param projectileLauncher (GameObject) - The game object that fires the projectile.
    */
    virtual public void FireProjectile(Transform spawnPosition, Transform fireDirection)
    {
        Instantiate(this, 
            new Vector3(spawnPosition.position.x, spawnPosition.position.y, spawnPosition.position.z), 
            new Quaternion(0.0f,0.0f,0.0f,0.0f));

        this.fireDirection = fireDirection;
    }

    // Start is called on the frame when a script is enabled just before any of the Update methods are called the first time.
    private  void Start()
    {
        xVelocity = 2;
        rb = GetComponent<Rigidbody2D>();
        Destroy(gameObject, 2.0f);
    }
    
    // Update is called every frame, if the MonoBehaviour is enabled.
    private void Update()
    {
        rb.velocity = new Vector2(fireDirection.position.x, 0.0f); 
    } 
}

So why is it that when the code isn’t getting the new fireDirection on the next frame? When this code fires, I get a NullReferenceException no mater how many times i trigger the script.

So, I found something that seems to work, but I feel like my code is starting to get a bit spaghetti is there a clearer solution for this. I’ll post all relevant code.

PlayerCharacter:

    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.SceneManagement;
    
    [RequireComponent(typeof(Rigidbody2D), typeof(PlayerController), typeof(Collider2D))]
    public class PlayerCharacter : MonoBehaviour
    {
        // Required components
        private Collider2D collider2d;
        private Rigidbody2D rb;
        private PlayerController playerController;
    
        // Serialized components/variables
        [Tooltip("Used to calculate the amount of push back happens when the player is damaged.")]
        [SerializeField] private float hurtForce = 1f;
    
        [Tooltip("Players default health")]
        [SerializeField] private int health;
    
        [Tooltip("Player health UI element")]
        [SerializeField] private Text healthAmount;
    
        /**
            The rigid body is used in determining whether or not the player is infornt or behind an enemy.!--
            GetRBPosition allows another script to access this information.!--
            @return rb (RigidBody2D) - physics object used to get the players location
        */
        public Vector2 GetRBPosition() { return rb.position; }
    
        // Awake is called when the script instance is being loaded.
        private void Awake()
        {
            collider2d = GetComponent<Collider2D>();
            rb = GetComponent<Rigidbody2D>();
            playerController = GetComponent<PlayerController>();
        }
    
        // Sent when an incoming collider makes contact with this object's collider (2D physics only)
        private void OnCollisionEnter2D(Collision2D collision)
        {
            if(collision.gameObject.tag == "Enemy")
            {
                Enemy enemy = collision.gameObject.GetComponent<Enemy>();
    
                // Get the velocity state of the player controller. If the player is falling,
                // "kill" the enemy. Otherwise take damage from the enemy.
                if(playerController.getState() == VelocityState.falling)
                {
                    enemy.SetDeathTrigger(); // This kills the enemy. It also assumes that all enemies have one health.
                    playerController.Jump();
                }
    
                else
                {
                    // Determine which direction to push the player when they are in the hurt state
                    if(collision.gameObject.transform.position.x > transform.position.x)
                    {
                        rb.velocity = new Vector2(-hurtForce, rb.velocity.y);
                        playerController.updateState(VelocityState.hurt);
                        TakeDamage();
                    } 
                    else
                    {
                        rb.velocity = new Vector2(hurtForce, rb.velocity.y);
                        playerController.updateState(VelocityState.hurt);
                        TakeDamage();
                    }
                }
            }
        }
    
        public void TakeDamage()
        {
            health--;
            // Uncomment when UI is ready.
            // healthAmount.text = health.ToString();
            
            // Reset the level if player dies. 
            if(health <= 0)
            {
                SceneManager.LoadScene(SceneManager.GetActiveScene().name);
            }
        }
    }

Enemy:

using UnityEngine;

/// <summary>
/// This is the base class for enemy behavior that should be applicable to all enemies in the game.
/// This class handles basic enemy movement that can be refined in child classes if desired.
///</summary>
[RequireComponent(typeof(Rigidbody2D), typeof(Animator))]
public class Enemy : MonoBehaviour
{
    public float moveSpeed;
    // Required components
    protected Rigidbody2D rigidBody2D;
    protected Animator animator;
    protected bool facingRight = true;
    [Tooltip("A reference to the player's character. It should be some Joe prefab.")]
    protected GameObject playerCharacter;

    // Seralized fields
    // Increases speed by the factor set in the editor.
    [SerializeField] private float movementMultiplier = 1f;

    protected Vector3 movement;

    /**
        Used to update the enemies animator component.
    */
    public void SetDeathTrigger()
    {
        animator.SetTrigger("death");
    }

    /**
        Flip is a function that "flips" an enemies sprite
        by inverting it's x-scale.
    */
    protected void Flip()
    {
        transform.localScale = 
            new Vector3(-transform.localScale.x, transform.localScale.y, transform.localScale.z); 
    }

    // Awake is called when the script instance is being loaded.
    public void Awake() 
    {
        rigidBody2D = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
        playerCharacter = GameObject.FindGameObjectWithTag("Player");
    }

    private void Death()
    {
        Destroy(this.gameObject);
    }

    // Flip the sprite if it collides with platforms/walls, and invert its speed.
    private void OnCollisionEnter2D(Collision2D collider)
    {
        if(collider.gameObject.tag == "Platforms" || collider.gameObject.tag == "Enemy")
        {
            Flip();
            moveSpeed = -moveSpeed;
        }
    }
}

Skunk (inherits from enemy) :

using UnityEngine;

/// <summary>
/// The Skunk class is a child of the enemy class. 
/// It has the added ability of movement and attacking the
/// player if they get too close.
/// </summary>
public class Skunk : Enemy
{
    // Serialized fields. (Private memebers accessable by the editor.)
    [Tooltip("This is where your projectile prefab should be inserted")]
    [SerializeField] private SkunkProjectile skunkProjectile;
   
    [Tooltip("A transform that should be a child component of the skunk")]
    [SerializeField] private Transform projectileSpawnPoint;

    

    // Private memebers
    private float savedMoveSpeed;

    /**
        Get facing right is used to inform a caller if this sprite is facing in a x positive direction
        @return facingRight (bool) - return true if facing right. Otherwise false.
    */
    public bool GetFacingRight() { return facingRight; } 

    // Sent when another object enters a trigger collider attached to this object (2D physics only).
    private void OnTriggerEnter2D(Collider2D collider)
    {
        // Check that the collision is with the player and that it
        // ignores any collider that is considered a trigger.
        if(collider.gameObject.tag == "Player")
        {
            // The player character will multiple box colliders,
            // but we only want to interact with the players hurt box here.
            if(!collider.isTrigger)
            {
                moveSpeed = 0;
                
                // If true, the player is further right on the x axis then the skunk. Else, it is to the left.
                if(playerCharacter.transform.position.x > transform.position.x)
                {
                    // If true, the skunk is facing the player. Else, it's facing away.
                    if(transform.right.x == transform.localScale.x)
                    {
                        Flip();
                        facingRight = false;
                    }

                    else 
                    {
                        facingRight = false; // In case it was previously true
                    }
                }

                else
                {
                    // If true the skunk is facing the player. Else, it's facing away.
                    if(transform.right.x != transform.localScale.x)
                    {
                        Flip();
                        facingRight = true;  
                    }  

                    else
                    {
                        facingRight = true;
                    }
                }
                Instantiate(skunkProjectile, new Vector3(projectileSpawnPoint.position.x, projectileSpawnPoint.position.y, projectileSpawnPoint.position.z), new Quaternion(0.0f,0.0f,0.0f,0.0f));
            }
        }
    }

    // Sent when another object leaves a trigger collider attached to this object (2D physics only).
    private void OnTriggerExit2D(Collider2D collider)
    {
        if (collider.gameObject.tag == "Player")
        {
            if(!collider.isTrigger)
            {
                moveSpeed = savedMoveSpeed;
                // This makes sure that the skunk does move in the wrong direction
                // after the player has left its attack range
                if(moveSpeed > 0.0f && transform.localScale.x < 0.0f || 
                   moveSpeed < 0.0f && transform.localScale.x > 0.0f)
                {
                    moveSpeed = -moveSpeed;
                }                
            }
        }
    }

    // Called once per frame
    private void Update()
    {
        rigidBody2D.velocity = new Vector2(moveSpeed, rigidBody2D.velocity.y);
        // It won't try and update the movement speed if the skunk is stopped.
        if(moveSpeed != 0.0f)
        {
            savedMoveSpeed = moveSpeed;
        }
    }
}

Projectile:

using System.Collections;
using UnityEngine;

/// <summary>
/// The projectile class is the base class for all projectiles.
/// It is responsible for when the projectile spawns and when it is destroyed.
/// Child classes should always be attached to a spcific projectile prefab.
/// </summary>
public class Projectile : MonoBehaviour
{
    // Serialized feilds. (protected memebers accessable by the editor.)
    [Tooltip("This field comes from the projectile base class.")]
    [SerializeField]protected Rigidbody2D rb;

    private void OnTriggerEnter2D(Collider2D collision)
    {
        // When player collides with trigger, they take damage, and projectile disappears
        if(collision.gameObject.tag == "Player")
        {
            collision.gameObject.GetComponent<PlayerCharacter>().TakeDamage();
            Destroy(this.gameObject);
        }
    }
}

Skunk Projectile (inherits from projectile) :

using UnityEngine;

///<summary>
/// The skunk projectile is a child class of projectile.
/// This class is responsible for calculating the trajectory of
/// the projectile.
/// This script should only be used with the SkunkProjectile prefab. 
/// Which should be at: Assets\Prefabs\Projectiles\SkunkProjectile.prefab 
///</summary>
public class SkunkProjectile : Projectile
{
    private int xVelocity = 2;
    private bool fireLeft;

    private Transform fireDirection;

    // Start is called on the frame when a script is enabled just before any of the Update methods are called the first time.
       private  void Start()
{
    xVelocity = 2;
    rb = GetComponent<Rigidbody2D>();
    PlayerCharacter pc = FindObjectOfType<PlayerCharacter>();
    playerPosition = pc.transform.position;
    Skunk skunk = FindObjectOfType<Skunk>();
    skunkPosition = skunk.transform.position;
    Destroy(gameObject, 2.0f);
}

// Update is called every frame, if the MonoBehaviour is enabled.
private void Update()
{
    Debug.Log(skunkPosition);
    if (playerPosition.x < skunkPosition.x)
    {
        rb.velocity = new Vector2(-2, 0);
    }
    else if(playerPosition.x > skunkPosition.x)
    {
        rb.velocity = new Vector2(2, 0);
    }
}

All advice will be appreciated :slight_smile: