Changing Hue/Brightness of Sprite (sprite flash)

Godot Version

GODOT4

Question

Hey there! I think this may be a simple one. What do I have to do to make this work? Trying to get the sprite to flash (on damage), so turning it’s saturation all the way up for a split second, then returning it to the original. This is for the Player node’s child Sprite2D.

var _original_color = $Player/Sprite2D.modulate

func flash_color():
	$Sprite2D.modulate = Color.from_hsv(_original_color.h, 0, _original_color.v)

flash_color() would trigger in the player hurt event. I’m getting the “Invalid get index ‘modulate’ (on base: ‘null instance’).” error. What can I do to fix the locations? Or maybe I’m approaching a sprite flash the wrong way?

The error message states that it is trying to find the property on a null instance - or in other words, the $Sprite2D is null and can’t be found. I suspect that you instead want to use $Player/Sprite2D just as in the line where you initialize the original color.

1 Like

I tried that, and now it yields this error:

Node not found: “Player/Sprite2D” (relative to “CharacterBody2D”)

I mean it was just a guess from my side what the correct path is. Please show pictures of your node tree so that we can see what the correct path is.

1 Like

image

Here we go! Thanks for helping

Ah then the first node path seem to be wrong. Please check if this works:

var _original_color = $Sprite2D.modulate

func flash_color():
	$Sprite2D.modulate = Color.from_hsv(_original_color.h, 0, _original_color.v)

Basically the script is attached on the Player node already, so you shouldn’t reference player in the node path as that is already the relative starting point

I see! I tried implementing though and got “Invalid get index ‘modulate’ (on base: ‘null instance’).”

Hm so both paths (variable definition and in the function) are without player now?
That’s really weird, as $Sprite2D should be correct assuming we are talking about a script attached to the Player noder

1 Like

This script extends another script called Character, does that matter? Should I try doing this with the Character script, even if this Hero script is the one the Player is connected to?

What matters is where the script is attached to. I am currently assuming the code you posted is in a script attached to the Player (so the one that opens if you click this button). Is that correct? (doesn’t matter what it inherits from)

image

Yes that’s correct

that’s weird then… Can you show the full script just to be sure?

class_name Hero extends Character

@export_category("Equip")
@export var _has_sword : bool
@onready var _attack_input_buffer : Timer = $HitBox/InputBuffer
@export var _dash_speed : float = 80
@export var _slide_speed : float = 80
@export var _double_jump_mult : float = .8
@export var _djump_dust : PackedScene
@export var _slide_dust : PackedScene
@export var _dash_dust : PackedScene
@export_category("Mechanics")
@export var _can_animation_cancel : bool = false #burst animation
@export var _is_bursting : bool

var _original_color = $Sprite2D.modulate 
var _sword : RigidBody2D
func jump():
	if _is_dead or _wants_to_attack or _is_attacking:
		return
	elif is_on_floor():
		_spawn_dust(_jump_dust)
		velocity.y = _jump_velocity
		_is_dashing = false
	elif not _is_hit and _double_jump > 0 and not is_on_floor() and velocity.y >= 0:
		if not _is_dashing:
			velocity.y = _jump_velocity * _double_jump_mult
			_double_jump -= 1
			_spawn_dust(_djump_dust)
		
func face_left():
	if _is_dead || _is_attacking:
		return
	if not _is_dashing:
		_is_facing_left = true
		_player_direction = -1
		_sprite.flip_h = not _sprites_face_left
		_hit_box.scale.x = 1 if _sprites_face_left else -1
		changed_direction.emit(_is_facing_left)

func face_right():
	if _is_dead || _is_attacking:
		return
	if not _is_dashing or _is_attacking:
		_is_facing_left = false
		_player_direction = 1
		_sprite.flip_h = _sprites_face_left
		_hit_box.scale.x = -1 if _sprites_face_left else 1
		changed_direction.emit(_is_facing_left)
		
func attack():
	if _is_dead || _is_hit || _is_attacking:
		return
	_wants_to_attack = true
	_attack_input_buffer.start()
	await _attack_input_buffer.timeout
	_wants_to_attack = false
	_is_dashing = false
	
func can_equip_sword() -> bool:
	return not _has_sword && not _is_dead
func equip_sword(sword : RigidBody2D):
	_sword = sword
	_has_sword = true
func flash_color():
	$Sprite2D.modulate = Color.from_hsv(_original_color.h, 0, _original_color.v)
func take_damage(amount : int, direction : Vector2):
	flash_color()
	_is_dashing = false
	_is_hit = true
	_is_attacking = false
	_wants_to_attack = false
	_hit_points = max(_hit_points - amount,0)
	health_changed.emit(float(_hit_points) / _max_hit_points)
	velocity = direction * Global.ppt * _hurt_recoil
	if _hit_points <= 0:
		_die()
	if _inv_dur != 0:
		become_invincible(_inv_dur)

func dash(): #dash and ground slide
	if _is_dead or _wants_to_attack or _is_attacking or _is_hit:
		return
	#ground slide, requires being on the ground
	if is_on_floor() and not _is_dashing:
		_spawn_dust(_slide_dust)
		_is_dashing = true
		if _is_attacking:
			velocity.x = 0
		else:
			velocity.x = (_dash_speed)* _player_direction * Global.ppt
		
	#air dash, requires a dash charge and not touching the ground
	elif not is_on_floor() and _dash_charge > 0:
		_dash_charge -= 1
		_can_animation_cancel = false
		_spawn_dust(_dash_dust)
		_is_dashing = true
		velocity.x = (_dash_speed)* _player_direction * Global.ppt

func drop_sword():
	#if not _has_sword:
		return
	#_has_sword = false
	#_sword.be_dropped(global_position)
	#_sword = null
	
func _die():
	if _has_sword:
		drop_sword()
	super._die()

func _on_hit_box_area_entered(area : Area2D):
	if _is_dead || not _is_attacking:
		return
	if not is_on_floor() && area.global_position.y > global_position.y:
		velocity.y = _jump_velocity / 4
	super._on_hit_box_area_entered(area)

Here we go!

Ahh I think I realized what the error is now. This line is outside any function:

var _original_color = $Sprite2D.modulate

So at this point, the node tree is not ready and it can not find the child node.
Try delaying the variable initialization until the tree is ready:

@onready var _original_color = $Sprite2D.modulate
1 Like

Yes that did the trick! Experimenting now with different numbers. Thank you for all the help!

Happy to help :gdparty:

1 Like