
Construeix un joc de plataformes 2D complet amb
Godot
UNA GUIA PRÀCTICA - 1ª EDICIÓ
Daniel Buckley Pablo Farias
academy.zenva.com youtube.com/c/zenva linkedin.com/company/zenva
©2026 Zenva Pty Ltd, tots els drets reservats
Introducció i configuració de l'entorn 8
Instal·lació de Godot i versió 11
Com instal·lar la versió 4.6 11
Inicialització del projecte 13
Visió general del disseny del joc 13
Controls i moviment del jugador 13
Monedes i objectes de col·lecció 16
Creació de carpetes de projectes 22
Configuració de la primera escena 25
Construir el món amb mapes de mosaic 29
Ajustar les dimensions de les rajoles 37
Afegir propietats de col·lisió 39
Pintura de rajoles a l'escena 41
Correcció d'errors habituals de TileSet 47
Creació de l'escena del jugador 49
Entendre l'escena del jugador 49
Creació del node del reproductor 49
Configuració del mapa d'entrada 65
Per què utilitzar les accions d'entrada? 65
Implementació del moviment del jugador 71
Configuració de l'script del reproductor 71
Definició de variables de moviment 73
Implementació del moviment bàsic 73
Visuals: Flipping the Sprite 80
Sistema d'animació del jugador 83
Entendre les animacions a Godot 83
Configuració del reproductor d'animació 83
Creació de l'animació inactiva 84
Configuració de fotogrames clau 88
Entendre les pistes de RESET 89
Creació de l'animació de moviment 89
Creació de l'animació de salt 95
Escriptura de la lògica d'animació 98
Configuració de l'escena enemiga 103
Programació del moviment de l'enemic 113
Mecànica bàsica: salut i puntuació 130
Configuració de la salut del jugador 130
Creació de la funció Take Damage 130
Actualització de l'script Enemy 131
Gestió de les devolució de trucades de física 132
Creació de col·leccionables 136
Configuració de l'escena de la moneda 136
Programació de l'animació de la moneda 141
Actualització de l'script del reproductor 144
Tractament de la col·lisió 145
Creació d'un script de càrrega automàtica 150
Implementació del Global Score 154
Actualització de l'script del reproductor 154
Lògica de superació del nivell 156
Organització de la jerarquia de l'escena 156
Creació de la bandera final 161
Escriptura de la bandera final 166
Implementació de la transició d'escena 169
Construcció de la interfície d'usuari 173
Configuració del CanvasLayer 173
Creació de la pantalla de salut 175
Ús de contenidors per al disseny 180
Creació del text de la partitura 184
Connexió de la lògica amb senyals 190
Actualització de l'script del reproductor 190
Creació de l'script de la IU 191
Implementació de funcions d'actualització 193
Connectant-ho tot a _ready 193
Efectes visuals i àudio 196
Implementació de Damage Flash 196
Implementació de Screen Shake 198
Evitar la caiguda infinita 200
Redimensionament i posicionament 201
Creació d'un fons de mosaic 202
Organització dels nodes de fons 203
Implementació de l'script Parallax 207
Inspecció d'actius d'àudio 209
Actualització de l'script del reproductor 211
Duplicant el primer nivell 214
Edició del mapa de rajoles 218
Elements del joc de posicionament 221
Bandera final 221
Monedes 222
Enemics 223
Canviar els colors de fons 225
Repte: alterar la bandera final 229
Creació del menú principal 232
Creació de l'escena del menú 232
Integració del menú al bucle de joc 257
Actualització de lògica de Game Over 259
- Amplieu aquest projecte 261
- Uneix-te a un Game Jam 261
- Exploreu la documentació 261
- Reconstrueix sense el llibre 262
- Continueu el vostre aprenentatge 262
Referència completa del codi font 263
Introducció
Benvingut al teu viatge cap al desenvolupament de jocs amb Godot. Tant si sempre has somiat amb construir els teus propis mons o simplement vols entendre la màgia que hi ha darrere dels teus jocs preferits, estàs al lloc correcte.
El desenvolupament de jocs és una combinació única de lògica i creativitat. Requereix que penseu com un programador, un artista i un dissenyador alhora. Això pot semblar intimidatori, però Godot està dissenyat per fer que aquest procés sigui el més intuïtiu possible. S'encarrega de la càrrega pesada de renderitzar gràfics i calcular la física perquè pugueu centrar-vos en allò que importa: fer que el vostre joc sigui divertit.
La millor manera d'aprendre a desenvolupar jocs és fent. En aquest llibre, no només llegirem sobre codi ni memoritzarem botons de la interfície. Construirem un joc de plataformes en 2D real i jugable des de zero.
Al llarg dels capítols següents, abordarem totes les parts del projecte, des del moviment i la física fins a les interfícies d'usuari i els bucles de joc.
Cada capítol es basa en l'últim per ampliar el vostre conjunt d'eines i, al final, tindreu un joc complet que podreu mostrar i ampliar pel vostre compte.
Com utilitzar aquesta guia
Per treure el màxim profit d'aquesta experiència, tracta aquest llibre com un taller més que com una conferència. Aquí teniu alguns consells per ajudar-vos a tenir èxit:
- Seguiu-lo activament. És temptador només llegir el codi, però escriure-lo tu mateix ajuda a construir la memòria muscular. Aprendràs més coses fent errors ortogràfics i arreglant-les del que mai ho faràs copiant-enganxant.
- Experimenta sovint. Quan configurem la velocitat d'un jugador a 200, prova de canviar-la a 500 o 50. Mira què passa quan desactives la gravetat o canvies el color d'un sprite. Trencar coses és sovint la manera més ràpida d'entendre com funcionen.
- Utilitzeu la referència. Hem inclòs una referència completa del codi font al capítol final. Si el vostre codi no funciona i no trobeu l'error, compareu el vostre script amb la referència per tornar al bon camí.
El motor està esperant i el teu primer projecte està a la volta de la cantonada. Passem pàgina i configurem el vostre entorn de desenvolupament.
Introducció i configuració de l'entorn
Benvingut a aquesta guia per crear un joc de plataformes en 2D amb el motor Godot. Junts, construirem un joc de plataformes 2D complet des de zero, incorporant mecàniques de joc essencials i elements de poliment perquè el joc sigui atractiu i emocionant.
Requisits previs
Per garantir una experiència d'aprenentatge fluida, es requereix una comprensió bàsica del motor Godot i GDScript. Hauríeu d'estar familiaritzat amb els següents conceptes fonamentals:
- La interfície de l'editor de Godot i l'estructura del projecte (el moll d'escena, l'inspector i el sistema de fitxers)
- El sistema de nodes i escenes (creació de nodes, construcció d'arbres d'escenes, desar escenes)
- Fonaments de GDScript (variables, tipus, operadors, condicionals, bucles i funcions)
- Conceptes bàsics en 2D com ara coordenades, transformacions i relacions pares-fills
Si alguns d'aquests conceptes us són nous, us recomanem que consulteu els recursos següents per posar-vos al dia:
Aprèn Godot des de zero
(https://academy.zenva.com/product/intro-to-godot-ebook/)
Miniprojectes amb Godot
(https://academy.zenva.com/product/godot-mini-projects-ebook/) Alternativament, si preferiu cursos de vídeo:
- Introducció al desenvolupament del joc Godot 4 (https://academy.zenva.com/product/intro-to-godot-4-game-develop ment/)
Godot 4 Mini-Projectes
(https://academy.zenva.com/product/godot-4-mini-projects/)
Dit això, seguirem cada pas amb cura mentre treballem a l'editor i escrivim codi junts.
Fitxers del projecte
Per seguir els tutorials, necessitareu els actius del projecte. També hem proporcionat el projecte complet com a referència. Podeu descarregar-los mitjançant els enllaços següents:
Fitxers d'actius:
(https://academy.zenva.com/wp-content/uploads/2026/02/Assets-Go dot-2D-Platformer.zip)
Projecte complet:
(https://academy.zenva.com/wp-content/uploads/2026/02/Complete-Project-Godot-2D-Platformer-v4.6.zip)
Llicències d'actius de tercers
Alguns dels actius proporcionats en aquest curs estan subjectes a llicències de tercers:
Actius de Kenney URL de sprites i fons: https://kenney.nl/
Llicència: Creative Commons CC0 (https://creativecommons.org/publicdomain/zero/1.0/)
Actius creats per Zenva
Totes les altres obres d'art i recursos multimèdia inclosos als fitxers del projecte que no s'enumeren més amunt han estat creats per Zenva i es publiquen sota:
Creative Commons CC0 (domini públic)
https://creativecommons.org/publicdomain/zero/1.0/
El codi font creat per Zenva inclòs als fitxers del projecte d'aquest curs es publica sota la llicència MIT (https://opensource.org/license/MIT).
Les biblioteques i dependències de tercers romanen sota les seves respectives llicències.
Que construirem
En aquest projecte, crearem un joc de plataformes funcional amb diverses funcions clau. Al final d'aquest llibre, sabràs com:
- Configura el moviment del jugador: Controla un personatge mitjançant les entrades del teclat, com ara moure's amb les tecles de fletxa i saltar amb la barra espaiadora.
- Crea una IA enemiga: Implementa enemics que es mouen cap endavant i cap enrere entre posicions, i requereixen que el jugador els eviti.
- Implantar un sistema sanitari: Dóna al jugador un nombre limitat de cors que representen la seva salut.
- Dissenyar un sistema de recollida de monedes: Escampeu monedes per tot el nivell que augmenten la puntuació del jugador quan es recullen.
- Gestioneu les dades persistents: transfereix informació, com ara la puntuació del jugador, entre diferents escenes.
- Configura banderes finals: Crea un objectiu que teletransporti el jugador al següent nivell en contactar.
Estructura de la guia
Aquí teniu una visió general dels temes inclosos en aquesta guia:
- Inicialització del projecte: Visió general del disseny del joc, importació d'actius i creació de la primera escena.
- Construint el món amb mapes de mosaic: pintar rajoles, afegir col·lisions i configurar el fons.
- Creació de l'escena del jugador: Construcció del CharacterBody2D, forma de col·lisió, càmera i mapa d'entrada.
- Implementació del moviment del jugador: Gravetat, salts, acceleració suau i volteig de sprites.
- Sistema d'animació del jugador: animacions inactius, moure i saltar amb AnimationPlayer.
- Creant enemics: patrulla d'enemics basats en Area2D, detecció de senyals i grups.
- Mecànica bàsica: salut i puntuació: danys, finalització del joc, monedes col·leccionables i persistència de càrrega automàtica.
- Lògica de finalització de nivell: organització de l'escena i la transició EndFlag.
- Construcció de la interfície d'usuari: CanvasLayer HUD amb cors i una etiqueta de puntuació.
- Efectes visuals i àudio: Flaix danyat, sacsejada de la pantalla, fons de paral·laxi i efectes de so.
- Disseny de nivells i reptes finals: Creació del nivell 2 i correcció d'alguns errors.
- Creació del menú principal: botons de reproducció i sortida, ancoratges i el bucle complet del joc.
Instal·lació dels versos de Godot
Aquest llibre fa servir Godot versió 4.6, l'últim llançament estable en el moment d'escriure.
És important utilitzar aquesta versió específica per assegurar-se que el codi i els fitxers de projecte proporcionats funcionen com s'esperava. Les versions més noves poden introduir canvis que podrien trencar la compatibilitat amb les instruccions d'aquesta guia.
Com instal·lar la versio 4.6
Podeu descarregar la versió 4.6 del motor visitant l'arxiu oficial de descàrregues de Godot (https://godotengine.org/download/archive/4.6-stable/).
En aquesta pàgina, seleccioneu l'opció Estàndard que coincideixi amb el vostre sistema operatiu. Tingueu en compte que les instruccions proporcionades en aquest llibre electrònic només estan pensades per a ordinadors de sobretaula.

Un cop descarregat el fitxer, descomprimiu-lo a la ubicació que vulgueu. Godot no requereix un procés d'instal·lació tradicional; simplement feu doble clic al fitxer de l'aplicació per iniciar el motor.
Inicialitzacio del projecte
En aquest capítol, establirem les bases del nostre joc de plataformes 2D. Abans d'escriure qualsevol codi o col·locar nodes, és fonamental tenir una visió clara del joc que estem construint. Començarem revisant el disseny i la mecànica del joc per entendre l'abast del projecte.
Un cop tinguem un pla clar, procedirem a engegar el projecte Godot. Això implica importar els recursos artístics i d'àudio necessaris, organitzar l'estructura del fitxer i crear l'escena inicial per al nostre primer nivell.
Visio general del disseny del joc
El nostre objectiu és crear un joc de plataformes en 2D clàssic on el jugador navegui pels nivells, reculli monedes, eviti enemics i assoleixi un objectiu final. Entendre aquests components ara ens ajudarà a estructurar els nostres nodes i scripts de manera eficaç en capítols posteriors.
Controls i moviment del jugador
El personatge del jugador serà el punt focal del joc. Implementarem controls de teclat per gestionar el moviment:
- Moviment: Les tecles de fletxa (o WASD) controlaran el moviment esquerre i dret.
- Saltant: La barra espaiadora activarà un salt.
Per assegurar-nos que el joc se sent responsiu i polit, no utilitzarem moviments bàsics i rígids. En lloc d'això, implementarem la mecànica basada en la física:
- Acceleració: El jugador augmentarà gradualment la velocitat quan es mou.
- Fricció: El jugador s'aturarà sense problemes quan es solten les tecles.
Aquestes addicions creen una sensació de pes i impuls, fent que l'experiència de plataformes sigui molt més satisfactòria que un simple moviment de píxels perfecte.

Enemics i sistema de dany
Per afegir desafiaments, el joc comptarà amb enemics que patrullen zones específiques. Dissenyarem un sistema on els enemics es moguin d'anada i tornada entre dos punts definits, requerint que el jugador cronometra els seus moviments amb cura.

La interacció amb els enemics activarà un sistema de danys. Quan el jugador xoca amb un enemic:
- La salut del jugador disminuirà en un.
- La interfície d'usuari s'actualitzarà per eliminar un cor.
- La pantalla parpellejarà en vermell i tremolarà per proporcionar comentaris visuals.
- Es reproduirà un efecte de so.
Aquest bucle de retroalimentació multisensorial garanteix que el jugador entengui clarament quan ha patit danys.

Monedes i col·leccionables
L'exploració es recompensa amb objectes de col·lecció. Col·locarem monedes al llarg dels nivells que augmenten la puntuació del jugador quan es recullin. Perquè destaquin, afegirem un poliment visual:
- Rotació: Simularem un efecte de gir alterant l'amplada de la moneda mitjançant una ona sinusoïdal.
- Bobing: La moneda flotarà amunt i avall suaument.
- Àudio: Un so satisfactori es reproduirà en la recollida.

Progressio de nivells
L'objectiu de cada etapa és arribar a la bandera final. Aquest objecte actuarà com a portal; quan el jugador entri a la seva àrea, el joc carregarà automàticament l'escena del següent nivell.

Menú principal
Finalment, crearem una interfície d'usuari senzilla per gestionar l'estat del joc. Un menú principal saludarà el jugador amb opcions per iniciar el joc o sortir de l'aplicació.

Tenint en compte aquest disseny, ara podem passar a Godot i començar a configurar el nostre projecte.
Configuracio del projecte
En aquesta secció, inicialitzarem el projecte, importarem els nostres actius i organitzarem el sistema de fitxers. Una organització adequada des del principi fa que sigui molt més fàcil gestionar un projecte de joc en creixement, ja que afegim escenes, guions i recursos en capítols posteriors.
Importacio dels recursos
El nostre joc es basa en fitxers d'art i de so prefabricats, de manera que el primer pas és introduir-los al motor. Si encara no ho heu fet, descarregueu el fitxer zip de l'actiu
previst en el Introducció capítol. Extraieu el contingut del fitxer zip a una ubicació convenient de l'ordinador.
Hauríeu de veure dues carpetes:
- Àudio: conté efectes de so per a esdeveniments de joc.
- Sprites: conté subcarpetes per a fons, personatges, objectes i fitxes.

Per importar-los a Godot, simplement arrossegueu i deixeu anar les carpetes d'àudio i sprites del vostre explorador de fitxers directament a Sistema de fitxers moll dins de l'editor Godot.

Godot importarà automàticament els fitxers. Preneu-vos un moment per verificar que les carpetes apareixen al moll del sistema de fitxers i continguin els actius esperats.

Creacio de carpetes del projecte
Per mantenir el nostre projecte organitzat, hauríem de separar els nostres fitxers de lògica i d'escena dels nostres actius en brut. Crearem dues carpetes noves:
- Scripts: per a tots els fitxers de codi GDScript.
- Escenes: per a escenes de joc com els nivells, el jugador i els menús.
Per crear-los, feu clic amb el botó dret a la carpeta arrel (res://) al fitxer Sistema de fitxers acobla i selecciona Carpeta nova.

Introduïu el nom Scripts i feu clic D'acord, després repetiu aquest procés per crear una carpeta anomenada Escenes.

El vostre sistema de fitxers hauria de semblar ara a la imatge següent, amb quatre carpetes diferents.

Configuració de la primera escena
Amb les nostres carpetes al seu lloc, és hora de crear l'escena que mantindrà el nostre primer nivell. Una escena a Godot és l'element bàsic per organitzar el vostre joc, de manera que entendre com funcionen les escenes i els nodes us ajudarà durant la resta del llibre.
Nota de Godot: l'arbre de l'escena i els nodes
A Godot, cada objecte del joc és un "node" i els nodes s'organitzen en una jerarquia d'arbre. Una escena és simplement una branca guardada d'aquest arbre que es pot reutilitzar. El Node2D que acabem de crear és el tipus base per a tots els objectes 2D. Proporciona una posició, rotació i escala en l'espai 2D, i qualsevol node secundari hereta la seva transformació.
Això vol dir que quan moveu un node pare, tots els seus fills es mouen amb ell. Aquesta relació pare-fill és la base per construir objectes de joc complexos a partir de parts simples.
Més informació: Node2D (https://docs.godotengine.org/en/4.6/classes/class_node2d.html)
En el Escena acoblar (cantonada superior esquerra), feu clic Escena 2D. Això crea una nova escena amb Node2D com a arrel.

Canvieu el nom del node arrel a Principal fent-hi doble clic. Això ens ajuda a identificar el propòsit de l'escena.

Finalment, salvem l'escena. Premeu Ctrl + S (o Cmd + S a macOS) o aneu a Escena > Desa l'escena. Navegueu a la carpeta Scenes que acabeu de crear i poseu un nom al fitxer level_1.tscn.

Ara podeu veure la vostra escena desada dins de la carpeta Escenes al moll del sistema de fitxers.

Amb la nostra estructura de projecte al seu lloc i la nostra escena de primer nivell creada, estem preparats per començar a construir el món del joc. En el següent capítol, començarem a construir l'entorn per al nivell 1 mitjançant TileMaps i fons.
Construccio del mon amb TileMaps
En aquest capítol, ens centrarem a crear l'entorn per al nostre nivell de joc. Un joc de plataformes necessita un terreny on el jugador es mantingui i obstacles per navegar. Per aconseguir-ho de manera eficient, utilitzarem a TileMap.
Un TileMap és essencialment una quadrícula on podeu "pintar" fitxes individuals (com ara herba, terra o pedra), que us permet dissenyar dissenys de nivell complexos ràpidament sense col·locar centenars de nodes de sprite individuals. També afegirem un fons per donar profunditat i ambient a la nostra escena.
Creació d'un TileMapLayer
Per començar, necessitem un node que gestioni la graella de rajoles. A Godot 4, el node recomanat per a això és el TileMapLayer.
Nota de Godot: TileMapLayer vs TileMap
Godot 4 va introduir TileMapLayer com a reemplaçament del node TileMap més antic. Cada TileMapLayer representa una única capa de fitxes, cosa que facilita la gestió de múltiples capes superposades (per exemple, una capa de terra i una capa de decoració). El node TileMap més antic gestionava totes les capes internament, cosa que era menys flexible.
Si veieu tutorials que fan referència a TileMap, és probable que facin servir un flux de treball anterior. Per a projectes nous, utilitzeu sempre TileMapLayer.
Més informació: TileMapLayer (https://docs.godotengine.org/en/4.6/classes/class_tilemaplayer.html)
Comencem afegint aquest node a la nostra escena. En el Escena dock, assegureu-vos que el vostre node principal estigui seleccionat i feu clic a + botó (o premeu Ctrl + A) per afegir un nou node fill.

A la finestra de creació, cerqueu i seleccioneu TileMapLayeri, a continuació, feu clic Crear.
Nota: És possible que vegeu un node simplement anomenat TileMap. Això es considera obsolet a les versions més noves de Godot a favor de TileMapLayer, que ofereix un millor rendiment i flexibilitat. Assegureu-vos de seleccionar TileMapLayer.

Configuració del TileSet
Un TileMapLayer necessita un TileSet recurs per funcionar. El TileSet defineix quines imatges estan disponibles per pintar i com es comporten (per exemple, la seva mida i propietats de col·lisió). Abans de poder pintar qualsevol fitxa, hem de crear aquest recurs i omplir-lo amb el nostre full de sprites.
Crearem aquest recurs al nostre sistema de fitxers per mantenir-lo organitzat. En el Sistema de fitxers dock, navegueu a la carpeta Sprites i, a continuació, obriu la carpeta Tiles. Feu clic amb el botó dret a la carpeta Tiles i seleccioneu Crea nou > Recurs.

A la finestra de creació de recursos, cerqueu TileSet i seleccioneu-lo.

Anomena el fitxer new_tile_set.tres (o un nom que escolliu) i deseu-lo a la carpeta Tiles.

Ara hem d'afegir el nostre full de sprites a aquest TileSet perquè sàpiga quines imatges utilitzar. Feu doble clic al vostre nou fitxer new_tile_set.tres per obrir el fitxer TileSet panell de l'editor a la part inferior de la pantalla. A continuació, arrossegueu el fitxer tiles_packed.png del FileSystem al fitxer Fonts de rajoles secció del panell.

Godot detectarà que esteu important una textura i us preguntarà si voleu crear fitxes automàticament. Feu clic Sí per confirmar. Això tallarà automàticament la imatge en rajoles en funció de la configuració predeterminada.

Alternativament, podeu crear un TileSet directament a l'Inspector seleccionant el node TileMapLayer, fent clic al menú desplegable al costat de Conjunt de rajoles propietat, seleccionant Nou TileSet, i després desar-lo. Tanmateix, crear-lo primer com a fitxer de recursos diferent sovint és més net per a l'organització del projecte.

Ajust de les dimensions dels tiles
Per defecte, Godot suposa que les fitxes són de 16 x 16 píxels. Tanmateix, els actius que estem utilitzant per a aquest projecte són lleugerament més grans (18 x 18 píxels) per tenir en compte l'embalatge o l'estil. Si no ho ajustem, les nostres rajoles es veuran tallades o desalineades.
En el TileSet panell (a la part inferior de la pantalla), localitzeu el Mida de la regió de textura establiment en el Configuració pestanya. Hem de canviar tant l'amplada com l'alçada de 16 a 18.

També hem de dir al mateix node TileMapLayer que utilitzi aquesta mida de quadrícula. Seleccioneu el TileMapLayer node al moll d'escena. En el Inspector, sota el Conjunt de rajoles propietats dels recursos, assegureu-vos que Mida de rajoles també s'estableix en 18 x 18 píxels.

Afegir propietats de col·lisio
Actualment, les nostres rajoles són només imatges visuals. Si col·loquéssim un jugador sobre ells, el jugador cauria directament. Hem d'afegir formes de col·lisió a les fitxes que haurien d'actuar com a sòl sòlid, de manera que el nostre jugador pugui estar a les plataformes i interactuar amb l'entorn.
- En el TileSet panell a la part inferior, canvieu a Pintar pestanya.
- Hem de seleccionar una propietat per pintar. Feu clic a Seleccioneu un editor de propietats desplegable.
- Amplia el Física agrupar i seleccionar Capa de Física 0.
Nota: Si no veieu "Capa de física 0", és possible que primer hàgiu d'afegir una capa de física al vostre recurs TileSet. Podeu fer-ho a l'Inspector seleccionant el recurs TileSet i afegint un element a la matriu "Capes de física".

Amb la capa de física seleccionada, ara podeu pintar formes de col·lisió a les vostres fitxes:
- Premeu F (o seleccioneu l'eina rectangle) per pintar un quadre de col·lisió quadrat complet.
- Feu clic a les fitxes de la vista de l'atles (la quadrícula d'imatges) que representen un sòl sòlid (p. ex., herba, terra, pedra).
- Veureu una superposició ressaltada a les fitxes que tenen la col·lisió activada.
Si cometeu un error, podeu eliminar la col·lisió d'una fitxa. Feu clic al menú de tres punts de la barra d'eines de pintura, seleccioneu Clari, a continuació, feu clic a la fitxa que voleu arreglar. Per reprendre la pintura, utilitzeu Restableix la forma predeterminada de la rajola opció del mateix menú.

Nota de Godot: capes de física i col·lisió
Godot utilitza un sistema de col·lisió basat en capes. Cada cos i àrea física poden pertànyer a una o més "capes" i poden escanejar una o més "màscares". Una col·lisió només es produeix quan la capa d'un objecte se solapa amb la màscara d'un altre objecte. Aquest sistema us permet crear regles complexes, com ara permetre que el jugador passi a través de monedes però xoqui amb les parets, sense escriure codi addicional.
La capa de física 0 que acabem de pintar a les nostres rajoles és la capa predeterminada. Més tard, quan afegim un jugador amb una CollisionShape2D, detectarà automàticament aquestes fitxes com a sòl sòlid.
Més informació: Introducció a la física (https://docs.godotengine.org/en/4.6/tutorials/physics/physics_introduction
.html)
Pintar tiles a l'escena
Amb el nostre TileSet configurat i les propietats de col·lisió al seu lloc, ara podem pintar la geometria del nivell real. Aquí és on el nivell comença a prendre forma visualment.
Seleccioneu el TileMapLayer node al moll d'escena.

A l'Inspector, assegureu-vos que el vostre new_tile_set.tres estigui assignat a Conjunt de rajoles propietat. Si no és així, arrossegueu el fitxer des del sistema de fitxers a la ranura de propietat.

Ara, mira el TileMap panell a la part inferior de l'editor. Aquesta és la vostra paleta per col·locar fitxes a l'escena.
- Seleccioneu una fitxa de la graella de la dreta.
- Feu clic a la vista d'escena per pintar aquella rajola.
- Feu clic amb el botó dret (o utilitzeu l'eina Goma d'esborrar, drecera E) per eliminar fitxes.
- Utilitza el Eina rectangle (drecera R) per pintar grans blocs de terra ràpidament.
- Utilitza el Eina de línia (drecera L) per dibuixar pendents o plataformes llargues.

Corregir sprites borrosos
Mentre pinteu, és possible que noteu que el vostre pixel art sembla borrós o "taca". Això passa perquè Godot utilitza per defecte el filtratge de textures lineal, que és ideal per a textures 3D d'alta resolució, però dolent per a l'art de píxels nítids. Arreglem-ho perquè les nostres rajoles es mostrin amb vores afilades.

Per solucionar-ho globalment per al nostre projecte:
- Vés a Projecte > Configuració del projecte a la barra de menú superior.

- A la finestra de configuració, aneu a Renderització > Textures.
- Troba el Filtre de textura predeterminat configuració.
- Canvia-ho de Lineal a Més propera.
Això garanteix que totes les textures del joc es representin amb vores nítides i perfectes amb píxels.

Dissenya el teu nivell
Ara que totes les eines estan al seu lloc, és hora de crear el vostre disseny de primer nivell. Utilitzeu les eines de pintura que hem configurat per crear plataformes, buits per saltar i parets. No hi ha cap manera correcta o incorrecta de fer-ho. Experimenta amb diferents dissenys i diverteix-te!
Per al nostre exemple, hem creat un terreny senzill amb diferents alçades i plataformes, com es mostra a continuació.

Configuració de fons
Per completar la configuració visual, afegim una imatge de fons perquè el nostre nivell no surti en un buit gris.
- En el Sistema de fitxers dock, navegueu a Sprites > Fons.
- Cerqueu el fitxer backgroundForest.png (o similar).
- Arrossegueu-lo i deixeu-lo anar directament a la vista d'escena.

Això crearà un nou node Sprite2D. Al moll Scene, canvieu el nom d'aquest node a BackgroundSprite per mantenir les coses organitzades.
Hem d'assegurar-nos que el fons es col·loca correctament. És possible que hàgiu de moure'l a la jerarquia o ajustar el seu índex Z a l'Inspector si cobreix les vostres fitxes. En general, mantenir-lo com a primer fill de Main (a sobre de TileMapLayer a la llista) garanteix que es renderitza darrere de les fitxes.

Corregir errors habituals del TileSet
Durant el desenvolupament, podeu trobar errors al depurador relacionats amb l'atles del conjunt de fitxes, específicament advertències sobre les fitxes que estan "fora de la textura". Aquesta secció mostra com resoldre'ls ràpidament.

Per corregir aquests errors, obriu el vostre recurs TileSet (feu doble clic al fitxer .tres o utilitzeu el tauler inferior). Busqueu qualsevol senyal d'advertència d'exclamació groc al Fonts de rajoles llista.

Feu clic als tres punts al costat de l'avís i seleccioneu Traieu les rajoles fora de la textura. També es recomana seleccionar Elimina les rajoles a les regions de textura totalment transparents per netejar l'atles.

Felicitats! Has configurat el món amb èxit per al teu joc. Ara tenim un mapa de rajoles sòlid amb col·lisions i un fons escènic. En el següent capítol, donarem vida a aquest món creant el nostre personatge de jugador i implementant controls de moviment.
Creacio de l'escena del jugador
En aquest capítol, configurarem el personatge del jugador, el principal element interactiu del vostre joc. Al final, tindreu una escena del personatge del jugador que està preparada per moure's i saltar dins d'un entorn d'estil de plataformes, juntament amb les configuracions d'entrada necessàries per controlar-la.
Entendre l'escena del jugador
L'escena del jugador és un component crucial del vostre joc, ja que defineix l'objecte físic que controlarà el jugador. Aquesta escena inclourà diversos nodes que determinen l'aparença del jugador, la detecció de col·lisions i altres propietats.
Per començar, hem de determinar el node arrel del nostre reproductor. Per als jocs de plataformes (i els personatges en general), Godot proporciona un node especialitzat anomenat CharacterBody2D. Aquest node és molt útil perquè inclou funcions integrades com ara la detecció de col·lisions, la detecció de terra i la modificació de la velocitat.
Nota de Godot: CharacterBody2D
CharacterBody2D és un cos de física especialitzat dissenyat per a personatges que es mouen mitjançant script. A diferència del RigidBody2D, no reacciona a les forces físiques (com la gravetat) automàticament; cal programar-ne el moviment. Això us proporciona un control precís sobre el comportament del personatge, que és essencial per als jocs de plataformes.
Més informació: CharacterBody2D (https://docs.godotengine.org/en/4.6/classes/class_characterbody2d.html)
Creació del node Player
Ara fem el node arrel del nostre jugador. Al moll de l'escena, creeu un node nou, cerqueu CharacterBody2D i creeu-lo.

Canvieu el nom del node a "Jugador".

És una bona pràctica desar el reproductor com a fitxer d'escena independent. Això us permet crear una instancia del jugador en diversos nivells sense recrear-lo.
Feu clic amb el botó dret al node i seleccioneu Desa la branca com a escena, o simplement arrossegueu el node a la vostra carpeta Escenes al moll FileSystem.

Deseu el fitxer com a player.tscn dins de la vostra carpeta Scenes.

Afegir elements visuals
Actualment, el nostre jugador és invisible. Hem d'afegir un sprite perquè el personatge tingui una representació visual al món del joc.
Obriu la vostra nova escena player.tscn (si encara no està oberta). A continuació, arrossegueu un sprite des de la vostra carpeta de sprites (p. ex., character_0000.png) al node Reproductor al moll de l'Escena, o directament a la finestra de visualització de l'editor mentre el node Reproductor està seleccionat.

Canvieu el nom del nou node de sprite a "Sprite" per a una referència fàcil als nostres scripts més endavant.

Hem d'assegurar-nos que el sprite es col·loca correctament en relació al node arrel. En el Inspector, estableix el Posició a (0, 0).
A més, sovint és útil alinear els peus del personatge amb l'origen del node arrel. Això fa que el posicionament del jugador a terra sigui molt més fàcil. A les propietats del sprite, trobeu el Desplaçament secció i establiu el Y valor a un nombre negatiu, com ara -12.

Definici de la fisica
Perquè el jugador xoqui amb plataformes i altres objectes del món, hem de definir la seva forma física. Feu clic amb el botó dret al node Reproductor, seleccioneu Afegeix un node filli trieu CollisionShape2D.

A l'Inspector, localitzeu el Forma propietat. Feu clic al menú desplegable (que actualment diu <buit>) i seleccioneu Nova CapsuleShape2D. Una forma de càpsula és ideal per als personatges perquè la seva part inferior arrodonida permet un moviment suau sobre petits cops o buits al terra.

A la finestra gràfica, veureu la superposició de la forma de col·lisió. Utilitzem les nanses de mida per ajustar la forma de manera que s'ajusti a les dimensions del sprite. Volem que la part inferior de la càpsula toqui la base dels peus del personatge (el punt d'origen).

Configuració de la camera
Si juguéssim ara, el jugador podria sortir de la pantalla i desaparèixer. Per mantenir la finestra centrada en el personatge, necessitem una càmera que segueixi el jugador.
Afegiu un node Camera2D com a fill del node Player. Com que és fill del jugador, es mourà automàticament sempre que el jugador es mogui.

Tanmateix, com que el nostre pixel art és petit, la vista de la càmera predeterminada estarà massa lluny. Ajusteu el Zoom propietat a l'inspector a (3, 3). Això augmentarà la càmera un 300%, mantenint l'enfocament ajustat al personatge.

També és possible que vulgueu col·locar la càmera lleugerament per sobre del reproductor (valor Y negatiu) perquè el jugador pugui veure més l'entorn que hi ha davant i per sobre.

Nota de Godot: Camera2D i seguiment de la vista
Quan un node Camera2D és la càmera activa (la propietat Enabled està marcada), Godot ajusta automàticament la finestra gràfica perquè la posició de la càmera estigui al centre de la pantalla. En fer que la càmera sigui una filla del jugador, la seva posició s'actualitza automàticament cada fotograma sense cap script.
La propietat Zoom funciona com a multiplicador: un valor de (3, 3) significa que cada píxel del món del joc ocupa tres píxels de pantalla, la qual cosa és ideal per a petits sprites d'art de píxels que, d'altra manera, semblarien petits.
Més informació: Càmera 2D (https://docs.godotengine.org/en/4.6/classes/class_camera2d.html)
Afegir nodes d'utilitat
Per preparar futurs capítols on animarem i afegirem so al reproductor, ara afegirem dos nodes més a l'escena del reproductor.
Primer, afegiu un node AnimationPlayer. Això gestionarà les animacions de moviment del personatge (inactiu, córrer, saltar).

A continuació, afegiu un node AudioStreamPlayer. Això gestionarà els efectes de so del personatge, com ara saltar o recollir monedes.

La vostra escena de jugador ara hauria de tenir cinc nodes secundaris en total, tal com es mostra a continuació.

Configuració del mapa d'entrada
Abans de submergir-nos en el guió del moviment, els salts i la física del nostre jugador, hem d'establir les nostres entrades. Les entrades defineixen quins botons del vostre teclat, ratolí o controlador realitzen accions específiques al joc.
En lloc d'escoltar tecles específiques com la "fletxa esquerra" o la "barra espaiadora" directament al nostre codi, escoltarem accions abstractes com move_left o jump. Aquest enfocament fa que el nostre script sigui més flexible i més fàcil de gestionar.
Per que fer servir accions d'entrada?
L'ús del mapa d'entrada ofereix diversos avantatges:
- Abstracció: Separa el mètode d'entrada de la lògica de l'script.
- Flexibilitat: podeu assignar diverses tecles (p. ex., tecles de fletxa i WASD) per activar la mateixa acció.
- Mantenibilitat: podeu canviar els controls (o afegir suport de gamepad) sense reescriure el vostre codi.
Nota de Godot: el mapa d'entrada
El mapa d'entrada de Godot és un mapeig a nivell de projecte entre noms d'accions llegibles pels humans (com move_left) i entrades físiques (com la tecla de fletxa esquerra o
un llapis de joc). En els vostres scripts, només feu referència al nom de l'acció, mai a la clau en brut. Això vol dir que podeu reasignar els controls, afegir compatibilitat amb el gamepad o fins i tot admetre dispositius d'accessibilitat sense tocar ni una sola línia de codi de joc.
Més informació: Mapa d'entrada (https://docs.godotengine.org/en/4.6/classes/class_inputmap.html)
Configuració d'accions
Per accedir al mapa d'entrada, aneu a Projecte al menú superior i seleccioneu Configuració del projecte.

A la finestra de configuració del projecte, feu clic a Mapa d'entrada pestanya.

Ara podem crear les nostres accions personalitzades. En el Afegeix una acció nova camp, escriviu un nom per a la vostra acció (p. ex., move_left) i premeu Intro (o feu clic a Afegeix).

Repetiu això per a les altres accions que necessitem. Creeu accions anomenades move_left, move_right i jump.

Assignacio de tecles
Ara hem d'assignar claus físiques a aquestes accions abstractes perquè el jugador les pugui utilitzar. A la fila de move_left, feu clic a + icona (més) al costat dret.

Això obre la finestra de configuració de l'esdeveniment. Podeu cercar una tecla manualment o simplement prémer la tecla del teclat mentre la finestra està oberta.

Premeu el botó Fletxa esquerra tecla i, a continuació, feu clic D'acord per confirmar l'encàrrec.

Repetiu aquest procés per afegir claus alternatives (com ara WASD) i configurar les altres accions. Una configuració habitual per als jocs de plataformes és:
- moure_esquerra: Fletxa esquerra, A
- moure_dreta: Fletxa dreta, D
- saltar: Fletxa amunt, barra espaiadora, W

Amb la nostra escena de jugador construïda i el nostre mapa d'entrada configurat, hem establert les bases per a la interactivitat. En el següent capítol, crearem el guió del jugador i donarem vida al nostre personatge amb la lògica de moviment.
Implementació del moviment del jugador
En el capítol anterior, vam crear la nostra escena de jugador i vam configurar el mapa d'entrada. Tanmateix, el nostre personatge actualment és només una imatge estàtica amb forma de col·lisió; no pot moure's, saltar o interactuar amb el món.
En aquest capítol, donarem vida al nostre personatge escrivint el guió del jugador. Implementarem el moviment horitzontal, aplicarem la gravetat, afegirem mecàniques de salt i suavitzarem els controls per donar una sensació polida al joc. Finalment, ens assegurarem que el personatge s'enfronti visualment a la direcció del moviment.
Configuracio de l'script del jugador
Per començar, hem d'adjuntar un script al nostre node de reproductor. Aquest script contindrà tota la lògica per llegir les entrades i aplicar forces físiques.
Obriu la vostra escena player.tscn i seleccioneu el node arrel CharacterBody2D (anomenat Player).

Amb el node seleccionat, feu clic a Nou guió icona a la part inferior de l'Inspector (el desplaçament amb un signe més verd). Alternativament, podeu fer clic amb el botó dret al node i seleccionar-lo Adjunta script.

Al diàleg de creació de l'script, assegureu-vos que l'idioma està configurat en GDScript i que l'script hereta de CharacterBody2D. Establiu el camí a res://Scripts/player.gd per mantenir el nostre projecte organitzat.

Feu clic Crear per generar el fitxer i obrir l'editor d'scripts.
Definici de variables de moviment
El nostre guió de jugador necessita diverses variables per controlar com es mou el personatge. En definir-los a la part superior del nostre guió, podem modificar fàcilment la "sensació" del joc més tard sense reescriure la lògica.
Utilitzarem la paraula clau @export per a aquestes variables. Això els exposa a l'Inspector, cosa que ens permet ajustar valors com la velocitat i l'alçada de salt directament a l'editor mentre el joc s'executa.
Substituïm el contingut de l'script predeterminat per la configuració següent:
extends
CharacterBody2D@export
var
move_speed : float = 100 @export
var
acceleration : float = 50 @export
var
braking : float = 20 @export
var
gravity : float = 500 @export
var
jump_force : float = 200var
move_input : floatAixò és el que controla cada variable:
- move_speed: velocitat màxima que pot assolir el jugador.
- acceleració: la rapidesa amb què el jugador arriba a la velocitat màxima.
- frenada: amb quina rapidesa s'alenteix el reproductor quan s'atura l'entrada.
- gravetat: la força cap avall aplicada quan el jugador està a l'aire.
- jump_force: la velocitat ascendent aplicada quan el jugador salta.
- move_input: una variable per emmagatzemar la direcció d'entrada actual (esquerra o dreta).
Implementació bàsica del moviment
Godot proporciona una funció integrada anomenada _physics_process per gestionar càlculs físics. A diferència de _process, que executa cada fotograma (temps variable), _physics_process s'executa a una velocitat fixa (per defecte 60 vegades per segon).
Això garanteix que el moviment i les col·lisions siguin constants independentment de la velocitat de fotogrames de l'ordinador.
Nota de Godot: _procés_físic vs _procés
Godot ofereix dues devolucions de trucada per fotograma. _process(delta) s'executa una vegada per fotograma representat, de manera que la seva velocitat varia amb el rendiment de l'ordinador.
_physics_process(delta) runs at a fixed interval (60 times per second by default), making it ideal for movement, collision detection, and anything that relies on consistent timing.
Com a regla general, poseu la lògica relacionada amb la física (velocitat, forces, difusió de raigs)
_processa_física i posa una lògica només visual (animacions, actualitzacions d'interfície d'usuari, efectes de càmera) al _procés.
Més informació: processament inactiu i físic (https://docs.godotengine.org/en/4.6/tutorials/scripting/idle_and_physics_ processing.html)
Començarem implementant el moviment bàsic esquerra i dreta amb el codi següent:
func
_physics_process(delta):move_input = Input.get_axis("move_left", "move_right") velocity.x = move_input * move_speed
move_and_slide()
Anem a trencar aquesta lògica:
- Tractament d'entrada: Input.get_axis("move_left", "move_right") comprova les dues accions que hem definit al mapa d'entrada. Retorna -1 si es prem "move_left", 1 si es prem "move_right" i 0 si no es prem cap (o tots dos).
- Assignació de velocitat: establim la velocitat horitzontal (velocity.x) multiplicant la direcció d'entrada pel nostre move_speed.
- Aplicació del moviment: move_and_slide() és una funció especial per a CharacterBody2D. Mou el cos en funció de la seva propietat de velocitat actual i gestiona automàticament les col·lisions amb parets i sòls.
Aplicacio de la gravetat
Si executeu el joc ara, notareu un problema: el personatge pot moure's cap a l'esquerra i la dreta, però suren a l'aire si surt d'una plataforma.

Per solucionar-ho, hem d'aplicar la gravetat a la velocitat vertical (velocity.y) sempre que el jugador no estigui a terra.
Actualitzeu la vostra funció _physics_process per incloure la gravetat:
func
_physics_process(delta):# Apply gravity only when airborneif not
is_on_floor():velocity.y += gravity * delta
move_input = Input.get_axis("move_left", "move_right") velocity.x = move_input * move_speed
move_and_slide()
La funció is_on_floor() és un mètode integrat que retorna true si el caràcter es troba en un objecte de col·lisió. Si torna fals, afegim el nostre valor de gravetat (multiplicat per delta per a la consistència del temps) a la velocitat vertical, fent que el jugador s'acceleri cap avall.
Ara, si proveu el joc, el vostre personatge hauria de caure si no es troba en una de les fitxes del sòl.

Implementació del salt
Un joc de plataformes no està complet sense la capacitat de saltar. Hem de comprovar l'entrada de salt i assegurar-nos que el jugador pot saltar (és a dir, que està a terra).
Afegirem aquesta lògica a la nostra funció _physics_process.
- Comproveu l'entrada i l'estat: Utilitzem Input.is_action_pressed("saltar") per veure si el botó està premut i is_on_floor() per assegurar-nos que el jugador no estigui ja a l'aire.
- Aplicar força: En el sistema de coordenades 2D de Godot, "amunt" és la direcció Y negativa. Per saltar, posem la velocitat.y en un valor negatiu
(-força_salt).
Aquí teniu el codi actualitzat:
func
_physics_process(delta):# Apply gravity only when airborneif not
is_on_floor():velocity.y += gravity * delta
move_input = Input.get_axis("move_left", "move_right") velocity.x = move_input * move_speed
# Only allow jumping while groundedif
Input.is_action_pressed("jump")
and
is_on_floor(): velocity.y = -jump_forcemove_and_slide()
El teu personatge ara pot saltar per sobre de buits i obstacles.

Suavitzar el moviment
Actualment, el nostre moviment és molt rígid. El personatge arriba a tota velocitat a l'instant quan es prem una tecla i s'atura a l'instant quan es deixa anar. Per fer que el moviment se senti més natural i pesat, podem utilitzar Interpolació lineal (sovint anomenat lerp).
Lerp ens permet fer una transició suau d'un valor d'un punt inicial a un punt final al llarg del temps. L'utilitzarem per augmentar gradualment la velocitat (acceleració) i disminuir-la (frenada/fricció).
Substituïu l'assignació de velocitat directa.x per la lògica següent:
# Smoothly accelerate toward target speed, or brake to a stopif
move_input != 0:velocity.x = lerp(velocity.x, move_input * move_speed, acceleration * delta)
else:
velocity.x = lerp(velocity.x, 0.0, braking * delta)
Si el jugador està prement una tecla (move_input != 0), lerpem la velocitat actual cap a la velocitat objectiu utilitzant el valor d'acceleració. Si no es prem cap tecla, anem la velocitat cap a 0,0 utilitzant el valor de frenada.
Aquí teniu la funció completa _physics_process amb suavització aplicada:
func
_physics_process(delta):# Apply gravity only when airborneif not
is_on_floor():velocity.y += gravity * delta
move_input = Input.get_axis("move_left", "move_right")
# Smoothly accelerate toward target speed, or brake to a stopif
move_input != 0:velocity.x = lerp(velocity.x, move_input * move_speed, acceleration * delta)
else:
velocity.x = lerp(velocity.x, 0.0, braking * delta)
# Only allow jumping while groundedif
Input.is_action_pressed("jump")
and
is_on_floor(): velocity.y = -jump_forcemove_and_slide()
Nota de Godot: interpolació lineal (lerp)
lerp significa "interpolació lineal". Donat un valor inicial, un valor final i un pes entre 0 i 1, retorna un punt proporcionalment entre ells.
Quan es diu cada fotograma amb delta com a part del pes, el resultat es mou ràpidament al principi i s'alenteix a mesura que s'acosta a l'objectiu, produint l'efecte d'acceleració i frenada suau que acabem d'implementar.
Godot proporciona lerp per a valors generals, lerpf per a flotadors específicament i
Vector2.lerp / Vector3.lerp per a vectors.
Més informació: Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know. (https://docs.godotengine.org/en/4.6/classes/class_@globalscope.html#class
-globalscope-method-lerp)
Visuals: inverteix el sprite
Tenim moviment funcional, però visualment, el personatge sempre mira a la mateixa direcció (normalment a la dreta). Quan es mou a l'esquerra, el personatge sembla que està caminant per la lluna. Hem de capgirar el sprite horitzontalment en funció de la direcció del moviment.
Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know.

Primer, necessitem una referència al node Sprite al nostre script. Let’s add the following line at the top of our script, just below extends CharacterBody2D:
@onready
var
sprite : Sprite2D = $SpriteLa paraula clau @onready assegura que Godot espera fins que el node estigui completament carregat abans d'assignar-lo a la variable.
A continuació, gestionarem les actualitzacions visuals. Mentre que els càlculs físics pertanyen
_procés_físic, sovint es gestionen actualitzacions visuals, com ara el gir de sprites
_procés, que executa cada fotograma.
Ara afegim una funció _process al nostre script:
func
_process(delta):# Flip the sprite to face the direction of movementif
velocity.x != 0:sprite.flip_h = velocity.x > 0
Aquest codi verifica si el jugador es mou (velocity.x != 0). Si ho són, es posa
flip_h en funció de la direcció:
- Si velocity.x > 0 (movint-se cap a la dreta), flip_h esdevé cert.
- Si la velocitat.x < 0 (movint-se a l'esquerra), flip_h esdevé fals.
Nota: Depenent de la direcció original del vostre sprite, potser haureu d'invertir aquesta lògica (p. ex., velocity.x < 0). Si el vostre sprite mira cap a la dreta de manera predeterminada, flip_h hauria de ser false quan es mou a la dreta i true quan es mou a l'esquerra.
Torna a provar el joc. El vostre personatge hauria d'enfrontar-se ara a la direcció en què va.

En aquest capítol, hem creat amb èxit la mecànica de moviment bàsica per al nostre joc de plataformes. El nostre jugador pot córrer, saltar i interactuar amb la gravetat, i els controls són suaus i sensibles. In the next chapter, we will enhance the visuals further by setting up the AnimationPlayer to switch between idle, running, and jumping animations.
Sistema d'animaci del jugador
En aquest capítol, configurarem animacions per al nostre personatge jugador. Les animacions són les que donen vida a un personatge del joc: donen al jugador comentaris visuals per a accions com córrer, quedar-se quiet i saltar. Ens centrarem a crear tres animacions essencials (inactiu, moure i saltar) i després escriure la lògica del guió per canviar entre elles en funció del que està fent el jugador.
Entendre les animacions a Godot
A Godot, una animació és essencialment una línia de temps que comença des de 0 segons i acaba amb una durada especificada. Al llarg d'aquesta línia de temps, podeu afegir fotogrames clau que canvien les propietats del vostre sprite o qualsevol altre node. Per al nostre jugador, animarem la propietat de textura del node Sprite2D per alternar entre diferents imatges de sprite al llarg del temps.
Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know.
AnimationPlayer is one of Godot’s most versatile nodes. Pot animar pràcticament qualsevol propietat de qualsevol node de l'arbre de l'escena, no només textures. Podríeu animar la posició d'un node per crear escenes de tall, el seu color modular per fer una cosa intermitent, o fins i tot un paràmetre d'ombra per als efectes visuals. Cada propietat que animeu té la seva pròpia "pista" a la línia de temps i es poden executar diverses pistes simultàniament dins d'una sola animació.
La pista RESET que habilitem més endavant és una pràctica recomanada: emmagatzema el valor predeterminat de cada propietat animada perquè Godot pugui restaurar els nodes al seu estat original quan s'atura l'animació.
Més informació: Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know. (https://docs.godotengine.org/en/4.6/classes/class_animationplayer.html)
Configuracio de l'AnimationPlayer
Abans de poder crear animacions, necessitem un node per gestionar-les. El node AnimationPlayer s'encarrega d'emmagatzemar i reproduir totes les animacions d'una escena, així que assegurem-nos que el jugador en tingui un.
Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know. Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know. node com a fill de l'arrel del reproductor. Si ho heu afegit al capítol anterior, simplement seleccioneu-lo. Si no, afegiu-ne un ara.

Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know. Animació apareixerà a la part inferior de l'editor. Aquí és on construirem les nostres línies de temps.

Creació d'animació inactiva
L'animació inactiva es reprodueix quan el jugador està parat. És l'estat visual predeterminat que fonamenta el vostre personatge al món, així que primer creem-lo.
A la finestra d'animació, feu clic a Animació botó i seleccioneu Nou.

Anomena l'animació inactiva i fes clic a D'acord.

De manera predeterminada, les animacions estan configurades per durar un segon. Per a una postura inactiva senzilla, volem que sigui més curta. Feu clic al camp de durada a prop de la part superior dreta de la línia de temps (que actualment diu 1,0) i canvieu el valor a 0,5.
També podeu ampliar la línia de temps utilitzant el control lliscant de la part inferior dreta per treballar en increments més petits.

Amb la durada establerta, verifiqueu que la línia de temps reflecteixi la nova durada.

Afegir la pista de textura
Per animar una propietat, hem de crear-hi una "pista". Una pista indica a AnimationPlayer quina propietat ha de canviar i quins valors ha d'utilitzar en cada punt de la línia de temps.
Mantingueu oberta la finestra d'animació i seleccioneu el vostre Sprite node al moll d'escena. En el Inspector, localitza el Textura propietat. Veureu una petita icona de clau al costat. Feu clic a aquesta icona de clau per crear la pista.

Apareixerà un diàleg de confirmació. Assegureu-vos Crea pistes de RESTABLECIMENT està habilitat i feu clic Crear.

Ara hauríeu de veure una pista nova per a la propietat de textura de Sprite a la línia de temps d'animació.

Configuració de fotogrames clau
Quan vam crear la pista, Godot va afegir automàticament un fotograma clau a la posició actual del temps (0,0 segons) utilitzant la textura de sprite actual.

Per a una animació estàtica inactiva (on el personatge només es queda quiet), aquest fotograma clau únic és realment tot el que necessitem. L'animació simplement mantindrà aquesta textura durant tot el temps. Podeu provar-ho mitjançant els controls de reproducció de la finestra d'animació.

Escolteu les pistes de RESET
Quan vam crear la nostra primera pista, vam habilitar el Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know. opció de pista. Aquesta és una animació especial que Godot crea automàticament per ajudar a gestionar les vostres propietats animades.
- PropòsitError 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know.
- Funció: Quan el joc comença o s'atura una animació, Godot pot utilitzar aquesta pista per tornar l'objecte al seu estat original.
- Ubicació: Podeu veure'l seleccionant "RESET" al menú desplegable d'animació.

Creació d'animació en moviment
Amb l'animació inactiva al seu lloc, creem l'animació per quan el personatge estigui en execució. Aquesta animació passarà a través de dos fotogrames alterns per donar l'aparença de moviment.
Feu clic a Animació > Nou de nou.

Anomena aquest moviment d'animació.

Igual que abans, afegiu una pista per al Textura propietat fent clic a la icona de clau a l'Inspector mentre el node Sprite està seleccionat.

Afegir fotogrames de moviment
Per a l'animació de moviment, volem que el personatge alterni entre els sprites "cames amunt" i "cames avall" per simular la carrera. Configurarem dos fotogrames clau, cadascun amb un marc de sprite diferent.
- Primer fotograma clau: seleccioneu el fotograma clau a 0,0 segons. A l'Inspector (a les propietats del fotograma clau), arrossegueu l'esprit on les cames del personatge estiguin cap amunt (p. ex., character_0001.png) al Valor ranura.

- Segon fotograma clau: moveu el cursor de la línia de temps aproximadament a la meitat de l'animació. Feu clic amb el botó dret a la pista i seleccioneu Insereix la clau.

- Assigna texturaError 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know. Valor propietat a l'inspector.

Bucle i temporitzacio
Una animació en execució s'ha de repetir contínuament mentre el jugador es mou, de manera que hem d'habilitar el bucle i augmentar el temps.
Activeu el bucle fent clic a Bucle icona (fletxes que formen un cercle) al final dels controls de la línia de temps.

La durada predeterminada d'1 segon és massa lenta per a un cicle d'execució. Canvieu la durada de l'animació a 0,25 segons per obtenir una sensació més ràpida.

Quan reduïu la durada, el vostre segon fotograma clau pot acabar fora de la línia de temps visible. Amplieu i arrossegueu el segon fotograma clau perquè quedi aproximadament al mig de la nova durada (al voltant de 0,1 o 0,12 segons).

Reprodueix l'animació per assegurar-te que les cames del personatge s'alternen ràpidament.
Creació de l'animació de salt
Finalment, creem l'animació de salt. A diferència de l'animació de moviment, aquesta serà una posició estàtica única que es mostra mentre el personatge està a l'aire, de manera que només necessita un fotograma clau.
- Feu clic Animació > Nou i anomena-li salt.


- Afegiu la pista de textura fent clic a la icona de la clau a la propietat Texture de Sprite.

- Seleccioneu el fotograma clau a 0,0 segons. A l'Inspector, assigneu el sprite amb les cames completament esteses o a l'aire (p. ex., character_0001.png o un marc de salt dedicat si en teniu un).

Ara hauríeu de tenir tres animacions diferents a la vostra llista: inactiu, moure i
saltar.

Programació lògica d'animació
Ja tenim les nostres animacions preparades, però el joc no sap quan jugar a cadascuna. En aquesta secció, actualitzarem el nostre script player.gd perquè l'animació correcta es reprodueixi automàticament en funció del que faci el jugador: dempeus, córrer o saltar.
Obriu l'script player.gd. Primer, necessitem una referència al node AnimationPlayer perquè el nostre codi pugui dir-li quina animació reproduir. Afegiu aquesta variable a la part superior del vostre script, juntament amb les vostres altres variables:
@onready
var
anim : AnimationPlayer = $AnimationPlayerA continuació, creem una funció dedicada per gestionar la selecció d'animacions. Mantenir aquesta lògica en la seva pròpia funció fa que el codi sigui més fàcil de llegir i mantenir. Afegiu la funció següent al vostre script:
# Select the correct animation based on the player's current statefunc
_manage_animation():
if not
is_on_floor(): anim.play("jump")
elif
move_input != 0: anim.play("move")else:
anim.play("idle")
Aquesta lògica verifica l'estat del jugador en un ordre específic de prioritat:
- Aerotransportat: Si el jugador no està a terra (no is_on_floor()), està saltant o caient. Juguem l'animació del "salt".
- En moviment: Si el jugador està al terra i proporciona entrada (move_input
!= 0), reproduïm l'animació "moveu".
- Inactiu: Si cap de les coses anteriors és certa (el jugador està a terra i no es mou), reproduïm l'animació "inactiva".
Finalment, hem d'anomenar aquesta funció cada fotograma perquè l'animació es mantingui sincronitzada amb les accions del jugador. Actualitzeu la vostra funció _process per incloure la lògica d'inversions de sprite i la crida a _manage_animation():
func
_process(delta):# Flip the sprite to face the direction of movementif
velocity.x != 0:sprite.flip_h = velocity.x > 0
_manage_animation()
Provar el joc
Ara és el moment de veure la nostra feina en acció. Torneu a l'escena del vostre nivell i premeu el botó Jugar botó. Amb les tres animacions connectades, el personatge hauria de respondre visualment a cada canvi d'estat.
Quan el jugador està parat, s'ha de reproduir l'animació inactiva.

Quan camines a l'esquerra o a la dreta, el personatge hauria de canviar a l'animació en curs.

Quan saltes, el personatge hauria de canviar a la postura del salt mentre estàs a l'aire.

Si tot funciona correctament, el vostre personatge de jugador hauria de fer una transició perfecta entre els estats, afegint una capa important de poliment al vostre joc.
En el següent capítol, passarem del personatge del jugador i començarem a implementar la IA enemiga per afegir desafiaments al nostre nivell.
Creacio d'enemics
En un joc de plataformes, els nivells estàtics només poden oferir tants reptes. Perquè el joc sigui més atractiu i dinàmic, hem d'introduir amenaces actives que obliguin el jugador a mantenir-se alerta. En aquest capítol, crearem el nostre primer personatge enemic: un ratpenat que patrulla una zona concreta.
Implementarem un patró de moviment d'anada i tornada i establirem un sistema de detecció perquè l'enemic sàpiga quan ha colpejat el jugador. Tot i que encara no implementarem la lògica de danys completa, establirem les bases essencials detectant col·lisions i identificant el jugador mitjançant el sistema de grups de Godot.
Configuracio de l'escena de l'enemic
Començarem per construir l'escena enemiga. A diferència del Player, que utilitza un CharacterBody2D per al moviment basat en la física (manejar la gravetat i les col·lisions), el nostre enemic utilitzarà un Area2D com a node arrel.
Un Area2D és perfecte per a aquest propòsit perquè detecta solapaments sense bloquejar físicament el moviment. Això permet que el jugador passi "a través" de l'enemic (fer danys en el procés) en lloc de colpejar una paret invisible a l'aire.
Nota de Godot: Area2D vs cossos físics
Godot ofereix tres tipus principals de nodes físics per a 2D, cadascun adequat per a un paper diferent:
CharacterBody2D es mou completament pel vostre codi. És l'opció preferida per als personatges dels jugadors i els NPC on necessiteu un control total sobre el moviment.
RigidBody2D està impulsat pel motor físic. Utilitzeu-lo per a objectes que haurien de respondre automàticament a la gravetat, les col·lisions i les forces (com ara caixes o boles que reboten).
Àrea 2D no participa en absolut de la física. Només detecta solapaments amb altres objectes. Això el fa ideal per a activadors, col·leccionables i enemics que haurien de detectar el jugador sense bloquejar el moviment.
Més informació: Àrea 2D (https://docs.godotengine.org/en/4.6/classes/class_area2d.html)
Creacio dels nodes
Amb el tipus d'escena decidit, anem a construir l'arbre de nodes que constituirà l'enemic. Necessitem un sprite per a la visualització, una forma de col·lisió per a la detecció de superposició i un reproductor d'animació per a l'efecte de bateig.
Primer creem una nova escena i seleccionem Àrea 2D com a node arrel.

Canvieu el nom d'aquest node a Enemy.

A continuació, necessitem una representació visual del nostre enemic. En el Sistema de fitxers dock, navegueu a Sprites > Personatges i trobeu l'esprit ratpenat (p. ex., character_0024.png).

Arrossegueu el ratpenat sprite al node Enemy al moll de l'escena i, a continuació, canvieu el nom del nou node fill a Sprite.

Per assegurar-vos que el sprite estigui alineat correctament amb el node arrel, configureu-lo Posició a (0, 0) a l'Inspector.

Ara hem de definir l'àrea on l'enemic pot detectar el jugador. Per fer-ho, feu clic amb el botó dret al node Enemic i afegiu un node fill del tipus CollisionShape2D.

Aquesta forma determina la regió que desencadena els esdeveniments de superposició. A l'Inspector, configureu el Forma propietat a Nova CircleShape2D.

A la finestra gràfica, ajusteu la mida del cercle perquè s'adapti a l'esprit ratpenat. Sovint és un bon disseny del joc per fer que el hitbox sigui lleugerament més petit que el sprite visual; això dóna al jugador una mica de marge a l'hora d'esquivar i fa que el joc se senti més just.

Finalment, afegiu un Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know. node com a fill del node Enemic. Ho farem servir més endavant per animar les ales del ratpenat.

Ara el vostre arbre d'escenes hauria de semblar a la imatge següent, amb el node arrel Enemy que conté Sprite, CollisionShape2D i AnimationPlayer.

Programacio del moviment de l'enemic
Amb l'escena muntada, ara podem donar vida al ratpenat amb un guió. L'objectiu és fer que l'enemic es mogui d'anada i tornada entre una posició inicial i una posició objectiu, creant un bucle de patrulla senzill que el jugador haurà d'esquivar.
Adjuntem un script nou al node Enemic. Anomeneu-lo enemy.gd i deseu-lo a la vostra carpeta Scripts.

Necessitem unes quantes variables per controlar el moviment. En exportar move_direction, podem establir la ruta de patrulla directament a l'editor per a cada instància enemiga individual, de manera que és fàcil reutilitzar la mateixa escena amb diferents rutes de patrulla.
També necessitem variables per emmagatzemar la velocitat de l'enemic, així com els dos punts de patrulla que farem servir.
Afegiu les variables següents al vostre script:
extends
Area2D@export
var
move_direction : Vector2 @export
var
move_speed : float = 20@onready
var
start_pos : Vector2 = global_position@onready
var
target_pos : Vector2 = global_position + move_directionAra implementem la lògica del moviment en _physics_process. Utilitzarem el mètode move_toward, que mou sense problemes un valor (la nostra posició) cap a un objectiu en una quantitat específica (la nostra velocitat).
Afegiu el codi següent:
func
_physics_process(delta):# Move steadily toward the current target positionglobal_position = global_position.move_toward(target_pos, move_speed
* delta)
# When the enemy arrives, swap the target to create a patrol loopif
global_position == target_pos:if
target_pos == start_pos:target_pos = start_pos + move_direction else:
target_pos = start_pos
Així és com funciona aquesta lògica:
- Mou-te: a cada fotograma, la posició_global de l'enemic es mou
target_pos a la velocitat de moviment definida.
- Comproveu l'arribada: Comprovem si l'enemic ha arribat a l'objectiu (posició_global == target_pos).
- Canvia d'objectiu: Si l'enemic és a l'objectiu, comprovem si aquest objectiu era l'inici o el final del camí, després l'intercanviem al punt oposat, creant un bucle continu.
Provar el moviment
Vegem-ho en acció. Col·loca una instància de la teva escena de l'enemic a l'escena del nivell principal i situa l'enemic on vulguis que comenci.

A l'Inspector, establiu move_direction a (0, -50). Això diu a l'enemic que es mogui 50 píxels cap amunt des de la seva posició inicial i després cap avall.

Premeu Reproduir. Hauríeu de veure el ratpenat volant amunt i avall en bucle.

Detectar el jugador
Ara que l'enemic es mou, ha de ser perillós. Volem detectar quan el jugador toca l'enemic per tal que finalment puguem fer mal. Com que el nostre node arrel és un Area2D, podem utilitzar els senyals integrats de Godot per escoltar les superposicions.
Per configurar-ho, seleccioneu el node Enemic i aneu a Node moll (al costat de l'Inspector). Trobeu el senyal body_entered. Aquest senyal es dispara cada cop que un cos físic (com el nostre Player's CharacterBody2D) entra a la zona.

Feu doble clic a body_entered per obrir el diàleg de connexió i, a continuació, connecteu-lo al vostre script enemy.gd.

Això crea una funció anomenada _on_body_entered al vostre script. Hem d'assegurar-nos que el cos que va entrar a la zona és realment el jugador i no una paret o un altre objecte.
Actualitzeu la funció amb el codi següent:
func
_on_body_entered(body):if not
body.is_in_group("Player"):returnprint("Deal damage to player")
Us dels grups
El codi anterior comprova si el cos està en un grup anomenat "Jugador". Tanmateix, encara no hem assignat el nostre jugador a aquest grup. Els grups són la manera de Godot d'etiquetar els nodes perquè pugueu identificar-los en codi sense dependre de noms o camins de nodes específics.
Arreglem això. Obriu la vostra escena player.tscn i seleccioneu l'arrel Jugador node. En el Node moll, canvia a Grups pestanya i, a continuació, feu clic a + icona per crear un grup nou. Anomeneu-lo "Reproductor" i feu clic D'acord.


Ara, sempre que el jugador xoqui amb l'enemic, el guió reconeixerà el grup "Jugador" i executarà el codi.
Nota de Godot: grups per a la identificació de nodes
Els grups a Godot funcionen com etiquetes. Qualsevol node pot pertànyer a diversos grups, i podeu comprovar la pertinença amb is_in_group ("nom") o recuperar tots els nodes d'un grup amb get_tree().get_nodes_in_group ("nom"). Els grups són una manera lleugera d'identificar nodes sense tipus de ruta o tipus de codificació, la qual cosa manté el vostre codi flexible i reutilitzable.
Més informació: Grups (https://docs.godotengine.org/en/4.6/tutorials/scripting/groups.html)
Provar la col·lisio
Amb el senyal connectat i el grup assignat, comprovem que tot funciona. Torna a executar el joc i mou el teu jugador al camí de l'enemic. Quan xoquen, comproveu Sortida panell a la part inferior de l'editor. Hauríeu de veure el missatge "Fes dany al jugador".

Animar l'enemic
El moviment de patrulla funciona, igual que la nostra detecció de col·lisions, però el ratpenat encara sembla estàtic mentre llisca pel seu camí. Per donar-li vida, afegim una animació de bateig perquè les seves ales batguin mentre vola.
Primer, seleccioneu el Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know. node a l'escena de l'enemic.

En el Animació panell a la part inferior, feu clic Animació > Nou.

Anomena la mosca d'animació.

Animarem la textura de l'Sprite per canviar entre dos fotogrames, creant un efecte de solapa d'ala. Tal com vam fer al capítol anterior amb el reproductor, seleccioneu el Sprite node, troba el Textura propietat a l'Inspector i feu clic a clau icona per inserir un fotograma clau.

Confirmeu que voleu crear una pista nova (i assegureu-vos Crea pistes de RESTABLECIMENT està comprovat).

En aquest cas, establiu la durada de l'animació en 0,3 segons.

Necessitem dos fotogrames per al moviment de bateig:
- Fotograma 1 (0,0 s): El sprite predeterminat (ales cap amunt). Això ja està establert pel primer fotograma clau.
- Fotograma 2 (~0,15 s): Les ales cap avall sprite.
Mou la línia de temps a uns 0,1 o 0,2 segons. Feu clic amb el botó dret a la pista i seleccioneu Insereix la clau.

Amb aquest nou fotograma clau seleccionat, arrossegueu el sprite amb les ales cap avall (p. ex.,
character_0025.png) al Valor ranura a l'inspector.

Repetiu aquest procés si voleu més fotogrames, o simplement alterna entre els dos.

Un cop hàgiu acabat l'animació, activeu-la Bucle de manera que el ratpenat bateja contínuament.

No dubteu a modificar el temps. En el nostre exemple següent, hem ajustat la durada a 0,4 segons per fer que el bateig sigui una mica més lent i per fer més espai per a un fotograma clau addicional.

Reprodueix l'animació
L'últim pas és dir que l'animació es reprodueixi automàticament quan es carregui l'escena. Afegiu la funció _ready al vostre script enemy.gd:
func
_ready():$AnimationPlayer.play("fly")
Si executes el joc ara, l'enemic patrullarà el seu camí mentre bat les ales.

Ara tenim un enemic funcional que patrulla una zona i detecta el jugador. En el següent capítol, implementarem el sistema de salut perquè aquestes col·lisions facin mal al jugador.
Principals mecàniques: Salut i puntuació
En els capítols anteriors, vam implementar el moviment del jugador i vam crear un enemic que patrullava el nivell. Tanmateix, el nostre joc actualment no té conseqüències i recompenses. El jugador pot caminar a través dels enemics sense patir danys i no hi ha objectius per completar.
En aquest capítol, convertirem el nostre projecte en un joc adequat introduint la mecànica de joc bàsica. Implementarem un sistema de salut on el jugador rep danys en xocar amb els enemics, restablint el nivell si la salut arriba a zero. També crearem monedes col·leccionables i un sistema de puntuació que fa un seguiment del progrés del jugador en els restabliments de nivell mitjançant un script de càrrega automàtica persistent.
El sistema de dany
El nostre primer objectiu és permetre que el jugador rebi danys. Definirem una variable de salut, crearem una funció per gestionar la lògica del dany i restablirem l'escena quan el jugador sigui derrotat.
Configuració de la salut del jugador
Hem de fer un seguiment de quants cops pot rebre el jugador abans que el joc es reiniciï. Obrim l'script player.gd i afegim una variable de salut a la part superior del fitxer (on hi ha les altres variables exportades).
@export
var
health : int = 3Iniciem aquesta variable a 3, és a dir, el jugador té tres "cors" o punts de vida.
Creacio de la funcio de rebre dany
A continuació, necessitem una manera de reduir aquesta salut. Crearem una funció anomenada take_damage que accepta una quantitat de dany per aplicar. Aquesta funció restarà el dany de la salut actual i comprovarà si el jugador ha estat derrotat.
Afegiu la funció següent a player.gd:
func
take_damage(amount : int): health -= amountif
health <= 0: game_over()Aquí teniu la lògica:
- La variable de salut es redueix en la quantitat.
- Comprovem si la salut ha baixat a zero (o per sota).
- Si és així, anomenem una funció game_over.
Implementació del Game Over
La funció game_over gestiona el que passa quan el jugador perd. De moment, simplement tornarem a carregar el nivell actual per reiniciar el joc.
Afegiu aquesta funció al vostre script:
func
game_over(): get_tree().change_scene_to_file("res://Scenes/level_1.tscn")Aquesta línia accedeix a l'arbre d'escenes i li indica que torni a carregar el level_1.tscn
fitxer, restablint efectivament l'estat del joc.
Actualitzacio de l'script de l'enemic
Ara que el jugador pot rebre danys, necessitem que l'enemic l'infligui realment. Com a tal, obriu l'script enemy.gd a continuació.
En el capítol anterior, vam configurar la funció _on_body_entered per imprimir un missatge quan es detectés el reproductor. Ara modificarem aquesta funció per trucar a take_damage al reproductor.
Podem actualitzar la funció de la següent manera:
func
_on_body_entered(body):if not
body.is_in_group("Player"):returnbody.take_damage(1)
Quan el jugador entra a l'àrea de col·lisió de l'enemic, el guió confirma que el cos pertany al grup "Jugador" i després crida a take_damage(1), causant un punt de dany.
Gestioneu les devolucions de trucades de la física
Si proveu el joc ara i deixeu que el jugador rebi tres danys, el joc intentarà reiniciar-se. Tanmateix, és possible que us trobeu amb un error a la consola del depurador i el joc pot fallar o comportar-se de manera inesperada.

Aquest error es produeix perquè _on_body_entered es produeix durant un pas de física. Godot bloqueja el motor físic durant aquest pas per garantir l'estabilitat. No es permet intentar canviar l'escena (que elimina nodes) mentre el motor està processant col·lisions.
Per solucionar-ho, hem d'utilitzar call_deferred. Això diu a Godot que espere fins que s'acabi el fotograma actual abans d'executar la funció.
Actualitzeu la funció take_damage a player.gd per utilitzar l'execució diferida:
func
take_damage(amount : int): health -= amount# Defer game_over to avoid freeing nodes mid-physics stepif
health <= 0: call_deferred("game_over")Ara, quan la salut arribi a zero, Godot acabarà els seus càlculs físics per al marc i aleshores truca a game_over, evitant l'error.
Nota de Godot: call_deferred i seguretat física
Godot processa la física en passos discrets. Durant una devolució de trucada de física (com
_on_body_entered), el motor bloqueja l'arbre de l'escena per evitar modificacions
que podria corrompre la simulació. La crida a queue_free(), el canvi d'escenes o l'eliminació de nodes directament dins d'aquestes devolucions de trucada provocarà un error.
call_deferred("function_name") indica a Godot que esperi fins que el pas de física del fotograma actual s'hagi completat abans d'executar la funció. És un patró habitual en projectes Godot quan cal modificar l'escena en resposta a esdeveniments de física.
Més informació: Object.call_diferred (https://docs.godotengine.org/en/4.6/classes/class_object.html#class-object-mètode-trucada-diferit)
Provar i desar
Tornem a provar el joc. Xoc amb l'enemic tres vegades. En el tercer cop, l'escena hauria de tornar a carregar sense problemes.
Quan estigueu satisfet amb el comportament de l'enemic, hauríem de desar-lo com a escena reutilitzable perquè puguem col·locar diversos enemics al nivell.
- Arrossegueu el node Enemy des del moll d'Escenes a la vostra carpeta Escenes al FileSystem.
- Deseu-lo com a enemy.tscn.

Ara pots duplicar l'enemic al teu nivell (Ctrl+D) per afegir més reptes. No dubteu a ajustar les propietats de direcció de moviment i velocitat de moviment per a cada instància per crear patrons de patrulla variats.


Creacio de col·leccionables
Ara que tenim riscos, afegim recompenses. Crearem un objecte moneda que el jugador podrà recollir per augmentar la seva puntuació. Començarem configurant l'escena visual i després animant-la.
Configuració de l'escena de la moneda
Per començar, crea una escena nova amb un Àrea 2D com a node arrel. Anomena-ho Coin. Com l'enemic, utilitzem Area2D perquè pot detectar cossos superposats sense bloquejar-los físicament, que és exactament el que volem per a un objecte col·leccionable.


A continuació, afegiu a Sprite2D node (anomenat Sprite) com a fill i assigneu la textura de la moneda dels vostres actius (p. ex., tile_0151.png o similar). Assegureu-vos que la seva posició estigui configurada en (0, 0).


Així podem detectar el reproductor, afegiu a CollisionShape2D node fill.

Assigna a CircleShape2D a la forma de col·lisió i canvieu-ne la mida perquè coincideixi amb el gràfic de la moneda.

Programació d'animació de monedes
Perquè la moneda sembli atractiva, escriurem una animació senzilla que la faci moure cap amunt, cap avall i girar. Si bé podríem utilitzar un
AnimationPlayer, fer-ho mitjançant codi ens proporciona una bona suavitat matemàtica i demostra com manipular transformacions directament.
Per començar, adjunteu un script nou anomenat coin.gd al node Coin.

A continuació, afegiu el codi següent per definir les nostres variables d'animació i lògica:
extends
Area2Dvar
rotate_speed : float = 3.0
var
bob_height : float = 5.0
var
bob_speed : float = 5.0@onready
var
start_pos : Vector2 = global_position @onready
var
sprite : Sprite2D = $Spritefunc
_physics_process(delta):var
time = Time.get_unix_time_from_system()# rotatesprite.scale.x = sin(time * rotate_speed)
# bob up and downvar
y_pos = ((1 + sin(time * bob_speed)) / 2) * bob_height global_position.y = start_pos.y - y_posDesglossem aquest efecte:
- Rotació: Utilitzem la funció sin() amb el temps actual per fer oscil·lar l'escala X del sprite entre -1 i 1. Això crea la il·lusió que la moneda gira. També fem servir una variable de velocitat de rotació per controlar la velocitat o la lentitud que volem que giri la moneda.
- Balanç: Utilitzem una altra ona sinusoïdal per calcular un desplaçament vertical (y_pos) i restar-lo de start_pos. Això fa que la moneda suri suaument cap amunt i cap avall. Igual que amb la rotació, disposem d'una variable de velocitat per controlar la rapidesa amb què es produeix el bobbing. En aquest cas, però, també tenim la variable d'alçada per controlar fins a quin punt es mourà la moneda des de la seva posició inicial.
Sobre les ones sinusoïdals
La funció sin() que hem utilitzat crea el que es coneix com a ona sinusoïdal. Una ona sinusoïdal és una corba suau i semblant a una ona que es repeteix amunt i avall en un patró regular.
S'utilitza habitualment als jocs per crear moviments naturals en bucle, com ara flotar, balancejar-se o agitar. Quan utilitzeu sin() al vostre codi, obtindreu un valor que varia sense problemes de -1 a 1 amb el temps.
A l'exemple del moviment de balanceig, notareu que el valor "pecat" s'ajusta afegint primer un i després dividint per dos. Això transforma el seu rang natural en un nou rang entre zero i un. Aquest canvi és útil perquè assegura que el moviment es mantingui sempre positiu, ideal per als casos en què voleu que la posició vertical de la moneda es mantingui per sobre d'un punt determinat.
Si col·loqueu la moneda al nivell i executeu el joc, la veureu girant i flotant.

Recollir monedes
Ara que tenim una moneda a l'escena, hem de connectar la lògica perquè el jugador pugui recollir-la. Això implica afegir una funció de puntuació al jugador i connectar el senyal de col·lisió de la moneda.
Actualitzacio de l'script del jugador
En primer lloc, necessitem una funció al jugador que gestioni els augments de puntuació. Obert
player.gd i afegiu una funció de marcador de posició:
func
increase_score(amount: int): print("increase score")Implementarem la lògica de puntuació real a la secció següent. De moment, aquesta declaració d'impressió ens ajudarà a comprovar que el mecànic de recollida funciona.
Gestionar la col·lisio
Amb el jugador preparat per rebre actualitzacions de puntuació, podem connectar el senyal de col·lisió de la moneda. Primer, seleccioneu el node Coin a la vostra escena. En el Node dock, connecteu el senyal body_entered a l'script coin.gd.


A la funció _on_body_entered generada, afegiu la lògica següent:
func
_on_body_entered(body):if not
body.is_in_group("Player"):return
body.increase_score(1) queue_free()Aquest codi comprova si el cos que xoca és el jugador. Si és així, activa la funció augmente_score al reproductor i després crida a queue_free() a si mateix. queue_free() és la forma estàndard d'eliminar un node de l'escena a Godot, "recollint" efectivament la moneda.
Provar i desar
Executar el joc i entrar en una moneda. Hauria de desaparèixer i hauríeu de veure "augmenta la puntuació" imprès al tauler de sortida.

Un cop verificada, deseu la moneda com a escena (coin.tscn) a la carpeta Escenes perquè pugueu col·locar diverses monedes al llarg del nivell.



Puntuacio persistent
Tenim un problema: sempre que el jugador mor, tornem a carregar l'escena amb change_scene_to_file. Això restableix tot a l'escena, inclòs el guió del jugador i qualsevol variable que hi hagi dins. Si emmagatzemem la puntuació a player.gd, la puntuació es tornarà a zero cada vegada que el jugador mor.
Per mantenir la puntuació entre els restabliments de nivell, necessitem una variable que existeixi fora l'escena. A Godot, fem servir un Carrega automàtica (o Singleton) per a això.
Creació d'un script de càrrega automàtica
Una càrrega automàtica és un script que Godot carrega una vegada a l'inici i es manté viu durant tota la sessió del joc. Com que no forma part de cap escena en particular, sobreviu als canvis d'escenari. Per configurar-ho, aneu a Projecte > Configuració del projecte.

Canvia a la Globals pestanya. Aquí és on definim scripts que es carreguen automàticament quan s'inicia el joc i romanen a la memòria fins que el joc es tanca.
- En el Nom del node camp, escriviu PlayerStats.
- Feu clic a la icona de la carpeta al costat del camp Camí per crear un script nou. Deseu-lo com a player_stats.gd a la vostra carpeta Scripts.
- Feu clic Afegeix.


Ara hauríeu de veure PlayerStats a la llista de càrrega automàtica.

Nota de Godot: Carrega automàtica de singletons
Una càrrega automàtica és un script (o escena) que Godot carrega automàticament quan comença el joc i es manté a la memòria durant tota la sessió. Com que viu fora de qualsevol escena en particular, les seves dades persisteixen durant els canvis d'escena. Podeu accedir-hi des de qualsevol script mitjançant el nom de node que heu assignat a la configuració del projecte (en el nostre cas, PlayerStats).
Les càrregues automàtiques són ideals per a l'estat global, com ara puntuacions, configuració o desar dades. Tanmateix, eviteu posar-hi massa lògica. Si un sistema només ha d'existir dins d'una única escena, un node normal és una millor opció.
Més informació: Singletons (càrrega automàtica) (https://docs.godotengine.org/en/4.6/tutorials/scripting/singletons_autoload
.html)
Implementació de la puntuació global
Obriu l'script player_stats.gd acabat de crear. Només necessitem una única variable per mantenir la puntuació:
extends
Nodevar
score : int = 0Com que aquest script és una càrrega automàtica, podem accedir-hi des de qualsevol lloc del nostre joc utilitzant el nom PlayerStats.
Actualitzacio de l'script del jugador
Ara, torna a player.gd. Modificarem la funció augmente_score per actualitzar la variable global en lloc d'imprimir només un missatge.
func
increase_score(amount : int): PlayerStats.score += amount print(PlayerStats.score)Provar la persistència
Executar el joc i recollir unes quantes monedes. Hauríeu de veure el recompte de la puntuació a la sortida (1, 2, 3...).

Ara, intenta deixar que el jugador mori intencionadament per activar un restabliment de l'escena. Quan es torni a carregar el nivell, recull una altra moneda. La puntuació hauria de continuar augmentant des d'on ho vau deixar (p. ex., 4, 5...), demostrant que les dades han persistit durant el canvi d'escena.

Mitjançant l'ús d'una càrrega automàtica, hem separat amb èxit les dades del joc de la lògica de l'escena, garantint-nos que el progrés es desa fins i tot quan el nivell es restableixi.
Al capítol següent, ampliarem el nostre disseny de nivell creant "Finals de finalització" que permeten al jugador completar un nivell i viatjar al següent.
Logica de finalitzacio del nivell
En aquest capítol, implementarem la condició guanyadora del nostre joc. Cada nivell de plataformes necessita un objectiu clar perquè el jugador assoleixi, i el nostre serà una "bandera final". Quan el jugador toca aquesta bandera, el joc detecta la col·lisió i passa al següent nivell.
Per aconseguir-ho, primer organitzarem la nostra jerarquia d'escenes per mantenir-la manejable. Aleshores, construirem l'objecte de bandera final utilitzant un node Area2D i escriurem un script breu per gestionar la lògica de transició d'escena.
Organitzacio de la jerarquia de l'escena
A mesura que el nostre nivell creix, el moll d'escena es pot desordenar ràpidament. Ara mateix, l'escena conté diversos nodes individuals d'enemics i monedes barrejats, cosa que dificulta trobar res d'un cop d'ull. Abans d'afegir més objectes, agrupem nodes similars sota els nodes del contenidor perquè l'arbre es mantingui ordenat.

Feu clic amb el botó dret a Principal node i seleccioneu Afegeix un node fill. Cerca un genèric Node (sovint anomenat "Node buit" perquè no té propietats de transformació ni visuals) i afegiu-lo a l'escena.

Canvieu el nom d'aquest nou node a Monedes. A continuació, seleccioneu tots els vostres nodes de monedes existents al moll d'escena i arrossegueu-los al node Monedes per crear-los.

Repetiu aquest procés per als enemics:
- Creeu un altre genèric Node i canviar-li el nom a Enemics.
- Seleccioneu tots els nodes enemics i arrossegueu-los al node Enemics.

La teva jerarquia d'escenes hauria de semblar molt més neta, amb seccions plegables per als objectes del joc.

Creació definitiva de la bandera
Ara que la nostra jerarquia està organitzada, podem construir l'objecte objectiu. La bandera final funciona de manera similar a les nostres monedes i enemics: necessita un sprite visual i una àrea de col·lisió per detectar el jugador. Quan el jugador hi entri, el joc sabrà que el nivell s'ha completat.
Primer, neteja un espai al teu nivell per a l'objectiu. Suprimeix qualsevol node de moneda situat on voleu col·locar la bandera.
A continuació, creeu un nou Àrea 2D node i canviar-lo per EndFlag.


A continuació, hem d'afegir el component visual. En el Sistema de fitxers atraca, navega fins a Sprites > Objectes i cerqueu la imatge de la bandera (p. ex., tile_0112.png). Arrossegueu aquesta imatge a l'escena per crear un sprite.

Canvieu el nom del nou node de sprite a Sprite i assegureu-vos que sigui un fill del
Node EndFlag.

Igual que amb els nostres altres objectes, configureu el Posició del node Sprite a (0, 0), de manera que s'alinea perfectament amb l'origen del node pare.

Finalment, hem de definir l'àrea de detecció perquè el joc sàpiga quan ha arribat el jugador. Afegeix a CollisionShape2D node com a fill d'EndFlag.

A l'Inspector, configureu el Forma propietat a a CircleShape2D. Canvieu la mida del cercle a la finestra gràfica perquè cobreixi l'esprit de la bandera. Aquesta àrea representa la zona a la qual ha d'entrar el jugador per acabar el nivell.

La vostra estructura d'escena EndFlag ara està completa i preparada per a la lògica.

Programaci de la bandera final
Amb l'estructura del node al seu lloc, necessitem un script per gestionar el que passa quan el jugador arriba a la bandera. Seleccioneu el node EndFlag i adjunteu un script nou anomenat end_flag.gd.

Connexió del senyal
Per detectar quan el jugador entra a l'àrea de la bandera, utilitzarem el senyal body_entered, tal com vam fer amb les monedes i els enemics. Com a recordatori, aquest senyal es dispara sempre que un cos físic se superposa a l'Area2D.
- Seleccioneu el node EndFlag.
- Aneu a la Node pestanya (al costat de l'Inspector).
- Feu doble clic al senyal body_entered.

Al diàleg de connexió, connecteu el senyal al mateix node EndFlag. Això generarà la funció _on_body_entered al vostre script.

Implementació de la transició d'escena
Ara podem obrir l'script i implementar la lògica per carregar el següent nivell. Utilitzarem una variable exportada per definir quina escena carregar, fent que aquest script sigui reutilitzable per a qualsevol nivell.
Afegiu el codi següent a end_flag.gd:
extends
Area2D@export
var
scene_to_load : PackedScenefunc
_on_body_entered(body):if not
body.is_in_group("Player"):returnget_tree().change_scene_to_packed(scene_to_load)Així és com funciona cada peça:
- @export var scene_to_load : PackedScene: això crea un camp a l'Inspector on podem arrossegar i deixar anar un fitxer .tscn. Fer servir PackedScene és més segur i còmode que escriure rutes manualment.
- if not body.is_in_group("Player"): aquesta clàusula de guarda verifica que l'objecte que entra a la bandera sigui realment el jugador i no un altre cos de física.
- get_tree().change_scene_to_packed(scene_to_load): això indica a Godot que descarregui l'escena actual i instanciï l'escena emmagatzemada a la nostra variable.
Nota de Godot: càrrega d'escena i escena empaquetada
Un PackedScene és un recurs que conté una escena en un format compacte i carregable. Quan arrossegueu un fitxer .tscn a una variable @export a l'Inspector, Godot emmagatzema una referència a aquesta escena empaquetada. En temps d'execució, cridar a change_scene_to_packed() el desempaqueta i s'intercanvia al nou arbre d'escenes.
L'ús de PackedScene en lloc d'una cadena de ruta de fitxer (com change_scene_to_file()) té dos avantatges: Godot valida la referència en el moment de l'edició (de manera que no podeu escriure accidentalment un camí incorrecte) i precarrega el recurs per a transicions més ràpides.
Més informació: Escena empaquetada (https://docs.godotengine.org/en/4.6/classes/class_packedscene.html)
Provar la bandera final
Amb l'script al seu lloc, comprovem que tot funciona. Seleccioneu el node EndFlag al moll de l'escena. A l'Inspector, veureu Escena per carregar propietat, on hem d'assignar l'escena a la qual ha de passar el joc.
Com que encara no tenim un "Nivell 2", podem assignar temporalment una escena diferent (com l'escena del reproductor) només per verificar que la transició funciona.

Nota: Godot impedeix que carregueu exactament el mateix fitxer d'escena que està actiu actualment per evitar bucles de recursivitat infinits. Per provar, assegureu-vos que trieu un fitxer d'escena diferent del que esteu reproduint actualment.
Executeu el joc i moveu el vostre jugador a la bandera. Si tot està configurat correctament, el joc hauria de canviar immediatament a l'escena que heu assignat.

Felicitats! Heu implementat amb èxit el bucle bàsic del vostre joc: comença un nivell, supera els reptes i assoleix l'objectiu. En el següent capítol, polirem l'experiència afegint elements d'IU per mostrar la salut i la puntuació del jugador.
Construccio de la interfície d'usuari
En aquest capítol, crearem la interfície d'usuari (UI) per al nostre joc. Una bona interfície d'usuari proporciona comentaris essencials al jugador, com ara el seu estat de salut actual i el seu progrés a través del nivell.
Dissenyarem un Heads-Up Display (HUD) format per dos elements principals: una fila de cors que representen la salut i una etiqueta de text que mostra la puntuació actual. A continuació, connectarem aquests elements visuals a la nostra lògica de joc mitjançant senyals, assegurant-nos que s'actualitzen a l'instant sempre que el jugador tingui danys o reculli una moneda.
Configuració del CanvasLayer
Per garantir que la nostra interfície d'usuari es mantingui visible a la pantalla independentment d'on es mogui el jugador al món del joc, necessitem un node especial que es mostri independentment de la càmera del joc. A Godot, aquest node s'anomena CanvasLayer.
Treballarem dins del Jugador escena per a aquesta configuració. Comencem per crear un nou node CanvasLayer com a fill del node del reproductor arrel.

El CanvasLayer actua com un full transparent enganxat al monitor. Qualsevol node col·locat dins d'ell es mantindrà fix en relació a la pantalla, el que el converteix en el contenidor perfecte per a barres de salut, puntuacions i menús.
Nota de Godot: CanvasLayer per a la interfície d'usuari de l'espai de pantalla
De manera predeterminada, tots els nodes d'una escena comparteixen el mateix llenç i es veuen afectats pel Camera2D actiu. Un CanvasLayer crea una capa de dibuix independent amb la seva pròpia transformació, el que significa que els seus fills ignoren completament la càmera. És per això que les barres de salut i les etiquetes de puntuació es mantenen fixades a les cantonades de la pantalla fins i tot mentre el jugador passa pel nivell.
Podeu apilar diversos CanvasLayers amb diferents valors de capes per controlar l'ordre de dibuix. Un número de capa més alt es renderitza a la part superior.
Més informació: CanvasLayer (https://docs.godotengine.org/en/4.6/classes/class_canvaslayer.html)
Creacio de l'indicador de salut
La nostra pantalla de salut constarà de diverses icones de cor disposades en fila. En lloc de posicionar cada cor manualment, utilitzarem un node contenidor per gestionar el disseny automàticament. Aquest enfocament manté la interfície d'usuari flexible si decidim afegir més cors més tard.
Afegir la textura del cor
Primer, creem un únic cor que serveixi de plantilla. Crea una nova
El node TextureRect com a fill del CanvasLayer.

Canvieu el nom d'aquest node a Cor.

Ara hem d'assignar una imatge a aquest node. En el Sistema de fitxers dock, navegueu a la carpeta Sprites > Objectes i localitzeu la textura del cor (p. ex., tile_0044.png).

Arrossegueu la textura del cor cap a Textura propietat del node Heart a l'Inspector.

Per defecte, la textura pot semblar estirada o comprimida a la vista de l'editor perquè TextureRect no canvia automàticament la mida per coincidir amb la relació d'aspecte de la imatge en totes les configuracions.

Per solucionar-ho, hem de definir la mida explícitament. A l'Inspector, amplieu el Disseny pestanya i després la Transformar secció. Estableix el Mida fins a 80 x 80.

Això garanteix que el nostre cor es mostri com un quadrat perfecte.

Us de contenidors per al disseny
Per gestionar de manera eficient diversos cors, utilitzarem un HBoxContainer (Horizontal Box Container). Aquest node disposa automàticament els seus fills en una fila horitzontal, la qual cosa ens estalvia d'haver de posicionar cada cor a mà.
Creeu un nou node HBoxContainer com a fill de CanvasLayer.

Canvieu el nom d'aquest node a HealthContainer.

Hem d'assegurar-nos que el contenidor és prou alt com per contenir els nostres cors. A l'Inspector, sota Disseny > Transformar, estableix el Talla Y a 80.

Ara, arrossegueu el vostre node Heart existent al HealthContainer per convertir-lo en fill.

Amb el cor dins del contenidor, hem d'ajustar com s'escala. Seleccioneu el node Cor i canvieu-lo Mode d'expansió propietat a Amplada d'ajust. Això indica que la textura s'escala horitzontalment per adaptar-se a les regles de disseny del contenidor mantenint la seva relació d'aspecte.

Finalment, duplica el node Cor (Ctrl+D) dues vegades per crear un total de tres cors.

L'HBoxContainer els organitzarà automàticament un al costat de l'altre.

Creació del text de la partitura
A continuació, afegirem una etiqueta de text per mostrar la puntuació del jugador. Això dóna als jugadors una clara sensació de progressió a mesura que recullen monedes al llarg del nivell. Per fer-ho, creeu un nou node Label com a fill de CanvasLayer.

Canvieu el nom d'aquest node a ScoreText.

A continuació, a l'Inspector, configureu el Text propietat per puntuar: 500. Això serveix com a marcador de posició perquè puguem veure com es veu a l'editor.

Com veureu, la mida de lletra predeterminada és bastant petita.

Per estilitzar el text, localitzeu el Configuració de l'etiqueta propietat a l'inspector. Feu clic al menú desplegable (actualment <buit>) i seleccioneu Nova configuració de l'etiqueta.

A continuació, feu clic al nou recurs per ampliar-ne les propietats. Sota Font, posem el Mida fins a 38 píxels.

Per fer que el text aparegui contra el fons, afegim una ombra. Amplia el Ombra secció, augmentar la Mida a 6 i ajusteu el Color alfa (transparència) per fer-lo visible (el valor exacte depèn de tu i de la teva estètica).

Col·loqueu HealthContainer i ScoreText a la cantonada superior esquerra de la pantalla. Si executeu el joc ara, hauríeu de veure la vostra interfície d'usuari superposada al joc.

Connectar la logica amb senyals
La nostra interfície d'usuari sembla bé, però ara mateix és completament estàtica. Perquè sigui funcional, hem d'actualitzar la pantalla sempre que canviï la salut o la puntuació del jugador. Farem servir signals for this purpose. Signals let the player script broadcast when something happens, and the UI script can listen and react, keeping both scripts cleanly separated.
Actualitzacio de l'script del jugador
Per començar, obriu l'script player.gd. Hem de definir dos senyals personalitzats a la part superior del fitxer. La paraula clau del senyal declara un esdeveniment amb nom al qual es poden subscriure altres nodes:
signal
OnUpdateHealth (health : int)signal
OnUpdateScore (score : int)Nota de Godot: senyals personalitzats
Els senyals a Godot implementen el patró d'observador. Qualsevol node pot definir un senyal amb el senyal SignalName(args) i emetre-lo amb SignalName.emit(values). Altres nodes es subscriuen mitjançant source.SignalName.connect(callback). Això permet que els sistemes es comuniquin sense referències directes entre ells, mantenint el vostre codi modular.
Els nodes integrats inclouen molts senyals predefinits (com body_entered a Area2D). Els senyals personalitzats amplien aquest sistema per a la vostra pròpia lògica de joc.
Més informació: Senyals (https://docs.godotengine.org/en/4.6/tutorials/scripting/gdscript/gdscript_b asics.html#signals)
A continuació, hem d'emetre aquests senyals quan canvien els valors rellevants. Actualitzem primer la funció take_damage per emetre OnUpdateHealth:
func
take_damage (amount : int): health -= amount OnUpdateHealth.emit(health)if
health <= 0: call_deferred("game_over")A continuació, actualitzarem la funció augmente_score per emetre OnUpdateScore:
func
increase_score (amount : int): PlayerStats.score += amount OnUpdateScore.emit(PlayerStats.score)Ara, sempre que el jugador fa malbé o recull una moneda, aquests senyals transmetran els nous valors.
Creació de l'script d'interfície
Per al següent pas, necessitem un script per gestionar els elements de la IU. Seleccioneu el
CanvasLayer i adjunteu un script nou anomenat player_ui.gd.

En aquest script, necessitem referències als nostres nodes d'IU i al reproductor. També crearem una matriu per contenir les nostres icones del cor perquè puguem mostrar-les o amagar-les en funció de la salut actual del jugador.
Recordeu que el nostre CanvasLayer és un fill del nostre node Player, així que podem utilitzar
get_parent() per obtenir la referència al jugador. Afegiu les variables següents a player_ui.gd:
extends
CanvasLayer@onready
var
health_container = $HealthContainer
var
hearts : Array = []@onready
var
score_text : Label = $ScoreText @onready
var
player = get_parent()Implementació de funcions d'actualització
Ara crearem dues funcions per gestionar les actualitzacions visuals.
La funció _update_hearts fa un bucle a través de la matriu d'icones del cor i compara cada índex amb el valor de salut actual. Si l'índex és inferior a la salut, aquest cor es manté visible; en cas contrari queda amagat.
# Show or hide each heart icon based on current healthfunc
_update_hearts(health : int):for
i
in
range(len(hearts)): hearts[i].visible = i < healthLa funció _update_score simplement actualitza el text de l'etiqueta.
func
_update_score(score : int): score_text.text = "Score: " + str(score)Connectar-ho tot a _ready
Finalment, hem de connectar-ho tot quan comenci el joc. En el
funció _ready, farem:
- Ompliu la matriu de cors amb els fills de HealthContainer.
- Connecteu els senyals del reproductor a les nostres funcions d'actualització.
- Truqueu immediatament a les funcions d'actualització una vegada per assegurar-vos que la interfície d'usuari mostra els valors inicials correctes.
func
_ready():hearts = health_container.get_children()
# Subscribe to the player's health and score signals
player.OnUpdateHealth.connect(
_update_hearts
) player.OnUpdateScore.connect(
_update_score
)# Show the correct starting values on the first frame_update_hearts(player.health)
_update_score(PlayerStats.score)
Provar la interfície
Executeu el joc i proveu la vostra nova interfície d'usuari. Els cors i l'etiqueta de la puntuació haurien d'aparèixer a l'extrem superior esquerre de la pantalla, superposant el joc.
Quan reps dany d'un enemic, els cors haurien de desaparèixer un a un.

Quan recolliu monedes, l'etiqueta de puntuació s'hauria d'actualitzar a l'instant.

Ara teniu una interfície d'usuari totalment funcional que respon als esdeveniments de joc en temps real. En el següent capítol, ens centrarem a polir la sensació del joc afegint comentaris visuals i efectes de so.
En aquest capítol, ens centrarem en la "sensació de joc", la retroalimentació tàctil i visual que fa que un joc sigui satisfactori per jugar. Actualment, el nostre joc funciona correctament: el jugador pot moure's, recollir monedes i fer mal. Tanmateix, el feedback és mínim. Quan el jugador rep un cop, l'única indicació és un número que canvia a la interfície d'usuari.
Millorarem aquesta experiència mitjançant la implementació d'efectes visuals com un flaix danyat i un moviment de pantalla per donar pes als impactes. També millorarem l'entorn afegint un fons de paral·laxi que crea una sensació de profunditat. Finalment, integrarem l'àudio per proporcionar comentaris auditius per als esdeveniments clau del joc.
Visuals de dany
La nostra primera tasca és millorar el feedback quan el jugador rep danys. Un bon feedback visual ajuda el jugador a entendre què està passant a l'instant, sense haver de mirar una barra de salut. Perquè aquests moments tinguin impacte, implementarem dos efectes: un flaix visual on el personatge es torna vermell i un sacsejada de la pantalla que sacseja la càmera.
Implementacio del flaix de dany
El flash de danys és un clàssic de videojocs que comunica clarament la lesió. Això ho aconseguirem manipulant la propietat de modular de l'esprit del jugador.
Obriu l'script player.gd. Crearem una nova funció anomenada
_damage_flash que canvia el color del sprite a vermell i després el torna a blanc al cap d'una fracció de segon.
Afegiu la funció següent a la part inferior del vostre script:
# Briefly tint the sprite red to signal damagefunc
_damage_flash(): sprite.modulate = Color.REDawait get_tree().create_timer(0.05).timeout sprite.modulate = Color.WHITE
En aquesta funció:
- Definim sprite.modulate a Color.RED, que tenyeix la textura de l'esprit.
- Utilitzem l'espera i un temporitzador temporal per aturar l'execució durant 0,05 segons.
- Restaurem el color a Color.WHITE (l'estat per defecte, sense tinta).
Nota de Godot: la propietat modular
Cada node CanvasItem (que inclou tots els nodes 2D com Sprite2D i Control) té una propietat de modulació. Aquest és un valor de Color que es multiplica amb els colors originals del node en temps de renderització. Configurant-lo a Color.RED tenyeix el sprite de vermell; establir-lo a Color (1, 1, 1, 0,5) el fa semitransparent.
Color.WHITE (per defecte) deixa els colors originals sense canvis.
Com que modulate s'hereta, establir-lo en un node pare també matisa tots els seus fills. Això fa que sigui fàcil de flashejar un caràcter sencer (sprite, partícules i tot) en una única línia de codi.
Més informació: CanvasItem.modulate (https://docs.godotengine.org/en/4.6/classes/class_canvasitem.html#class-ca nvasitem-property-modulate)
Per activar aquest efecte, hem de cridar la funció sempre que el jugador rep danys. Localitzeu la funció take_damage al vostre script i afegiu la trucada a
_damage_flash():
_damage_flash()
Ara, quan el jugador xoca amb un enemic, el sprite parpellejarà breument en vermell, proporcionant una confirmació visual immediata de l'impacte.

Implementació del tremolor de camera
A continuació, afegirem un efecte de sacsejada de pantalla. Això afegeix una sensació d'impacte visceral al dany, reforçant que alguna cosa important acaba de passar. Implementarem això adjuntant un script a la nostra càmera que compensa ràpidament la seva posició quan s'activa.
En el Jugador escena, seleccioneu el node Camera2D. Adjunteu-hi un script nou i anomeneu-lo camera_shake.gd.

Obriu l'script camera_shake.gd i substituïu-ne el contingut pel codi següent:
extends
Camera2Dvar
intensity : float = 0func
_ready():# Listen for damage events from the parent Player nodeget_parent().OnUpdateHealth.connect(_danyar_agitar)
# Kick off a shake whenever health changesfunc
_damage_shake(health : int): intensity = 3func
_process(delta):if
intensity > 0:# Gradually reduce the shake strength each frame
intensity = lerpf(intensity, 0, delta * 10) offset = _get_random_offset()# Generate a random camera offset within the current intensity rangefunc
_get_random_offset() -> Vector2:var
x = randf_range(-intensity, intensity)
var
y = randf_range(-intensity, intensity)
return
Vector2(x, y)Així és com funciona la lògica de sacsejada:
- Connexió de senyal: a _ready, ens connectem al senyal OnUpdateHealth del jugador (com que la càmera és una filla del jugador, get_parent() fa referència al node del jugador).
- Disparador: Quan s'anomena _damage_shake, posem la intensitat a 3.
- Decaïment: A _process, fem servir lerpf per reduir gradualment la intensitat fins a 0 amb el temps. Això és el que finalment impedeix que la càmera tremoli.
- Desplaçament: sempre que la intensitat sigui superior a 0, calculem un desplaçament aleatori x i y i l'apliquem a la càmera. Això crea l'efecte de tremolor.
Torna a provar el joc. Ara, prendre danys hauria de sentir-se molt més significatiu amb el flaix vermell i la sacsejada de la càmera treballant a l'uníson.
Evitar la caiguda infinita
Com a darrer pas per a la lògica del jugador, abordem un problema comú als jocs de plataformes: caure del mapa. Actualment, si el jugador cau a una fossa, cau per sempre. Afegir una comprovació de seguretat evita que el joc es bloquegi en aquesta situació.
Afegirem una comprovació a l'script del reproductor per detectar si han caigut per sota d'un determinat llindar. Obriu player.gd i modifiqueu la funció _process per incloure aquesta comprovació:
if
global_position.y > 200: game_over()Aquesta lògica senzilla comprova la posició vertical del jugador. Si global_position.y supera els 200 (que està per sota del nivell del nostre nivell), activem la funció game_over per restablir el nivell.
Paral·laxi del fons
Ara centrem la nostra atenció en el medi ambient. Un fons estàtic pot fer que fins i tot un nivell ben dissenyat se senti pla. El nostre fons actual pot no cobrir tot el nivell si el jugador es mou molt cap a la dreta, tampoc. Arreglarem la mida, enrajolarem el fons per cobrir el nivell i implementarem un efecte de paral·laxi per simular la profunditat.
Canviar la mida i la posició
Abans d'enrajolar el fons, ens hem d'assegurar que cada sprite té la mida adequada i està correctament alineat. Seleccionem el BackgroundSprite a l'escena principal.
A l'Inspector, aneu a Transformar secció. Estableix el Escala a (0,5, 0,5) per reduir la pixelació i coincidir amb la resolució del nostre joc. Estableix el Posició a (0, 0) per centrar-lo.

Creació d'un fons repetible
Per assegurar-nos que el fons cobreix tot el nivell, hem de crear una franja perfecta d'imatges de fons.
Dupliqueu el BackgroundSprite (Ctrl+D). Mou el duplicat cap a la dreta ajustant la seva posició. Per als actius proporcionats en aquest projecte, un desplaçament de 512 píxels funciona perfectament.
Repetiu aquest procés, creant duplicats a les posicions +512, +1024, -512, etc., fins que el fons ocupi tota la longitud del vostre nivell.

Organitzacio dels nodes del fons
Tenir diversos nodes de sprites que desordena l'arbre de l'escena és desordenat. Agrupem-los sota un sol progenitor perquè siguin més fàcils de gestionar i es puguin moure junts.
Creeu un nou Node2D i anomeneu-lo Fons.


Hem d'assegurar-nos que el node Fons està situat a (0, 0).

Un cop col·locats correctament, seleccioneu tots els vostres nodes de sprite de fons i arrossegueu-los al node Fons per fer-los fills.

Implementació de l'script de paral·laxi
La paral·laxi és una tècnica on les capes de fons es mouen més lentament que les capes de primer pla, creant una il·lusió de distància. Fins i tot un sol
La capa de moviment més lent afegeix una sorprenent quantitat de profunditat a una escena 2D. Implementarem un script senzill per gestionar-ho.
Adjunteu un nou script al node Fons anomenat background_parallax.gd.

Afegiu el codi següent a l'script:
extends
Node2D# Controls how much slower the background moves relative to the player (0.7 = 70% speed)var
parallax : float = 0.7 @onready
var
player = $"./Player"func
_process (delta):# Offset the background by a fraction of the player's position to simulate depthglobal_position = player.global_position * parallax
Això és el que fa el codi:
- paral·laxi: Aquesta variable (0,7) defineix la relació de moviment. El fons es mourà al 70% de la velocitat del jugador.
- jugador: obtenim una referència al node del jugador.
- _procés: a cada fotograma, actualitzem la posició_global del fons perquè coincideixi amb la posició del jugador multiplicada pel factor de paral·laxi.
Deseu l'script i executeu el joc. A mesura que mous el personatge, ara el fons hauria de desplaçar-se suaument a un ritme diferent al de les plataformes, donant a l'escena una bona sensació de profunditat.
Àudio
Les imatges són només la meitat de l'experiència. Per polir realment el joc, necessitem so. La retroalimentació d'àudio reforça el que el jugador veu a la pantalla i fa que accions com recollir monedes o rebre un cop se sentin realment gratificants. Implementarem efectes de so per recollir monedes i fer malbé mitjançant el node AudioStreamPlayer que hem afegit anteriorment.
Inspeccio dels recursos d'àudio
Comencem per verificar els sons amb els quals hem de treballar. Navegueu a la carpeta d'àudio del vostre sistema de fitxers. Hauríeu de veure dos fitxers: Coin.wav i Take Damage.wav.

Nota de Godot: precàrrega vs càrrega
preload("res://path") loads a resource at compile time, embedding it into the script. The resource is available instantly with zero load delay, which is perfect for small assets like sound effects and textures that you know you will need. load("res://path") loads at runtime and can accept variable paths, but introduces a brief delay the first time it is called.Per als actius del joc que s'utilitzen sempre (com els nostres danys i els sons de les monedes),
preload is the right choice.Més informació: Referència de GDScript (https://docs.godotengine.org/en/4.6/tutorials/scripting/gdscript/gdscript_b asics.html#preload)
Reproducrem aquests sons mitjançant el node AudioStreamPlayer situat al nostre Jugador escena.

Actualitzacio de l'script del jugador
Hem de modificar player.gd per carregar aquests sons i reproduir-los en els moments adequats.
Primer, anem a obtenir una referència al reproductor d'àudio. Afegiu aquesta variable a la part superior del vostre script:
@onready
var
audio : AudioStreamPlayer = $AudioStreamPlayerA continuació, hem de carregar prèviament els fitxers de so a la memòria. Això garanteix que juguin a l'instant sense cap retard de càrrega. Afegeix aquestes variables:
var
take_damage_sfx : AudioStream = preload("res://Audio/take_damage.wav")var
coin_sfx : AudioStream = preload("res://Audio/coin.wav")Ara, creem una funció d'ajuda per reproduir un so específic. Aquesta funció assignarà el flux de so al reproductor i activarà la reproducció.
# Play a one-shot sound effect through the shared AudioStreamPlayerfunc
play_sound(sound : AudioStream): audio.stream = sound audio.play()Activar els sons
Finalment, hem de cridar play_sound a les nostres funcions de joc existents. Aquí és on tot s'uneix: ara cada acció produirà una resposta tant visual com auditiva.
Actualitzeu la funció take_damage per reproduir el so de dany:
func
take_damage(amount : int): health -= amount OnUpdateHealth.emit(health)_damage_flash() play_sound(take_damage_sfx)
# Defer game_over to avoid freeing nodes mid-physics stepif
health <= 0: call_deferred("game_over")A continuació, actualitzeu la funció augmente_score per reproduir el so de la moneda:
func
increase_score(amount : int): PlayerStats.score += amount OnUpdateScore.emit(PlayerStats.score) play_sound(coin_sfx)Reprodueix àudio
Executeu el joc per darrera vegada. Recull una moneda i topa amb un enemic. Hauríeu d'escoltar els efectes de so corresponents, acompanyats del flaix visual i el moviment de la pantalla que hem implementat anteriorment.
Amb aquestes addicions, el vostre joc hauria de sentir-se molt més polit i atractiu. En el següent capítol, treballarem per afegir un nivell addicional al nostre joc per mantenir-lo atractiu per als jugadors.
Disseny de nivells
En aquest capítol, ampliarem el nostre joc creant un segon nivell. Un joc de plataformes necessita més d'una etapa per mantenir els jugadors compromesos i, gràcies al nostre disseny modular, afegir contingut nou és un procés senzill. Duplicarem el nostre nivell existent, modificarem el seu disseny i imatges per crear un tema desert i ajustarem els comportaments dels enemics per oferir nous reptes.
També abordarem un error crític relacionat amb la nostra bandera final. Actualment, la transició d'escenes durant un esdeveniment de col·lisió pot provocar errors al motor físic de Godot. Ho presentarem com un repte que heu de resoldre, seguit d'un recorregut detallat de la solució.
Creacio de nivells nous
La manera més eficient de crear contingut addicional és basar-nos en el que ja tenim. Els desenvolupadors de jocs professionals sovint dupliquen i reelaboren els nivells existents en lloc de començar des de zero, ja que l'estructura bàsica (arbre de l'escena, capes de fons, capes de rajoles) ja està provada i funcionant. Seguirem el mateix enfocament: duplicarem el nostre primer nivell i transformarem-lo en alguna cosa nova.
Duplicar el primer nivell
El nostre primer pas és fer una còpia de l'escena de nivell existent. Això ens proporciona un punt de partida totalment funcional que podem transformar en una experiència totalment nova.
Per començar, navegueu a Escenes carpeta al moll FileSystem. Feu clic amb el botó dret a sobre Nivell 1 (o level_1.tscn) i seleccioneu Duplicat.

Godot demanarà un nom per al nou fitxer. Canvieu el nom a level_2.tscn i feu clic D'acord.

Seguiu endavant i feu doble clic al fitxer nou per obrir-lo. Ara teniu una còpia exacta del nivell 1 que serveix de base per al nivell 2.
Modificar el fons
Per donar-li al nou nivell una identitat diferent, canviarem l'entorn d'un bosc a un desert. Seleccioneu el BackgroundSprite node (i qualsevol altre node de fons que pugueu haver agrupat). A l'Inspector, podem arrossegar l'esprit de fons del desert des del Sprites carpeta a la Textura propietat.

Aquest senzill canvi diferencia immediatament el to visual del nivell.

Editeu el TileMap
Amb els antecedents al seu lloc, el següent pas és redissenyar el disseny físic perquè el nivell 2 ofereixi els seus propis reptes de plataformes. Seleccioneu el TileMapLayer node al moll d'escena.

Volem netejar l'antic traçat del bosc. Seleccioneu el Goma d'esborrar a la barra d'eines TileMap (o premeu E). Per accelerar les coses, podeu utilitzar el Rectangle eina de selecció per esborrar grans blocs de fitxes alhora.

Un cop el llenç estigui clar, canvieu a Pintar (o premeu P) i seleccioneu les fitxes amb temàtica del desert del vostre TileSet. Dibuixa un disseny nou que ofereixi diferents reptes de plataformes en comparació amb el primer nivell.

No dubteu a experimentar amb la verticalitat, les mides dels buits i la col·locació de la plataforma per crear un nivell que sigui divertit de navegar.

Posicionar elements del joc
Amb el terreny establert, hem de col·locar els nostres objectes interactius perquè el nivell sigui jugable. Cada element (la bandera final, les monedes i els enemics) té un propòsit de joc específic, de manera que la seva ubicació afecta directament la sensació del nivell.
Mourem el EndFlag node fins al final del vostre nou camí. Això defineix l'objectiu per al qual està treballant el jugador.

A continuació, reposicioneu les monedes per guiar el jugador a través del nivell o recompenseu l'exploració. Si necessiteu més monedes, simplement dupliqueu un node de moneda existent (Ctrl + D). Si en tens massa, elimina els extres.

Finalment, col·loquem els enemics. Com que hem exportat les variables de moviment al nostre script enemy.gd, podem personalitzar cada instància de l'enemic directament a l'Inspector sense tocar cap codi.
Per exemple, podeu fer que un enemic es mogui horitzontalment en lloc de verticalment configurant-lo Direcció de moviment El valor Y a 0 i el seu valor X a 300. També podeu ajustar el Velocitat de moviment per crear amenaces més ràpides o més lentes.

Aquesta flexibilitat us permet crear diferents escenaris de joc utilitzant la mateixa escena enemiga.

Canviar els colors del fons
Per distingir encara més els nivells visualment, podem tintar els sprites de fons utilitzant el Modular propietat. Això ens permet reutilitzar la mateixa textura però donar-li un estat d'ànim completament diferent (per exemple, una posta de sol o un tema nocturn).
Per accedir a aquesta configuració, seleccioneu els vostres sprites de fons. A l'Inspector, sota el Visibilitat (o CanvasItem), feu clic al quadre de color que hi ha al costat Modular.

Ajustem el color a alguna cosa que complementi el tema del desert, com ara un taronja o un groc càlids.

Podeu aplicar aquesta mateixa tècnica al nivell 1. Per exemple, podeu modular el fons del bosc amb una lleugera tonalitat verda per millorar l'atmosfera exuberant.


Preneu-vos un moment per revisar els dos nivells per assegurar-vos que se sentin diferents i ofereixen una bona progressió en dificultat.
Repte: modificar la bandera final
Actualment, si jugues al joc i xoques amb la bandera final, Godot pot produir el següent error al depurador:
end_flag.gd:9 @ _on_body_entered(): eliminar un node CollisionObject durant una devolució de trucada física no està permès i provocarà un comportament no desitjat. Elimina amb call_deferred() en lloc d'això.
Aquest error es produeix perquè la funció _on_body_entered s'activa durant un pas de física (quan Godot està calculant col·lisions). Intentar canviar l'escena, que implica suprimir els nodes del nivell actual, mentre el motor físic encara està processant aquest nivell no és segur.
El teu repte: Corregiu aquest error a l'script end_flag.gd perquè la transició de nivell estigui lliure d'errors.
Pista: Recordeu com vam gestionar la funció game_over a l'script del jugador quan el jugador va patir danys. Vam haver d'utilitzar un mètode específic per retardar l'acció fins que fos segura.
Preneu-vos un moment per intentar resoldre-ho pel vostre compte abans de llegir la solució següent.
Solucio del repte
Passem per la solució. La idea bàsica és el mateix patró que hem utilitzat anteriorment a l'script del reproductor: ajornar qualsevol modificació de l'arbre d'escenes fins que s'hagi acabat el pas de la física.
Per solucionar l'error amb el nostre Bandera final, hem de fer referència a com hem gestionat la funció take_damage al nostre script de reproductor. Quan vam trucar a game_over, vam embolicar la trucada a call_deferred perquè el motor esperés fins que acabés el pas de la física:
func
take_damage (amount : int):...
# Defer game_over to avoid freeing nodes mid-physics stepif
health <= 0: call_deferred("game_over")call_deferred indica a Godot que espere fins al final del fotograma actual (un cop s'hagin completat els càlculs físics) per executar la funció. Això evita que el motor intenti calcular la física dels objectes que s'estan suprimint.
Per aplicar aquest mateix patró a end_flag.gd, primer hauríem d'encapsular la lògica de càrrega d'escenes a la seva pròpia funció.
Obriu end_flag.gd i creeu una funció nova anomenada _load_new_scene:
func
_load_new_scene
(): get_tree().change_scene_to_packed(scene_to_load)Ara, actualitzeu la funció _on_body_entered per cridar aquesta nova funció
call_diferred:
func
_on_body_entered(body):if not
body.is_in_group("Player"):return# Defer the scene change to avoid modifying the tree during a physics callbackcall_deferred("_load_new_scene")
Amb aquest canvi, es detecta la col·lisió, s'acaba el marc i aleshores l'escenari canvia amb seguretat. Ara hauríeu de poder completar els nivells sense cap error.
Enhorabona per crear un joc de plataformes 2D complet i multinivell! En el següent capítol, crearem un menú principal per lligar-ho tot.
Creació del menú principal
En aquest capítol final del projecte, configurarem l'escena del menú principal per al nostre joc. El menú és la primera pantalla que veuen els jugadors quan inicien l'aplicació, de manera que serveix com a porta d'entrada a tota l'experiència de joc. Comptarà amb un botó "Jugar" per iniciar el joc i un botó "Surt" per tancar-lo.
Construirem aquesta interfície utilitzant els nodes de la interfície d'usuari de Godot, l'estilarem amb un fons i fonts personalitzades i escriurem un script per gestionar les interaccions dels botons. Finalment, integrarem el menú en el bucle del joc perquè en completar el joc o perdre'l torni el jugador a aquesta pantalla.
Creacio de l'escena del menu
Cada pantalla diferent d'un projecte Godot viu en la seva pròpia escena. El nostre menú no és una excepció, així que ara crearem una escena d'IU dedicada.
A la barra de menú superior, feu clic a Escena i seleccioneu Nova Escena.

En el Crea un node arrel diàleg, seleccioneu Interfície d'usuari. Això crea un control
node com a arrel, que és el tipus base per a tots els elements d'IU a Godot.

Canvieu el nom del node arrel a Menú.

Finalment, deseu la nova escena com a menu.tscn dins del vostre Escenes carpeta.

Afegir una imatge de fons
Un fons gris senzill no és molt acollidor, així que afegim una imatge de fons per definir el to visual del menú. Crea una nova TextureRect node com a fill del node Menú.

En el Sistema de fitxers dock, navegueu a la carpeta Sprites/Backgrounds i arrossegueu la imatge forest_background.png a la Textura propietat del node TextureRect a l'Inspector.

Volem que aquest fons cobreixi tota la pantalla, independentment de la mida de la finestra. Per aconseguir-ho, utilitzarem ancoratges.
- Seleccioneu el node TextureRect.
- A la barra d'eines a la part superior de la finestra gràfica, feu clic a Anchor Preset botó (la icona del cercle verd).
- Seleccioneu el Full Rect opció (la icona inferior dreta al menú desplegable).

Nota de Godot: àncores i interfície d'usuari sensible
Les àncores defineixen com es posiciona un node de control i es redimensiona en relació al seu pare. Cada àncora és un valor entre 0 i 1 que representa un percentatge de la mida del pare. Una àncora de (0, 0) significa la cantonada superior esquerra; (1, 1) significa la part inferior dreta.
El preajust "Full Rect" estableix les quatre àncores perquè el node s'estingui per omplir completament el seu pare, per això la nostra imatge de fons cobreix tota la pantalla. El preajust "Centre" situa el node al punt mitjà del pare, que és perfecte per a títols i botons.
Més informació: Mida i ancoratges (https://docs.godotengine.org/en/4.6/tutorials/ui/size_and_anchors.html)
A continuació, hem d'assegurar-nos que la imatge s'escala correctament sense distorsió. A l'Inspector, localitzeu el Mode d'expansió propietat i establiu-la Ajust a l'alçada. Això garanteix que la imatge canviï de mida en funció de la resolució vertical mantenint la seva relació d'aspecte.

Canvieu el nom del node TextureRect a Fons per mantenir la nostra escena organitzada.

El vostre fons ara hauria d'omplir tota l'àrea de la pantalla, tal com es mostra a continuació.

Afegir un titol
Amb el fons al seu lloc, afegim el títol del joc perquè els jugadors sàpiguen a què estan a punt de jugar. Crea una nova Etiqueta node com a fill del node Menú.

Canvieu el nom d'aquest node a Títol.

A l'Inspector, configureu el Text propietat a Joc de plataformes 2D (o el teu títol preferit).

Per centrar el títol a la pantalla, tornarem a utilitzar àncores.
- Desplaceu-vos cap avall fins a Disseny secció a l'inspector.
- Canvia el Mode de disseny a Àncores.
- Estableix el Àncores preestablerts a Centre.

Això centra el node en si, però el text dins del node encara pot estar alineat a l'esquerra. Per solucionar-ho, canvieu tots dos Alineació horitzontal i Alineació vertical propietats a Centre.

Ara el text està col·locat correctament, però és petit i senzill.

Per estilitzar-lo, localitza el Configuració de l'etiqueta propietat a l'inspector. A continuació, seleccioneu el menú desplegable i trieu Nova configuració de l'etiqueta.

Feu clic al nou recurs per ampliar-ne les propietats. Configureu els paràmetres següents per fer que el títol destaqui (no dubteu a experimentar, però, i seleccioneu altres paràmetres si creieu que funcionen millor):
- Mida de la lletra: 48 píxels
- Color de lletra: Un groc clar (o un color que contrasti amb el vostre fons).
- Mida del contorn: 5 píxels
- Color del contorn: Un verd bosc (o color fosc).
- Mida de l'ombra: 5 píxels

El títol ara hauria de ser gran, llegible i visualment atractiu.

Afegir botons
Ara necessitem els elements interactius que permetin als jugadors començar el joc o sortir. Crea una nova Botó node com a fill del node Menú.

Canvieu el nom d'aquest node a PlayButton.

Per a aquest botó, configureu el Text propietat a Jugar.

Igual que el títol, volem centrar aquest botó a la pantalla. En el Disseny secció, canvieu el Mode de disseny a Àncores i el Àncores preestablerts a Centre.

A la finestra gràfica, arrossegueu el botó cap avall perquè quedi a sota del títol. També podeu canviar la mida per fer-lo més gran i fer-lo més fàcil de fer clic.

Per crear el botó Surt, només heu de duplicar el botó de reproducció (Ctrl + D) i canviar el nom de la còpia a QuitButton.

A continuació, canvieu el Text propietat del botó nou a Surt.

Finalment, col·loqueu el QuitButton a sota del PlayButton.

Programacio del menu
Tenim les imatges al seu lloc, però els botons encara no fan res. Adjuntem un script per connectar la lògica del botó.
Seleccioneu el node del menú arrel i adjunteu un script nou. Deseu-lo com a menu.gd al fitxer Guions carpeta.

Utilitzarem senyals per detectar quan es prem els botons. Com a actualització, els senyals permeten a un node notificar a altres parts de l'escena que ha passat alguna cosa, sense que aquestes parts hagin de comprovar cada fotograma.
- Seleccioneu el botó de reproducció.
- Aneu a la Node acobla i fes doble clic al senyal premut().

- Connecteu-lo al node Menú. Això crearà el
funció _on_play_button_pressed al vostre script.

Repetiu aquest procés per al QuitButton, connectant el seu senyal pressed() a l'script per crear _on_quit_button_pressed.

Ara, afegiu la lògica a aquestes funcions:
extends
Controlfunc
_on_play_button_pressed():# Reset persistent score before starting a new gamePlayerStats.score = 0 get_tree().change_scene_to_file("res://Scenes/level_1.tscn")func
_on_quit_button_pressed(): get_tree().quit()- Botó de reproducció: Reiniciem la puntuació (utilitzant el nostre singleton PlayerStats) i canviem l'escena a level_1.tscn.
- Botó Surt: Cridem get_tree().quit() per tancar l'aplicació.
Integrar el menú al bucle del joc
Per completar el flux del joc, hem d'assegurar-nos que el jugador pot accedir al menú i que els nivells passen correctament. Al final d'aquesta secció, el joc passarà perfectament des del menú a través dels dos nivells i de nou.
Provar el menu
Abans d'ajustar els nostres nivells, assegurem-nos que el menú en si funciona. Executeu l'escena del menú directament (premeu F6).
- Feu clic Surt per confirmar que el joc es tanca.
- Torna-ho a executar i fes clic Jugar per confirmar que es carrega el nivell 1.
Si tot es veu bé aquí, podem treballar amb la resta del bucle.
Connectar nivells
Els nostres següents passos són enllaçar els nostres nivells mitjançant la lògica EndFlag que vam crear als capítols anteriors.
Obert Nivell 1 i seleccioneu el EndFlag node. A Inspector, assigneu
level_2.tscn al Escena per carregar propietat.

Igualment, obert Nivell 2 i seleccioneu-ne EndFlag. Assigna menu.tscn al fitxer Escena per carregar propietat. Això crea un bucle complet: Menu -> Level 1 -> Level 2
-> Menu.

Si haguéssiu de crear més nivells, com a repte per a vosaltres mateixos, simplement els connectaríeu d'una manera similar.
Actualitzar la logica de Game Over
Finalment, quan el jugador perd, volem tornar-los al menú principal en lloc de simplement tornar a carregar el nivell.
Obriu l'script player.gd i actualitzeu la funció game_over:
func
game_over(): get_tree().change_scene_to_file("res://Scenes/menu.tscn")Ara, si el jugador es queda sense salut o cau del mapa, es tornarà a enviar a la pantalla de títol per tornar-ho a provar.
Ara tens un bucle de joc totalment funcional amb un menú principal polit! Amb això, el nostre projecte ja està enllestit.
Paraules finals
Felicitats. Has arribat al final d'aquest llibre, però el més important és que has arribat al començament del teu viatge com a desenvolupador de jocs.
Llegir un llibre tècnic és un compromís, però seguir i escriure el codi tu mateix és un èxit important. Vau començar amb una carpeta buida i una pantalla en blanc. Ara, tens un joc de plataformes en 2D totalment funcional amb un personatge jugable, enemics, un sistema de puntuació i una interfície d'usuari polida. Aquesta transformació és l'essència del desenvolupament del joc.
Abans de tancar aquest llibre i obrir un projecte nou, dediquem un moment a reflexionar sobre el conjunt d'eines que heu creat i a veure on podeu anar a continuació.
El que has aconseguit
Heu fet més que copiar codi; has après els patrons fonamentals que impulsen Godot. Tot i que ens hem centrat en un gènere específic, les habilitats que has practicat s'apliquen a gairebé qualsevol joc que crearàs en el futur.
Has dominat el sistema d'escenes. Vas aprendre que a Godot tot és una escena. Vas crear objectes complexos com el jugador i l'enemic combinant nodes especialitzats més petits. També heu après a estructurar aquestes escenes de manera dinàmica, cosa que us permet omplir nivells amb centenars d'objectes sense reescriure codi per alterar configuracions específiques.
Vas controlar el motor de la física. En treballar amb CharacterBody2D i Area2D, heu après a gestionar les col·lisions, la gravetat i els vectors de moviment. Vas anar més enllà de la simple animació per crear un personatge que se senti sensible i fonamentat en el món del joc.
Heu connectat sistemes amb senyals. Una de les habilitats més crítiques de Godot és desacoblar el vostre codi. Vau utilitzar senyals per fer saber a la interfície d'usuari quan va canviar la salut o quan el jugador va recollir una moneda, assegurant-vos que la vostra lògica de joc es mantingués neta i modular.
Has polit l'experiència. Un joc és més que lògica. Heu après a utilitzar AnimationPlayer, AudioStreamPlayer i els efectes de sacsejada de la càmera
afegeix "suc" al teu projecte. Aquests detalls converteixen un prototip funcional en una experiència atractiva.
Cap on continuar
La transició de seguir un tutorial a crear les teves pròpies idees pot resultar descoratjador. La millor manera de superar aquesta bretxa és fer passos petits i concrets.
Aquí teniu quatre maneres de continuar el vostre creixement.
Amplia aquest projecte
La manera més senzilla de practicar és modificar el codi que ja enteneu. Prova d'afegir una funció nova al joc que acabes d'acabar:
- Afegeix un mecànic nou: Dóna al jugador un doble salt o una habilitat de guió.
- Crea un nou enemic: Construeix un enemic que dispara projectils o vola en lloc de caminar.
- Afegiu un estat "Guanyar": Crea un nivell final amb un cap o un trofeu específic que activa la pantalla "Tu guanyes".
Participar en un joc jam
Els embussos de joc són esdeveniments en què creeu un joc des de zero en un temps limitat (normalment de 48 hores a una setmana) basat en un tema. Aquesta és una de les maneres més ràpides d'aprendre. La limitació de temps us obliga a centrar-vos en acabar un projecte en lloc de perfeccionar-lo.
Llocs web com itch.io allotgen desenes de confitures cada setmana. Busqueu melmelades que donen la benvinguda explícita als principiants.
Explora la documentació
Aquest llibre cobria l'essencial, però Godot és vast. La documentació oficial és el teu millor amic. Sempre que us pregunteu "Puc fer X?", la resposta sol estar a la referència de classe.
Preneu l'hàbit de llegir la documentació dels nodes que encara no heu utilitzat. És possible que descobriu un node RayCast2D o Timer que resol un problema que us costava solucionar manualment.
Visiteu la documentació aquí: docs.godotengine.org (https://docs.godotengine.org)
Reconstrueix-ho sense la guia
Intenta recrear la mecànica bàsica del moviment d'aquest joc en un projecte nou sense mirar el codi font. Probablement us quedareu atrapats, i això és bo. Quan heu de buscar la solució o llegir vosaltres mateixos els missatges d'error, el coneixement es manté.
Continua aprenent
Si preferiu l'aprenentatge pràctic i centrat en projectes a Godot i la programació, consulteu la nostra biblioteca de cursos completa a Zenva Academy (https://academy.zenva.com). El catàleg inclou més de 300 cursos i més de 40 vies d'aprenentatge, orientant els estudiants des dels fonaments bàsics fins a temes avançats.
Reflexions finals
El desenvolupament del joc és una pràctica de perseverança. Hi haurà errors que us deixaran caure durant dies. Hi haurà funcions que resulten més difícils del que semblaven. Això és normal. Tots els desenvolupadors professionals, des de creadors independents fins a veterans de l'AAA, s'enfronten a aquests mateixos reptes.
La diferència és que ara teniu un flux de treball. Sabeu com dividir un gran problema en petits nodes, com escriure el seu comportament i com combinar-los en una escena.
Tens les eines. Tens el coneixement. Ara, ves a construir alguna cosa divertida.
Referència completa del codi font
En aquesta darrera secció, proporcionem el codi font complet de tots els scripts creats al llarg del desenvolupament de 2D Platformer. Aquesta referència està pensada per ajudar-vos a depurar el vostre propi codi, verificar la vostra lògica o servir com a base per expandir el projecte amb les vostres pròpies característiques.
Si necessiteu comprovar la configuració o els actius de l'escena, recordeu que podeu descarregar els fitxers complets del projecte mitjançant l'enllaç que hi ha a la Introducció capítol.
background_parallax.gd
Camí del fitxer: res://Scripts/background_parallax.gd
Aquest script gestiona l'efecte de desplaçament de paral·laxi per al fons. Calcula la posició del fons multiplicant la posició global del jugador per un factor de paral·laxi (0,7), fent que el fons es mogui més lentament que el primer pla per crear una sensació de profunditat.
extends
Node2D# Controls how much slower the background moves relative to the player (0.7 = 70% speed)var
parallax : float = 0.7 @onready
var
player = $"./Player"func
_process (
_delta
):# Offset the background by a fraction of the player's position to simulate depthglobal_position = player.global_position * parallax
camera_shake.gd
Camí del fitxer: res://Scripts/camera_shake.gd
Adjunt al node Camera2D, aquest script crea un efecte de sacsejada de la pantalla quan el jugador pateix danys. Escolta el senyal OnUpdateHealth del reproductor. Quan s'activa, estableix un valor d'intensitat que disminueix amb el temps, aplicant un desplaçament aleatori a la posició de la càmera a cada fotograma.
extends
Camera2Dvar
intensity : float = 0func
_ready ():# Listen for damage events from the parent Player nodeget_parent().OnUpdateHealth.connect(_danyar_agitar)
# Kick off a shake whenever health changesfunc
_damage_shake
(
_health
: int): intensity = 3func
_process (delta):if
intensity > 0:# Gradually reduce the shake strength each frameintensity = lerpf(intensity, 0, delta * 10) offset = _get_random_offset()
# Generate a random camera offset within the current intensity rangefunc
_get_random_offset
() -> Vector2:var
x = randf_range(-intensity, intensity)
var
y = randf_range(-intensity, intensity)
return
Vector2(x, y)coin.gd
Camí del fitxer: res://Scripts/coin.gd
Aquest script controla el comportament visual i la lògica de col·lisió de les monedes col·leccionables. Utilitza una ona sinusoïdal basada en el temps del sistema per girar el sprite (mitjançant l'escala) i pujar-lo cap avall. Quan el jugador entra a l'àrea de la moneda, activa l'augment de la puntuació i elimina la moneda de l'escena.
extends
Area2Dvar
rotate_speed : float = 3.0
var
bob_height : float = 5.0
var
bob_speed : float = 5.0@onready
var
start_pos : Vector2 = global_position @onready
var
sprite : Sprite2D = $Spritefunc
_physics_process(
_delta
):var
time = Time.get_unix_time_from_system()# Simulate a spinning effect by oscillating the horizontal scale with a sine wavesprite.scale.x = sin(time * rotate_speed)
# Bob the coin up and down using a sine wave mapped to a 0-to-bob_height rangevar
y_pos = ((1 + sin(time * bob_speed)) / 2) * bob_height global_position.y = start_pos.y - y_posfunc
_on_body_entered(body):if not
body.is_in_group("Player"):returnbody.increase_score(1) queue_free()
end_flag.gd
Camí del fitxer: res://Scripts/end_flag.gd
Aquest script gestiona la lògica de finalització del nivell. Exporta una variable PackedScene, que us permet assignar el següent nivell directament a l'Inspector. Quan el jugador entra a l'àrea de la bandera, utilitza call_deferred per carregar de manera segura la nova escena, evitant errors físics durant la transició.
extends
Area2D@export
var
scene_to_load : PackedScenefunc
_on_body_entered(body):if not
body.is_in_group("Player"):return# Defer the scene change to avoid modifying the tree during a physics callbackcall_deferred("_load_new_scene")
func
_load_new_scene
(): get_tree().change_scene_to_packed(scene_to_load)enemy.gd
Camí del fitxer: res://Scripts/enemy.gd
Aquest script controla la IA enemiga. Mou l'enemic cap endavant i cap enrere entre la seva posició inicial i una posició objectiu definida per move_direction. També detecta col·lisions amb el jugador i causa danys.
extends
Area2D@export
var
move_direction : Vector2 @export
var
move_speed : float = 20@onready
var
start_pos : Vector2 = global_position@onready
var
target_pos : Vector2 = global_position + move_directionfunc
_ready ():$AnimationPlayer.play("fly")
func
_physics_process(delta):# Move steadily toward the current target positionglobal_position = global_position.move_toward(target_pos, move_speed * delta)
# When the enemy arrives, swap the target to create a patrol loopif
global_position == target_pos:if
target_pos == start_pos:target_pos = start_pos + move_direction
else:
target_pos = start_pos
func
_on_body_entered(body):if not
body.is_in_group("Player"):returnbody.take_damage(1)
menu.gd
Camí del fitxer: res://Scripts/menu.gd
Aquest script gestiona les interaccions del menú principal. Es connecta als botons "Reproduir" i "Surt". Prement "Reproduir" restableix la puntuació global i carrega el primer nivell, mentre que prement "Surt" tanca l'aplicació.
extends
Controlfunc
_on_play_button_pressed():# Reset persistent score before starting a new gamePlayerStats.score = 0 get_tree().change_scene_to_file("res://Scenes/level_1.tscn")func
_on_quit_button_pressed(): get_tree().quit()player.gd
Camí del fitxer: res://Scripts/player.gd
Aquest és el guió bàsic del personatge del jugador. Gestiona el moviment basat en la física (gravetat, acceleració, fricció, salt), gestiona la salut i la puntuació, reprodueix animacions i efectes de so i gestiona l'estat del joc. També emet senyals (OnUpdateHealth, OnUpdateScore) per actualitzar la IU.
extends
CharacterBody2Dsignal
OnUpdateHealth (health : int)
signal
OnUpdateScore (score : int)@export
var
move_speed : float = 100 @export
var
acceleration : float = 50 @export
var
braking : float = 20 @export
var
gravity : float = 500 @export
var
jump_force : float = 200@export
var
health : int = 3var
move_input : float@onready
var
sprite : Sprite2D = $Sprite@onready
var
anim : AnimationPlayer = $AnimationPlayer @onready
var
audio : AudioStreamPlayer = $AudioStreamPlayervar
take_damage_sfx : AudioStream = preload("res://Audio/take_damage.wav")var
coin_sfx : AudioStream = preload("res://Audio/coin.wav")func
_physics_process(delta):# Apply gravity only when airborneif not
is_on_floor():velocity.y += gravity * delta
move_input = Input.get_axis("move_left", "move_right")
# Smoothly accelerate toward target speed, or brake to a stopif
move_input != 0:velocity.x = lerp(velocity.x, move_input * move_speed, acceleration * delta)
else:
velocity.x = lerp(velocity.x, 0.0, braking * delta)
# Only allow jumping while groundedif
Input.is_action_pressed("jump")
and
is_on_floor(): velocity.y = -jump_forcemove_and_slide()
func
_process(
_delta
):# Flip the sprite to face the direction of movementif
velocity.x != 0:sprite.flip_h = velocity.x > 0
# Trigger game over if the player falls off the mapif
global_position.y > 200: game_over()_manage_animation()
# Select the correct animation based on the player's current statefunc
_manage_animation
():if not
is_on_floor(): anim.play("jump")
elif
move_input != 0: anim.play("move")else:
anim.play("idle")
func
take_damage (amount : int):health -= amount OnUpdateHealth.emit(health)
_damage_flash() play_sound(take_damage_sfx)
# Defer game_over to avoid freeing nodes mid-physics stepif
health <= 0: call_deferred("game_over")func
game_over (): get_tree().change_scene_to_file("res://Scenes/menu.tscn")func
increase_score (amount : int): PlayerStats.score += amount OnUpdateScore.emit(PlayerStats.score) play_sound(coin_sfx)# Briefly tint the sprite red to signal damagefunc
_damage_flash
(): sprite.modulate = Color.REDawait get_tree().create_timer(0.05).timeout sprite.modulate = Color.WHITE
# Play a one-shot sound effect through the shared AudioStreamPlayerfunc
play_sound (sound : AudioStream): audio.stream = soundaudio.play()
player_stats.gd
Camí del fitxer: res://Scripts/player_stats.gd
Aquest és un script de càrrega automàtica (Singleton) que persisteix durant els canvis d'escena. Emmagatzema la puntuació del jugador perquè no es reiniciï quan es recarregui o canviï un nivell.
extends
Nodevar
score : int = 0player_ui.gd
Camí del fitxer: res://Scripts/player_ui.gd
Aquest script gestiona la pantalla d'atenció al públic (HUD). Es connecta als senyals d'actualització del jugador per actualitzar la pantalla de salut (mostrant/amagant les icones del cor) i l'etiqueta de text de la puntuació en temps real.
extends
CanvasLayer@onready
var
health_container = $HealthContainer
var
hearts : Array = []@onready
var
score_text : Label = $ScoreText @onready
var
player = get_parent()func
_ready ():hearts = health_container.get_children()
# Subscribe to the player's health and score signals
player.OnUpdateHealth.connect(
_update_hearts
) player.OnUpdateScore.connect(
_update_score
)# Show the correct starting values on the first frame_update_hearts(player.health)
_update_score(PlayerStats.score)
# Show or hide each heart icon based on current healthfunc
_update_hearts
(health : int):for
i
in
len(hearts): hearts[i].visible = i < healthfunc
_update_score
(score : int): score_text.text = "Score: " + str(score)