How to change a global player stat from a collision2D trigered?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Syl

Greets!

In my character creation scene, i got Area2Ds to click in order to define the stats. Got the Area2Ds and their collision2Ds signaled, but what’s the script please?

Could you post your code and explain in a bit more details what you are trying to achieve?

It sounds like what you need is a button. I’m not sure why you would use an Area2D to detect clicking.

Bernard Cloutier | 2020-01-30 13:48

I’m strugglin with Godot files over the net to get my character creation started… :slight_smile: Basically, it’s kind of a psychedelic limbo with aspects (sprite and areas2Ds) spawnin randomly on the screen and reactin to mouse click to increase the wanted stats/build of the char.
Like this:

Imgur: The magic of the Internet

The code of my random area2D group:

extends Node2D

onready var screen_size = get_viewport().get_visible_rect().size
onready var areas = get_tree().get_nodes_in_group("areas")
onready var timer = get_node("Timer")
onready var format_string = "Health: %s"  
onready var actual_string = format_string % (GlobalP.health) 


func _ready():
        randomize()
        randomize_areas()
        timer.connect("timeout", self, "on_timeout")
        timer.start(2)
        print(actual_string)
func randomize_areas():
        for area in areas:
            area.position = get_rand_position(screen_size)
func get_rand_position(var size):
        var rand_x = rand_range(0, size.x)
        var rand_y = rand_range(0, size.y)
        return Vector2(rand_x, rand_y)
func on_timeout():
        var next_seconds = rand_range(1, 3)
        for area in areas:
            area.visible = !area.visible
        randomize_areas()
        timer.start(next_seconds)

And the code of my Collision2D linked to one of the area2d:

func _on_Area2D_input_event(viewport, event, shape_idx):
	if event is InputEventMouseButton and event.pressed and event.button_index == 1:
		GlobalP.health = + 10

At least that’s what i want to achieve with my GlobalP autoload, but i could click as much as i want on the collisionshape2D determinin the health, it stays at 100 (printed on output). Dunno the syntax to make it works…

You understand?

Actually i don’t know how to display the result with a label… :s

Syl | 2020-01-30 14:57

Ok it’s a bit clearer.

Some basic stuff you’ve probably already checked but wouldn’t hurt to doublecheck:

Displaying stuff on a label is as easy as $Label.text = 100

Bernard Cloutier | 2020-01-30 15:26

The area2D is signaled and pickable, should be with my GlobalP.health wich don’t have an output to display it.

here’s the code of my GlobalP autoload:

extends Node2D

var energy = 100
var energy_regeneration = 0.5
var health = 100
var health_regeneration = 1
var health_increased = null
var power = 100
var power_regeneration = 1

Putin ‘onready var $label.text = GlobalP.health’ on my parent Node2D script, i get the error: ‘expected identifier for member variable name’…

Syl | 2020-01-30 15:59

The dollar symbol $mynodename is shorthand for get_node(“mynodename”). Meaning you need to add a Label node first, not declare a variable with this name. See this tutorial: https://docs.godotengine.org/en/3.1/getting_started/scripting/gdscript/gdscript_basics.htm

If you put a break point on the line GlobalP.health = + 10, does it hit it when you click one of the areas?

Bernard Cloutier | 2020-01-30 16:10

The page of your link doesn’t exist. When i put a label node, like ‘$label.text = GlobalP.health’ at the begining of my Node2D script, got ‘unexpected token’ error.

And how do you put a breakpoint on the GlobalP.health = + 10? Sry for the noob questions…

Syl | 2020-01-30 16:44

I’m learnin what i need for my game, as i create it, askin simple questions for simple lines of code.
Don’t think that i’m askin too much.

Syl | 2020-01-30 18:01

Don’t take this the wrong way, but I first assumed you were at least familiar with the basics as presented in the short tutorials. Things such as knowing how to access a node with $ or placing a breakpoint. In this case, it’s more a matter of spending a little bit of time now to avoid lots of issues later on.

I don’t think I would be helping you by regurgitating the basics covered by the tutorials. If you were having issues with something that wasn’t in an easy to digest tutorial or video, it would be another matter.

But if you prefer asking how to place a breakpoint in this forum instead of searching “breakpoint” in the docs, go ahead. Getting an answer might take a bit longer though.

Bernard Cloutier | 2020-01-30 18:20

I knew the bit about $nodes, that just doesn’t fit in my curent script, dunno why, and sry, first time i hear about breakpoint, and i have browsed, tell you, on the docs and tutos of Godot.
Glad of havin learnt about it, thxs, but still, if helpin out tellin me what it is or a few lines of code is regurgitatin for you, i’m sorry for the inconvenience. But i won’t spend hours rummaging through a mountain of irrelevant docs for that info and those lil lines of code.
Got others things to learn and create about my project, and in time, i will understand the trick of those precise lines, maybe on my own, maybe with the help of a friendly and helpful comunity member.

Syl | 2020-01-30 19:05

The line onready var $label.text = GlobalP.health doesn’t work because you are declaring a variable with the invalid name $label.text. Use $ like you would use get_node. Ex:

onready var my_label_node = $Label # this assumes the current node has a child node called Label

I suggest you add a label node to your scene with the following script attached:

extends Label

func _ready():
    $"/root/GlobalP".connect("health_changed", self, "_on_GlobalP_health_changed")

func _on_GlobalP_health_changed(new_value):
    self.text = new_value

Add this to your GlobalP.gd script (I assume you’ve set it up as an autoloaded singleton):

signal health_changed(new_value)

var health = 100 setget _set_health

func _set_health(value):
    health = value
    emit_signal("health_changed", value)

These two bits of code will give you a label that auto updates its content to match the current value of GlobalP.health, without having to check it every frame. This pattern (connnecting to a signal and emitting that signal when a value has changed) is called the observer pattern Observer · Design Patterns Revisited · Game Programming Patterns. It is very common and useful for reacting to events within your game without having every object check every other object for a change.

The setget keyword lets you define setter and getter functions (or just a setter in this case), which let you control how your variable is accessed and changed, as well as to react accordingly. GDScript basics — Godot Engine (3.2) documentation in English

Can’t help you further with your main issue though. Although why not use button nodes instead of basically recreating a button with an Area2D?

Bernard Cloutier | 2020-01-30 20:22

Man, i’m tired, and i’ll check all that tomorow. I’m just so glad that it takes a good turn, and so grateful for your help, cheers! :slight_smile:

Syl | 2020-01-30 20:46

Ok, got the scripts changed and atached, but playin the test, got this error when clickin my Area2D:

Invalid set index text (on base: ‘Label(HealthLabel.gd)’) with value of type ‘int’.

Shouldn’t the CollisionShape2D script be changed also, to signal the ‘health_changed’?
Got this at the moment:

extends CollisionShape2D

	
func _on_Area2D_input_event(viewport, event, shape_idx):
	if event is InputEventMouseButton and event.pressed and event.button_index == 1:
		GlobalP.health = + 10

Otherwise, the info is really interestin, i’ll read it, specially about the observer pattern, unfortunately your link is broken.

Syl | 2020-01-31 10:26

Oops, I forgot that gdscript doesn’t automatically casts int values to string. Replace the line self.text = new_value with this one self.text = str(new_value).

Also this line GlobalP.health = + 10 is not adding 10 to health, it is setting health to positive 10 (same as GlobalP.health = - 10, the resulting health would just be -10). Use this instead: GlobalP.health += 10 The difference is subtle (= + vs +=), but is completely different. The += operator is a shortcut. GlobalP.health += 10 just means GlobalP.health = GlobalP.health + 10. Using += means you don’t have to write the variable twice.

Shouldn’t the CollisionShape2D script be changed also, to signal the ‘health_changed’?

Nope! That is the beauty of setters and getters. Since GlobalP.health now has a setter, when I assign a value to it (ex: GlobalP.health = 50), the gdscript interpreter instead does: GlobalP._set_health(50). Since the health_changed signal is emitted by _set_health, you don’t have to worry about sending the signal, it will be done automatically every time someone changes the health variable.

Here is the observer link again (there was an unwanted period at the end): Observer · Design Patterns Revisited · Game Programming Patterns

Bernard Cloutier | 2020-01-31 14:35

Great! That works great. My thxs for you, you taught me so much as well. Good on all your projects!

Syl | 2020-02-01 14:12