Applying logic to the most recently selected character

Godot Version

Godot 4.4

Question

Any way to access the most recently selected object? For example I have 2 characters I can switch between by clicking on them. If I click on one, I want to take that object’s value and do something.

And I have the camera attached to a staticbody, I would like to take the static body along with its child camera and have it tween (or tween it) to the selected character’s position.

I managed to get this somewhat working, but, I want to have the camera go to the most recently selected characters position, and then be able to click between characters and have it tween and follow between the two depending on which on I just clicked, along with maybe other conditions to change tween destinationlocation of body. Coming up short on all my solutions.

Was thinking of checking if the mouse position was equal to the character position when clicked, setting a selected variable as true if those are equal, and then do another if statement where I apply the logic to whichever character had the position equal to the mouse…

but there is an offset, so it’s never exactly equal and my brain cannot logically figure how to remove this offset and apparently I don’t understand clear instruction, plus that could be the wrong approach to begin with, so I am stumped.

Any advice is much appreciated! Thank you for reading

2D or 3D?

Assuming 2D, you can attach an Area2D node to your character class, and add a collisionshape that covers the sprite. Area2D nodes have input_event signals, so if you attach that signal to your character script you can do something like

func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int) -> void:
	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
			print("Character selected: ", self)

How you wire this up with other stuff is up to you. Maybe set up an Autoload called Globals, and replace the print statement above with Globals.currently_selected_character = self

2 Likes

Thank you for your reply. This is for 2D.

I have gotten the area 2D logic you have provided. My intention for it can be described better than how I did in the initial post, I apologize and will edit the original post.

so I have the camera attached to a staticbody, I would like to take the static body along with its child camera and have it tween (or tween it) to the selected character’s position.

I managed to get this somewhat working, but, I want to have the camera go to the most recently selected characters position, and then be able to click between characters and have it tween and follow between the two depending on which on I just clicked, along with maybe other conditions to change tween location of body and camera

sorry for rambling

must sign off now for a while but will be back on later

This should be possible with signals. Have the character class emit a character_selected signal, and have the staticbody holding the camera handle that signal. Better yet, have the signal in some autoload, say SignalBus.gd (so that we don’t need the camera registering with every single character’s character_selected signal):

SignalBus.gd:

signal character_selected

Then, the area2d input event logic above becomes:

func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int) -> void:
	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
			SignalBus.character_selected.emit(self)

And in your camera (or the staticbody that holds the camera) script, you would have something like:

func _ready():
    SignalBus.character_selected.connect(_on_character_selected)

func _on_character_selected(character):
    print(character.global_position)
    # Whatever tween logic you want here
2 Likes

Dude I appreciate your response very much. Your code works perfectly. I still cannot wrap my head around signals. Idk if you saw but this is an edit. I didn’t realize what you meant by setting that autoload script and that is crazy. Literally exactly what I am going for.

Thanks again for your help.

1 Like

No problem, I’m glad it helped.

It took me a while to understand signals as well - I made them more complicated than they needed to be. I think the way to go about understanding how they work is to understand the sorts of problems they’re good at solving, and work backwards from there.

I can give an example that helped me a lot with this. Imagine a player getting hit by a bullet. When this happens, you might want a lot of different things to happen, for example, (1) bullet_hits_armor sound effect plays, (2) health bar decreases, (3) floating text number (red) “-5” floats away from the player to show the hp loss, (4) screen gets a little blurry for a fraction of a second, or blood hits the screen, or whatever other sort of visual feedback you want to add on top, etc.

Without signals, you could do this by having all the code lumped in one place, like func get_hit_by_bullet(...), in player.gd. But then player.gd needs to know the locations in the scene tree for all the various nodes that take care of these different actions, like $Root/Audio/AudioPlayer.play("get_hit_by_bullet.mp3") and $$Root/Player/PlayerUI/HealthBar.change_health_bar_amount(...) and so on. You can imagine that this is quite brittle - if you move anything around in the scene tree, and you forget to change these lines, then these functionalities will just break. Also, even if all those things stay in the same place, if the names of the methods ever change, like AudioPlayer.queue_play instead of AudioPlayer.play, things break.

So, that’s the philosophy without signals.

The philosophy with signals is, when the player gets hit by a bullet, it sort of shouts into the void “Hey, if anyone out there is listening, I just got hit by a bullet”. And each separate module, like the audio player for example, declares at some point “I’m a listener for people getting hit by a bullet. If anyone ever gets hit by a bullet, let me know”.

The shouting into the void here is the some_signal.emit(...) call. The declaration to listen and react to a specific signal is the some_signal.connect(foo) call, and what it really does is says “whenever some_signal is emitted, call the foo method”.

1 Like

Thank you so much for diving into this. Your explanation has been the best one yet, because I have always thought “isn’t instantiating a variable and calling it later just sending a signal?” I still need to chew on it more to really get why and where to use them over variables, but I’m the type of person to overthink it and make everything a signal, so I end up going the tons of variables and functions route and keep my fingers crossed. Your solution helped me wrap my head around sort of what it was doing, I could follow the ball, but your reply has helped me see why that is a better way of calling functions. Still don’t know if I’m seeing it right but I feel like I’m getting the outline of the picture and that’s more than I could say yesterday. Appreciate it!