Character Switching in Godot

Godot Version

4.2

Question

I’d like to toggle between 3 characters however I have no idea of going about that.
I tried a state system but I’m not sure if that’s the best way to go about it and I couldn’t get it to work. I’d also like a cool-down between switches and once you’re already in a character you can’t switch to that character again until you’re in a different one.

(I’m fairly new to Godot and most likely would need a very simplistic response to aid my comprehension.)

A state machine works well enough for me. If you have to switch characters for a large portion of the game, you may want to make a singleton to handle it.

extends Node2D

var max_time = 1.0
var time_left = 0.0
var can_change = false

enum {CHAR_1, CHAR_2, CHAR_3}
var state

# Called when the node enters the scene tree for the first time.
func _ready():
	time_left = max_time
	state = CHAR_1


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	if time_left > 0.0:
		time_left -= delta
		print(time_left)
	elif time_left <= 0.0 and can_change == false:
		print("Can now change characters")
		can_change = true
	
	match state:
		CHAR_1:
			if Input.is_action_just_pressed("ui_accept") and can_change == true:
				state = CHAR_2
				print(state)
				print("char 2 active")
				reset_char_switch_delay()
		CHAR_2:
			if Input.is_action_just_pressed("ui_accept") and can_change == true:
				state = CHAR_3
				print(state)
				print("char 3 active")
				reset_char_switch_delay()
		CHAR_3:
			if Input.is_action_just_pressed("ui_accept") and can_change == true:
				state = CHAR_1
				print(state)
				print("char 1 active")
				reset_char_switch_delay()

func reset_char_switch_delay():
	time_left = max_time
	can_change = false
1 Like

Your method worked perfectly, thank you so much. Just a small pet peeve though, how could I prevent the player from switching to the same character while already in it?
Basically if you’re character 1 and press character 1’s input, you shouldn’t be able to switch to character 1 again until you’re in characters 2 and 3.

(For those who dont want the switch delay upon opening the game, simply remove the “time_left = max” from the ready(): function)

enum CharacterSelect { char_1, char_2, char_3 }

var max_time = 1.0
var time_left = 0.0
var can_switch = false

var currentCharacter = CharacterSelect.char_1

func _process(delta):
	if time_left > 0.0:
		time_left -= delta
		print(time_left)
	elif time_left <= 0.0 and can_switch == false:
		can_switch = true
	
	if Input.is_action_just_pressed("switch_to_1") && can_switch == true:
		switch_to_character(CharacterSelect.char_1)
	if Input.is_action_just_pressed("switch_to_2") && can_switch == true:
		switch_to_character(CharacterSelect.char_2)
	if Input.is_action_just_pressed("switch_to_3") && can_switch == true:
		switch_to_character(CharacterSelect.char_3)

func switch_to_character(current_character: CharacterSelect) -> void:
	currentCharacter = current_character
	
	match currentCharacter:
		CharacterSelect.char_1:
			char_1_data()
		CharacterSelect.char_2:
			char_2_data()
		CharacterSelect.char_3:
			char_3_data()

func char_1_data():
	reset_char_switch_delay()
func char_2_data():
	reset_char_switch_delay()
func char_3_data():
	reset_char_switch_delay()

func reset_char_switch_delay():
	time_left = max_time
	can_switch = false

You could either add another condition to the if statements like lets say make a variable called ‘var current_char = 1’ … and if you switch to char 2 set current_char = 2 and so on…

	if Input.is_action_just_pressed("switch_to_1") && can_switch == true && current_char != 1:
		current_char = 1
		switch_to_character(CharacterSelect.char_1)

… Or make another state machine and keep the input to call the current character out of that state.

Also just a note, personally I’d set my inputs up like this using elif after the first if. This prevents any of the other if’s from activating if you press one of the other input buttons at the same time.


	if Input.is_action_just_pressed("switch_to_1") && can_switch == true:
		switch_to_character(CharacterSelect.char_1)
	elif Input.is_action_just_pressed("switch_to_2") && can_switch == true:
		switch_to_character(CharacterSelect.char_2)
	elif Input.is_action_just_pressed("switch_to_3") && can_switch == true:
		switch_to_character(CharacterSelect.char_3)
1 Like

Thank you again it worked :slight_smile:

As of 4.0, there is something called “await” so another alternative method rather than the timer could be that switching is true until after something like a transition animation plays and then the player could indeed switch again.

ex.

var can_switch = true

func switch():
  await %transitionanimation.animation_finished
  can_switch = false
1 Like

There is a very simple way to do it (although possibly not the most correct):

  • set_physic_process(false) on your character 1 and hide it (visible).
  • Instantiate character 2 at the global position of your character 1.
  • Delete character 1 (queue_free).
1 Like