#How to make a working score counter?

108 messages · Page 1 of 1 (latest)

ancient topaz
#

I'm pretty much having first exposure here and I have two jobs in my group's little project right now.
One was adding and changing textures of our player and enemies, and I did that after fumbling on both.

However, I'm still stumped on how to make a working score counter given that most of the tutorials I see use either GDScript (to which I have no qualms to) or use event nodes, that I know I should be doing with...
But the code of our game is C# and I see no proper event node that relates to the enemy being deleted.

Details:

Just need a barebones score counter. Just have the score's number displayed is enough.
Enemies are removed by using GetParent().QueueFree() contained in a separate C# file handling HP.
I'm very new, please guide me through.
Images here if they can help.

shell vault
#

On a Label node I'd add a script with a function like this. Lets say it's in a Label inherited class I'll call ScoreCounter.

private int score;
public void incrementScore() {
    score += 1;
    Text = "Score: " + score;
}```
Then in your `enemy` class get a refrence to that node and call the function. You can do that either by defining the refrence as exported `[Export] private ScoreCounter;` and then assigning it through the editor or declare the refrence without the `[Export]` and do it programmatically in `_Ready()` with `GetNode<>()`
#

Let me know if anything was unclear about that

vague juniper
#

Sorry but I disagree with @shell vault ; I would instead store a score variable as a global script, because presumably you will want to access this variable in lots of different places (in game, in other scenes, etc.) so keeping it only in a label would be limiting.

To do create a global script whose variables you can access from anywhere, you can do that using "singletons", read here https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html

this script would have all the relevant functions to handle score, and then if you'd want to access it and display it in a label, you could do something like text = scriptName.score

shell vault
#

That would work too, personally I've just never been a fan of the singleton design pattern and it's drawbacks.

#

Before implementing a singleton you should definitely read up on the theory of them

#

In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to a singular instance. One of the well-known "Gang of Four" design patterns, which describes how to solve recurring problems in object-oriented software, the pattern is useful when exactly one object is needed to coordinate ac...

ancient topaz
ancient topaz
#

So first is a Label node that I'll add onto our main game scene (shooter_game), right?

shell vault
#

Yes

#

It'll need to be a child of an canvaslayer to be visable

ancient topaz
#

Just add a node under ShooterGame in the Scenes, right?

shell vault
#

not quite

#

youll wanna do it like this

#

the CanvasLayer represents a ui while the Label is some text in the ui

ancient topaz
#

So I add a CanvasLayer first then add the Label?

shell vault
#

yes

ancient topaz
#

Huh, I can't seem to add it...

shell vault
#

type canvaslayer into the search

#

it inherits directly from Node

ancient topaz
#

Ah, there we go

#

Something like this, right?

shell vault
#

yea looks good

ancient topaz
#

Then from hereon I attach a Script to the Label, right?

shell vault
#

yes, it would also be a good idea to rename your label to something recognizable

#

like ScoreLabel

ancient topaz
#

I have done so already

#

Here I am now

shell vault
#

ok youll wanna add the variable youll store the score numeric in and functions to acces it

#
using Godot;
using System;

public partial class ScoreCounter : Label {
    private int score;

    public void incrementScore() {
        score += 1;
        Text = "Score: " + score;
    }

    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {
    }
}
#

something like this to start

ancient topaz
#

++ would work fine, right?

shell vault
#

yes thats fine

#

really how you adjust the score is up to you

ancient topaz
#

All right, got that down

shell vault
#

ok yea looks good

#

save that and build the project

ancient topaz
#

Game runs but nothing on the Label

shell vault
#

we haven't actually linked anything up to the function we just made so that's to be expected

ancient topaz
#

Yeah, but at least it's still running

shell vault
#

ok so now on whatever we want to increase the score we wanna add some code

#

i think you said that you wanted to increase the score when an enemy dies. correct?

ancient topaz
#

Yeah

#

Enemies are removed through the HP script with GetParent().QueueFree()

shell vault
#

ok

ancient topaz
shell vault
#

hmm splitting the health off the main enemy script was an odd choice but thats workable

#

are you using this same health script for the player or is this for the enemy only?

ancient topaz
#

Same health script

#

At least from what I gather

shell vault
#

ok yea that complicates things a bit

#

id suggest to do the following

#

in health.cs where you delete the node ```
if (GetParent() is enemy){
((enemy)GetParent()).die();
}
GetParent().QueueFree();

#

then in enemy.cs ```
[Export] ScoreLabel score;
public void die() {
score.incrementScore();
}

#

so what this will do is that when the health determines it should die it calls die() in the associated enemy if there is one

ancient topaz
shell vault
#

you would want it inside

#

really the whole point of adding this die function allows you to easily add more functionally to an enemies death without modifying a bunch of out of context code

ancient topaz
shell vault
#

yes you should put the variable at the top. but it can exist anywhere

ancient topaz
#

Then I'll just place it there with the others since it's prepended by [Export] too.

shell vault
#

sounds good

ancient topaz
shell vault
#

ok now if you build you should see that new exported variable in the node inspector

ancient topaz
#

It... didn't.

shell vault
#

assign the score label thats in your scene an its should update whenever you kill an enemy

ancient topaz
#

Let me check real quick-

shell vault
#

when i say build i mean click this button

#

that recompiles all your c# code which is needed for godot to find new exports

ancient topaz
#

Not sure why but I'm having build errors...

shell vault
#

you got one too many closing braces

#

the line should be ((enemy)GetParent()).die();

#

and for reference these build errors tell you exactly where the error was encountered

#

its formatted like this
C:\Users\phooo\Documents\Godot Projects\Demo\Player.cs(24,27): } expected
file path (line, charicter) error message

ancient topaz
#

I see

#

I've fixed it now

shell vault
#

cool so now assign the score label refrence

#

and then running should increment the score when an enemy dies

#

the last basic thing would be putting some default text in the score

ancient topaz
shell vault
#

which you could do by setting Text i the labels _Ready

#

in the inspector for the enemy node

#

itll be right next to your other enemys expoted values

ancient topaz
#

Um... It's not here...

shell vault
#

you need to do this is the main scene

ancient topaz
#

From shooter_game?

shell vault
#

yes

#

you can only assign node references to nodes that exist in the same scene with the inspector

ancient topaz
shell vault
#

oh you are using a spanner

#

ok we will need to do it programmatically then

#

in _Ready for enemy add the following score = GetNode<ScoreLabel>("/root/ShooterGame/CanvasLayer/ScoreLabel");

ancient topaz
#

Added.

shell vault
#

ok now whenever an enemy spawns in it should find the label itself

#

go ahead and run it and kill a few enemies

ancient topaz
#

It works!

shell vault
#

cool, so the last thing youll wanna probably do is put some default text into the score

#

so its not blank before you kill your first enemy

#

you could do that by putting Text = "Score 0"; in ScoreLabel._Ready()

ancient topaz
#

I just did it with the Text property from the Inspector, it also worked.

#

Anyways, thank you so much for the help!

shell vault
#

ok nice

#

no problem

#

have a good evening or day or whatever