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 3 posts - 1 through 3 (of 3 total)
  • Author
    Posts
  • #15537
    Terence
    Level 30
    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()
    {
        <mark class="green">base.Start();</mark>
        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()
    {
        <mark class="green">if (!sorted) return;</mark>
        int newSortOrder = (int)(-transform.position.y / minimumDistance);
        if (lastSortOrder != newSortOrder) <mark>sorted</mark>.sortingOrder = newSortOrder;
    }

    }

    #15586
    Terence
    Level 30
    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;
    
    <mark class="green">static float activeMinimumDistance;</mark>
    
    // Start is called before the first frame update
    protected virtual void Start()
    {
        sorted = GetComponent<SpriteRenderer>();
        <mark class="green">activeMinimumDistance = Mathf.Min(minimumDistance, activeMinimumDistance);</mark>
    }
    
    // Update is called once per frame
    protected virtual void LateUpdate()
    {
        if (!sorted) return;
        int newSortOrder = (int)(-transform.position.y / <mark class="green">Mathf.Max(0.000001f, activeMinimumDistance)</mark><del>minimumDistance</del>);
        if (lastSortOrder != newSortOrder) sorted.sortingOrder = newSortOrder;
    }

    }

    #15936
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Here’s another update to fix 2 issues:

    1. Replaced the minimumDistance with a MIN_DISTANCE constant to streamline things. Since all the sprites have to share the minimum distance, or else the code won’t work consistently; and we also won’t change the minimum distance in the game. If you want to change the minimum distance, just update the code directly.
    2. Added a new part to update the lastSortOrder. It is meant to prevent the sorting order from updating every frame, but because it was never updated, it never got to do its job properly.
    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.
    <del>public float minimumDistance = 0.2f; // Minimum distance before the sorting value updates.</del>
    <mark class="green">public const float MIN_DISTANCE = 0.2f;</mark>
    int lastSortOrder = 0;
    
    <del>static float activeMinimumDistance;</del>
    
    // Start is called before the first frame update
    protected virtual void Start()
    {
        sorted = GetComponent<SpriteRenderer>();
        <del>activeMinimumDistance = Mathf.Min(minimumDistance, activeMinimumDistance);</del>
    }
    
    // Update is called once per frame
    protected virtual void LateUpdate()
    {
        if (!sorted) return;
        int newSortOrder = (int)(-transform.position.y / <mark class="green">MIN_DISTANCE</mark><del>Mathf.Max(0.000001f, activeMinimumDistance)minimumDistance)</del>;
        if (lastSortOrder != newSortOrder) <mark class="green">{
            lastSortOrder = sorted.sortingOrder
            </mark>sorted.sortingOrder = newSortOrder;<mark class="green">
        }</mark>
    }

    }

Viewing 3 posts - 1 through 3 (of 3 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: