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
- This topic has 2 replies, 1 voice, and was last updated 2 months ago by Terence.
-
AuthorPosts
-
August 8, 2024 at 2:13 pm #15537::
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 thePlayerMovement
script inherit theStart()
function — which is responsible for setting up the SpriteRenderer — fromSortable
.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, butLateUpdate()
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 ownStart()
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; }
}
August 14, 2024 at 2:29 pm #15586::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 thisMathf.Max(0.00001f, minimumDistance)
, so that ifminimumDistance
is 0, we will use a value of0.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; }
}
September 29, 2024 at 11:59 pm #15936::Here’s another update to fix 2 issues:
- Replaced the
minimumDistance
with aMIN_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. - 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> }
}
- Replaced the
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: