Forum begins after the advertisement:


[Part 22] NullReferenceException with Sortable objects

Home Forums Video Game Tutorial Series Creating a Rogue-like Shoot-em Up in Unity [Part 22] NullReferenceException with Sortable objects

Viewing 2 posts - 1 through 2 (of 2 total)
  • Author
    Posts
  • #15537
    Terence
    Keymaster
    Helpful?
    Up
    0
    ::

    If you’ve used the Sortable class from Part 22 of our series, you will notice that when you run the game, you may get a ton of NullReferenceException errors from your Console. This is because, in our article and video, we forgot to have the PlayerMovement script inherit the Start() function — which is responsible for setting up the SpriteRenderer — from Sortable.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using Terresquall;
    
    public class PlayerMovement : Sortable
    {
    
        public const int DEFAULT_MOVESPEED = 5;
    
        //Movement
        [HideInInspector]
        public Vector2 moveDir;
        [HideInInspector]
        public float lastHorizontalVector;
        [HideInInspector]
        public float lastVerticalVector;
        [HideInInspector]
        public Vector2 lastMovedVector;
    
        //References
        Rigidbody2D rb;
        PlayerStats player;
    
        protected override void Start()
        {
            base.Start();
            player = GetComponent<PlayerStats>();
            rb = GetComponent<Rigidbody2D>();
            lastMovedVector = new Vector2(1, 0f); //If we don't do this and game starts up and don't move, the projectile weapon will have no momentum
        }
    
        void Update()
        {
            InputManagement();
        }
    
        void FixedUpdate()
        {
            Move();
        }
    
        void InputManagement()
        {
            if(GameManager.instance.isGameOver)
            {
                return;
            }
    
            float moveX, moveY;
            if (VirtualJoystick.CountActiveInstances() > 0)
            {
                moveX = VirtualJoystick.GetAxisRaw("Horizontal");
                moveY = VirtualJoystick.GetAxisRaw("Vertical");
            }
            else
            {
                moveX = Input.GetAxisRaw("Horizontal");
                moveY = Input.GetAxisRaw("Vertical");
            }
            
    
            moveDir = new Vector2(moveX, moveY).normalized;
    
            if (moveDir.x != 0)
            {
                lastHorizontalVector = moveDir.x;
                lastMovedVector = new Vector2(lastHorizontalVector, 0f);    //Last moved X
            }
    
            if (moveDir.y != 0)
            {
                lastVerticalVector = moveDir.y;
                lastMovedVector = new Vector2(0f, lastVerticalVector);  //Last moved Y
            }
    
            if (moveDir.x != 0 && moveDir.y != 0)
            {
                lastMovedVector = new Vector2(lastHorizontalVector, lastVerticalVector);    //While moving
            }
        }
    
        void Move()
        {
            if (GameManager.instance.isGameOver)
            {
                return;
            }
    
            rb.velocity = moveDir * DEFAULT_MOVESPEED * player.Stats.moveSpeed;
        }
    }

    This causes the NullReferenceExceptions on the player character, because the Sortable class does not have its sorted variable set, but LateUpdate() tries to use it every frame (see yellow highlight below).

    Hence, to prevent small oversights from causing a cascade of NullReferenceExceptions, we also want to modify the Sortable class to ignore objects that forget to call base.Start() when overriding their own Start() functions (see green highlight below).

    using UnityEngine;
    
    /// <summary>
    /// This is a class that can be subclassed by any other class to make the sprites
    /// of the class automatically sort themselves by the y-axis.
    /// </summary>
    [RequireComponent(typeof(SpriteRenderer))]
    public abstract class Sortable : MonoBehaviour
    {
    
        SpriteRenderer sorted;
        public bool sortingActive = true; // Allows us to deactivate this on certain objects.
        public float minimumDistance = 0.2f; // Minimum distance before the sorting value updates.
        int lastSortOrder = 0;
    
        // Start is called before the first frame update
        protected virtual void Start()
        {
            sorted = GetComponent<SpriteRenderer>();
        }
    
        // Update is called once per frame
        protected virtual void LateUpdate()
        {
            if (!sorted) return;
            int newSortOrder = (int)(-transform.position.y / minimumDistance);
            if (lastSortOrder != newSortOrder) sorted.sortingOrder = newSortOrder;
        }
    }
    #15586
    Terence
    Keymaster
    Helpful?
    Up
    0
    ::

    Found another bug with the Sortable script. If you have multiple sortable objects with different Minimum Distance values set for each of them, this breaks the sorting order for some of the objects. To fix this, we’ll need to save the smallest minimum distance value and use that for all of the sortables.

    Another thing is that this line (-transform.position.y / minimumDistance) will cause a divide by zero error, so we need to wrap it around like this Mathf.Max(0.00001f, minimumDistance), so that if minimumDistance is 0, we will use a value of 0.000001f instead.

    Here is the latest script:

    using UnityEngine;
    
    /// <summary>
    /// This is a class that can be subclassed by any other class to make the sprites
    /// of the class automatically sort themselves by the y-axis.
    /// </summary>
    [RequireComponent(typeof(SpriteRenderer))]
    public abstract class Sortable : MonoBehaviour
    {
    
        SpriteRenderer sorted;
        public bool sortingActive = true; // Allows us to deactivate this on certain objects.
        public float minimumDistance = 0.2f; // Minimum distance before the sorting value updates.
        int lastSortOrder = 0;
    
        static float activeMinimumDistance;
    
        // Start is called before the first frame update
        protected virtual void Start()
        {
            sorted = GetComponent<SpriteRenderer>();
            activeMinimumDistance = Mathf.Min(minimumDistance, activeMinimumDistance);
        }
    
        // Update is called once per frame
        protected virtual void LateUpdate()
        {
            if (!sorted) return;
            int newSortOrder = (int)(-transform.position.y / Mathf.Max(0.000001f, activeMinimumDistance)minimumDistance);
            if (lastSortOrder != newSortOrder) sorted.sortingOrder = newSortOrder;
        }
    }
Viewing 2 posts - 1 through 2 (of 2 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: