Monede, semnale și scor
Adaugi monede care dispar când sunt atinse, folosind Area2D și semnale, apoi afișezi scorul pe ecran. Jocul e gata.
La finalul fișei, elevul poate:
- explice ce este un semnal și când este emis
- construiască o zonă de detecție cu Area2D
- conecteze semnalul body_entered la o funcție din script
- păstreze scorul într-o variabilă și să îl afișeze cu un Label
Ai nevoie de:
- Godot 4
- Proiectul ColecteazaMonede, cu pereții din fișa 4
Ai un personaj care se mișcă într-o zonă închisă. Îi dăm un scop: să strângă monede.
Cum arată la final

Uită-te aici ori de câte ori nu ești sigur că ai făcut bine: în stânga arborele de noduri, în mijloc cum arată terenul.
1. Semnalul
Până acum, codul tău a întrebat în continuu: „se apasă o tastă? se apasă o tastă?”, de 60 de ori pe secundă.
Pentru unele lucruri asta e risipă. E mai bine să fii anunțat când se întâmplă ceva.
Un semnal este exact acest anunț: un mesaj trimis de un nod atunci când se produce un eveniment.
Area2D este nodul care definește o zonă de detecție. El nu oprește pe nimeni — doar
observă cine intră. Când un corp intră în zonă, emite semnalul body_entered.
Moneda va arăta așa:
Coin [Area2D]
├── Imagine [Sprite2D]
└── Coliziune [CollisionShape2D]
Aceeași structură ca la Player, dar cu alt tip de rădăcină: Area2D în loc de
CharacterBody2D. Personajul se ciocnește, moneda doar detectează.
2. Construim scena Coin
- Scene → New Scene → Other Node → caută
Area2D→ Create. - F2 →
Coin. - Cu
Coinselectat: Ctrl+A →Sprite2D→ F2 →Imagine. - Trage
icon.svgpeste câmpul Texture din Inspector. - La
Imagine, deschide Transform și pune Scale0.12și0.12— moneda e mai mică decât personajul. - Selectează din nou
Coin, apoi Ctrl+A →CollisionShape2D→ F2 →Coliziune. - La Shape alege New CircleShape2D. Deschide Shape și pune Radius
10. - Salvează cu Ctrl+S, sub numele
coin.tscn.
3. Scriptul monedei
- Cu nodul
Coinselectat, apasă Attach Script 📜, lasă numelecoin.gdși Create. - Șterge tot și scrie:
extends Area2D
func _on_body_entered(body):
if body.name == "Player":
print("Monedă colectată")
queue_free()
| Instrucțiunea | De unde vine | Ce face |
|---|---|---|
_on_body_entered(body) | chemată de semnal | tu scrii conținutul, dar nu o chemi; o cheamă semnalul |
body | dat de semnal | corpul care a intrat în zonă |
body.name | proprietate din Godot | numele nodului, așa cum apare în panoul Scene |
if | cuvânt al limbajului | execută ce urmează doar dacă e adevărat |
queue_free() | funcție a lui Godot | o chemi tu; șterge nodul din joc |
4. Conectăm semnalul
- Selectează nodul
Coin. - În panoul din dreapta, lângă Inspector, apasă fila Node, apoi Signals.
- În listă găsește
body_entered(body: Node2D). - Dublu-clic pe el. Se deschide o fereastră.
- Verifică jos, la Receiver Method, că scrie
_on_body_entered. - Apasă Connect.
Lângă funcția din script a apărut o iconiță verde 🟢 în marginea din stânga. Înseamnă că semnalul e legat.
5. Punem monedele în joc
- Deschide
main.tscn. - Trage
coin.tscndin FileSystem în Viewport. - Repetă până ai cinci monede, așezate în locuri diferite, între pereți.
Ai grijă ca toate să fie copii direcți ai lui Main, nu ale altui nod:
Main
├── Player
├── WallTop, WallBottom, WallLeft, WallRight
└── Coin, Coin2, Coin3, Coin4, Coin5
- Ctrl+S, apoi F5. Treci cu personajul peste fiecare monedă.
Moneda dispare, iar în Output apare Monedă colectată.
6. Scorul
Jocul funcționează, dar nu ține minte nimic. Adăugăm un scor.
Scorul este o informație care se schimbă în timpul jocului, deci îl păstrăm într-o
variabilă. Îl afișăm cu un nod Label.
- În
main.tscn, selecteazăMainși adaugă un nodLabel. F2 →ScoreLabel. - În Inspector, la Text, scrie
Scor: 0. - Deschide Transform și pune
Positionla(30, 20)— colțul din stânga sus. - Cu
Mainselectat, apasă Attach Script, lasă numelemain.gd, Create. - Scrie:
extends Node2D
var score: int = 0
func add_score():
score += 1
$ScoreLabel.text = "Scor: " + str(score)
| Instrucțiunea | De unde vine | Ce face |
|---|---|---|
score | ales de tine | numele variabilei |
: int | tip din Godot | ține numere întregi, fără zecimale |
add_score() | funcție scrisă de tine | nu începe cu _, deci o chemi tu — din coin.gd |
score += 1 | cuvânt al limbajului | prescurtare pentru score = score + 1 |
$ScoreLabel | scurtătură din Godot | găsește nodul copil cu numele ScoreLabel |
.text | proprietate din Godot | textul afișat de nodul Label |
str() | funcție a lui Godot | transformă numărul în text, ca să poată fi lipit de o frază |
- Deschide
coin.gdși adaugă o linie, înainte dequeue_free():
extends Area2D
func _on_body_entered(body):
if body.name == "Player":
get_parent().add_score()
queue_free()
get_parent() este o funcție a lui Godot: întoarce nodul părinte al monedei, adică Main.
Pe el chemi apoi add_score(), funcția scrisă de tine în main.gd.
Citit de la stânga la dreapta: ia părintele meu → cheamă-i funcția add_score. Moneda nu
ține scorul; îi cere lui Main să-l crească.
- Salvează tot și rulează cu F5.
Fiecare monedă colectată crește scorul cu 1.
7. Verificarea finală
| De verificat | Cum |
|---|---|
| Personajul se mișcă în toate direcțiile | săgețile |
| Se oprește la toți cei patru pereți | mergi în fiecare margine |
| Fiecare monedă dispare o singură dată | strânge-le pe toate |
| Scorul final este egal cu numărul monedelor | citește eticheta |
| Nu apar erori roșii | uită-te în Output după ce închizi jocul |
Exerciții
🔑 Rezolvare
Nu se întâmplă nimic. Moneda rămâne pe loc, scorul nu crește, iar în Output nu apare nicio eroare.
Codul din funcție e corect, dar funcția nu e chemată niciodată. Godot nu are de unde să știe că tu voiai să o folosească.
De reținut: „nu apare nicio eroare” nu înseamnă „totul e în regulă”. Uneori înseamnă că o parte din cod nici măcar nu rulează.
🔑 Rezolvare
Apare A intrat: Player.
print cu mai multe valori le lipește una după alta — un mod simplu de a vedea ce se
întâmplă în cod fără să ghicești.
Această tehnică se numește depanare prin afișare și e prima pe care o încearcă orice
programator când ceva nu merge: pune un print și vezi dacă ajunge acolo.
Șterge linia după exercițiu.
🔑 Rezolvare
În coin.gd:
extends Area2D
@export var valoare: int = 1
func _on_body_entered(body):
if body.name == "Player":
get_parent().add_score(valoare)
queue_free()
În main.gd:
func add_score(puncte):
score += puncte
$ScoreLabel.text = "Scor: " + str(score)
@export face variabila să apară în Inspector. Selectezi o monedă din main.tscn, îi
pui valoare = 5, și doar aceea valorează 5 puncte — fără să scrii cod separat pentru ea.
Este exact felul în care se construiesc jocurile adevărate: o singură scenă, reglată diferit pentru fiecare instanță.
🔑 Rezolvare
extends Node2D
var score: int = 0
func add_score():
score += 1
$ScoreLabel.text = "Scor: " + str(score)
if score == 5:
$ScoreLabel.text = "Ai câștigat!"
O variantă mai bună nu folosește numărul 5 scris direct, ci numără monedele existente:
if get_tree().get_nodes_in_group("monede").size() == 0:
Pentru asta, fiecare monedă trebuie pusă în grupul monede, din fila Node → Groups.
Avantajul: adaugi sau ștergi monede din scenă fără să mai modifici codul.
Ce ai învățat
- Un semnal este un anunț trimis când se produce un eveniment.
Area2Ddetectează, nu oprește;body_enteredse emite la intrarea unui corp.- Semnalul trebuie conectat la funcție, altfel funcția nu e chemată niciodată.
queue_free()șterge un nod la sfârșitul cadrului.$NumeNodgăsește un copil;str()transformă un număr în text.get_parent()urcă un nivel în arbore — de aceea contează unde stă fiecare nod.
Proiectul complet
Main [Node2D] (main.gd)
├── Player [CharacterBody2D] (player.gd)
├── WallTop, WallBottom, WallLeft, WallRight [StaticBody2D]
├── Coin ×5 [Area2D] (coin.gd)
└── ScoreLabel [Label]
Fișierele: main.tscn · player.tscn · coin.tscn · main.gd · player.gd · coin.gd
Ai construit un joc 2D complet, cu scene refolosibile, mișcare, coliziuni, semnale și interfață.