Receiving null value when switching player-controlled sprites

Godot Version: v4.6.1

How do I stop receiving null value for my sprites animation?

I have more or less came up with a solution to creating switchable sprites for the Player in my game like an RPG adventurer party. The solution was to use Enums and a variable to store the current constant within that enum, which are the four different characters that I have. Switching characters using the keyboard inputs work just fine following the if statements I have written (refer below), however whenever I run the game the debugger would continuously generate hundreds and hundreds of null error since my sprites animations does not have that value. The sprite only performs idle or walking animation when I press their keyboard inputs each time, so checking if the character is moving doesn’t apply whatsover in the code.

Is there a code I’m missing?

extends CharacterBody2D

const SPEED: float = 300.0
var last_dir: Vector2 = Vector2.RIGHT
var current_member = Enum.Member.TARO

@onready var anim_sprite: AnimatedSprite2D = $AnimatedSprite2D

func _physics_process(_delta: float) -> void:
	_process_movement()
	_process_animation()
	move_and_slide()

#-----------------------------------------------------
# MOVEMENT & ANIMATION
#-----------------------------------------------------
func _process_movement() -> void:
	var direction: = Input.get_vector("left", "right", "up", "down")
	
	if direction != Vector2.ZERO:
		velocity = direction * SPEED
		last_dir = direction
	else:
		velocity = Vector2.ZERO
		
func _process_animation() -> void:
	if velocity != Vector2.ZERO:
		_play_animation("walk", last_dir)
	else:
		_play_animation("idle", last_dir)
		
func _get_member_prefix():		
	if Input.is_action_just_pressed("switch_MAKO"):
		match current_member:
			Enum.Member.MAKO:
				return "mako."
		current_member = Enum.Member.MAKO
	elif Input.is_action_pressed("switch_NIOU"):
		match current_member:
			Enum.Member.NIOU:
				return "niou."
		current_member = Enum.Member.NIOU
	elif Input.is_action_just_pressed("switch_TARO"):
		match current_member:
			Enum.Member.TARO:
				return "taro."
		current_member = Enum.Member.TARO
	elif Input.is_action_just_pressed("switch_RAI"):
		match current_member:
			Enum.Member.RAI:
				return "rai."
		current_member = Enum.Member.RAI

func _play_animation(action: String, dir: Vector2) -> void:
	var prefix = _get_member_prefix()
	
	if dir.x != 0:
		anim_sprite.flip_h = dir.x < 0
		anim_sprite.play(str(prefix) + str(action))
	elif dir.y != 0:
		anim_sprite.play(str(prefix) + str(action))
extends CharacterBody2D

const MEMBER_SPRITES = {
	Enum.Member.TARO : preload("res://scenes/taro.tres"),
	Enum.Member.MAKO : preload("res://scenes/mako.tres"),
	Enum.Member.NIOU : preload("res://scenes/niou.tres"),
	Enum.Member.RAI : preload("res://scenes/rai.tres")
	}

It’s not clear for me what works, what doesn’t work, when doesn’t work and how do you need it to work.

I think you can get more help if you make those things clear for more people to understand.

You are running this for every sprite in every physics frame. If there are sprites that do not have the animation, then the debugger will tell you so. A lot.

The code that is missing is you checking to make sure the animation exists before trying to run it.

Changing your sprites so they all have the same animations would work as well.

So sorry! I’ll elaborate further about my problem.

Whenever I run the problem, the player sprite loads up a default sprite of a character A, so when I click a key (e.g. press P) that switches to character B, character B’s idle sprite runs, but when I move the sprite around it doesn’t switch to the walking animation. But if I click P again it enables the walking animation, but stops the idle animation entirely. Hope this is a better explanation!

I’ll attach a screenshot of the debugger here.

I’ll also link the tutorial video I followed. It’s the chapter called Enums from 47:27-1:05:44.

Recreate Stardew Valley in Godot & and master advanced Godot concepts | Clear Code

Yes I think you get my problem. However, I have rechecked my animations names a lot and I have coded no characters incorrectly. I’ll show you my animations for the different sprites which are all within the same AnimatedSprite2D frames.

You only set the value of last_dir if direction != Vector2.ZERO. Any frame it is == Vector2.ZER0, the variable will be null. Set it to Vector2.ZERO alongside velocity in the else part of the statement.

1 Like

I would also skip setting velocity = Vector2.ZERO in else, cause you already check for it in the if. You can only reach the else part if velocity == Vector2.ZERO, so you set it to what it already is. It’s not a big issue but here, you can skip a few lines of code if you exclude it.

Perhaps you could skip the entire else and instead of sending last_dir as an argument to the animations, just send direction. It could be possible remove the entire variable last_dir as it seems like it is the same as direction but renamed. I could misunderstand something here.

1 Like

I would add an else here too. Right now it looks like the Vector2.ZERO-arguments you send can not really be used? If both x and y != 0, both checks will fail. And the reason it works now is because you pass a null, not a zero, zero. So the checks work as intended but for the wrong reason.

func _play_animation(action: String, dir: Vector2) -> void:

	var prefix = _get_member_prefix()

	

	if dir.x != 0:

		anim_sprite.flip_h = dir.x < 0

		anim_sprite.play(str(prefix) + str(action))

	elif dir.y != 0:

		anim_sprite.play(str(prefix) + str(action))

This function can and will return null quite often.

You are mixing Input.is_action_just_pressed and Input.is_action_pressed, do you intend to hold these buttons down, or press the button to swtich to another prefix? If the former, do not use _just_pressed, if the latter, move your actions to an input function and store the last-used prefix.

1 Like

I think I get the problem now after listening to everyone.

Because the physics_process function occurs every 1/60th of a second. What happens is that when the get_prefix function is called with no key press, u return null.

The proper solution is to introduce this in _unhandled_input as I now know from reading godot docs someone gave on coding a game.

But the fastest hack is this. Add an else statement at the end of the entirely block of code getting the prefix that returns current_character.name. Just take the enum key and switch it to lower case maybe.

1 Like

The problem if you tie it all to input is that as its laid out now the animations and everything will be tied to input. No animation unless input means no idle animation will be possible.

I dont see any issues with using process here but there are a few weird little things in the code. Like latest_dir being null if no direction, the animation function not having any way to deal with a Vector2(0,0) and perhaps a few possibly unnecessary/confusing variables and lines of code.

I agree with gertkeno that the way of switching between enums seems sketchy.

It is hard to teach a newbie to filter the sprite node from the children of the scene, remove it, then add a new one.

Easier to teach them my hack due to the fact that we don’t know how messy their code actually is.

I don’t even know if current_character is used or just vibe coded in from ChatGPT XD

1 Like

Hahaha no it is a bit messy right now but it will be easy to tidy up after some more practice.

But damn i remember how confusing it all was when starting out.

1 Like