La Nau i les Matemàtiques
En aquest capítol crearem la nostra nau i farem que es mogui. Però no copiarem un script. Entendrem per què es mou. I per això, hem de parlar de la nostra amiga (o enemiga): La Fletxa (El Vector).
Necessites gràfics?
Per seguir aquest curs, necessitaràs una nau. Si no en tens una a mà, et recomanem encaridament el pack gratuït de Kenney Space Shooter Extension. És net, bonic i perfecte per aprendre.
Necessites editar les imatges? No cal piratejar res. Tens la suite Affinity Studio disponible de forma gratuïta a la web.
1. L’Escenari (Pla Cartesià)
El teu monitor és una quadrícula de píxels.
- X (Horitzontal): Creix cap a la dreta.
- Y (Vertical): Creix cap a BAIX.
Compte amb la Y!
A les matemàtiques de l’escola, la Y creix cap amunt. En Game Dev, la Y creix cap avall (perquè comencem a dibuixar des de la cantonada superior esquerra). No ho oblidis o la teva nau volarà cap per avall.
2. L’Elecció del Node (La Teva Nau)
A la intro vam dir que “Tot és una Escena”. Cert, però… de què està feta una escena? De Nodes. Si l’Escena és la casa, els Nodes són els maons. I hi ha molts tipus de maons:
- Node2D: És la base per al món 2D. Té posició
(x, y), rotació i escala. (Si estiguéssim en 3D seriaNode3D, i per a interfíciesControl, però aquí vivim al pla). - RigidBody2D: Té física real (gravetat, rebots). Si li dones una puntada de peu, surt volant. Difícil de controlar per a un personatge principal.
- CharacterBody2D: El terme mig perfecte. Té posició (com Node2D) i sap xocar (com RigidBody), però no es mou sol. TU ets el motor. És l’estàndard per a personatges en jocs 2D.
Prepara els teus Assets
Abans de començar, necessites una nau. Pots dibuixar-ne una o utilitzar aquesta de prova:

Clic dret -> Desa la imatge.
Crèdits: PSX Plane per Puck.
- Crea una carpeta anomenada
spritesal teu FileSystem. - Arrossega aquesta imatge a dins.
Passos:
- A l’escena buida que apareix per defecte:
- Al panell de “Create Root Node”, selecciona Other Node.
- Escriu
CharacterBody2Dal cercador i fes-li doble clic. Això crearà una nova escena amb aquest node com a arrel. Anomena’lPlayer. - El node es queixarà amb una alerta ⚠️. T’està dient: “Ei, tinc cos físic però no tinc forma! Sóc un cercle? Un quadrat?”.
- Afegeix com a fill de
CharacterBody2Dun node anomenatCollisionShape2D.- A l’Inspector, on diu Shape, selecciona
New CircleShape2D. - Important: Fes clic sobre el requadre que posa “CircleShape2D” (s’expandirà).
- A Radius, escriu
40 px. Això ajustarà la mida exacta sense escalar el node.
- A l’Inspector, on diu Shape, selecciona
- Afegeix un altre node fill
Sprite2Dperquè es vegi bonic (arrossega la teva imatge de la nau a la propietat Texture). - Crea una carpeta anomenada
scenesa l’arrel del projecte i desa-hi l’escena com aplayer.tscn.
Regla d'or: Hitbox vs Sprite
En Game Design, hi ha una regla no escrita: “Més val que en sobri que no que en falti”.
- Si la teva col·lisió és més gran que la nau, el jugador cridarà “Però si no m’ha tocat!” quan mori per l’aire. Això se sent injust.
- Si la teva col·lisió és una mica més petita, permets que les bales “freguin” la nau sense matar-la. Això genera moments de “Ufff, gairebé!” que se senten èpics.
Dona-li sempre una mica d’avantatge al jugador. Fes el CollisionShape un 10-20% més petit que el dibuix visible.
3. Conceptes Bàsics de GDScript
Abans d’escriure codi, necessites saber llegir el mapa. Si no has programat mai, això és tot el que necessites saber per ara:
-
extends(Herència): És la primera línia. Li diu a Godot: “Sóc una extensió de…”.extends CharacterBody2D: Si estens de com en aquest casCharacterBody2Dsignificarà que ara l’script té tots els mètodes i propietats d’un CharacterBody2D, més el que afegeixis.
Això es diu Herència de Classes. És un concepte fonamental de programació orientada a objectes. El teu script no comença de zero: hereta un “vehicle” ja funcional amb rodes, motor i volant (
CharacterBody2D), i tu només afegeixes la personalització (la teva lògica de moviment). -
func(Acció/Verb): És una ordre o tasca. Agrupa una llista de passos.func _physics_process(delta): És una funció especial que Godot crida a un ritme fix (per defecte 60 vegades per segon). Tot el que escriguis a dins, es repetirà en bucle per moure coses.
-
var(Variable): És un lloc on guardem informació a la memòria RAM de l’ordinador.var speed = 300: Hem reservat un lloc a la memòria, li hem posat l’etiqueta “speed” i hem guardat el número 300 a dins.
Perill: Tabs vs Espais
GODOT NO PERDONA. La “sagnia” (aquests espais a l’esquerra) defineix quin codi està dins de quina funció.
- Utilitza sempre Tabulacions (Tecla Tab ↹), no espais.
- Mai barregis tabs i espais al mateix fitxer o tindràs errors invisibles.
- Nota: El botó de copiar d’aquesta web intenta convertir espais a Tabs automàticament, però si veus errors vermells, revisa això primer.
4. Creant l’Script
La nostra nau és bonica, però no fa res. Necessitem un Script.
- Crea una carpeta a l’arrel anomenada
scripts. - Selecciona el node arrel
Player. - Fes clic a la icona del Pergamí amb un
+verd (a sobre del panell d’escena). O fes clic dret -> Attach Script. - Apareixerà una finestra.
- Template: Deixa-ho en
CharacterBody2D: Basic Movementsi vols veure molt codi complex, o posaEmptyper començar net. Nosaltres ho esborrarem tot, així que és igual. - Path: Aquí està la clau. No el llencis a l’arrel. Escriu
res://scripts/player.gd. - Dona-li a Create.
Ara veuràs un editor de text. Benvingut al teu primer programa!
5. El Problema de “Moure’s”
Per moure la nau, necessitem dir-li cap a on anar. Aquí entren els Vectors.
Un vector no és més que una fletxa. En videojocs, quan aquesta fletxa ens indica “cap a on”, l’anomenem Vector de Direcció.
- Si toques Dreta, el vector apunta a
(1, 0). - Si toques Avall, el vector apunta a
(0, 1).
En Godot, això es diu Input.get_vector().
Però obtenir el vector no mou la nau. Només ens diu “cap a on vol anar el jugador”.
Per moure-la, necessitem dues coses màgiques que viuen dins de CharacterBody2D:
velocity(Velocitat): És una variable interna. Imagina-la com el velocímetre de la nau. Tu li dius “Posa’t a 300 km/h seguint aquest vector”.move_and_slide(): Aquesta funció és el motor. Li dius “Arrenca!”. I ella sola busca la variablevelocityi mou la nau aquesta quantitat, xocant si cal.
No has de passar-li velocity a move_and_slide(). Ho fa sol.
Aquí tens el codi complet. Et recomano escriure’l a mà per memoritzar les comandes, però si tens pressa, pots copiar-lo.
extends CharacterBody2D
func _physics_process(delta):
var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = direction * 400
move_and_slide()extends CharacterBody2D
func _physics_process(delta):
var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = direction * 400
move_and_slide()Desglossament línia per línia:
1. extends CharacterBody2D
Ja sabem que això és herència. Aquí ho usem concretament per desbloquejar la variable velocity. Si estenguéssim d’un simple Node2D, aquesta variable no existiria i hauríem de calcular la física manualment. En heretar de CharacterBody2D, guanyem tot el sistema de col·lisions sense haver de reescriure a mà tota una lògica que ja ens dona CharacterBody2D.
2. func _physics_process(delta):
Usem el bucle de físiques (i no el _process normal) perquè move_and_slide() necessita una estabilitat perfecta per calcular xocs. Si uséssim el loop visual, la nau podria travessar parets si el joc va lent. Aquí ens assegurem que el moviment sigui sòlid com una roca.
3. var direction = Input.get_vector(...)
| Tecla/es premuda/es | Valor de direction |
|---|---|
| Cap | (0, 0) |
| Dreta | (1, 0) |
| Avall | (0, 1) |
| Dreta + Avall | (0.707, 0.707) ← Normalitzat! |
Input.get_vector() llegeix les tecles i construeix un vector de direcció. Ja ve normalitzat (magnitud 1), així que moure’s en diagonal no és més ràpid que moure’s recte.
Però què és “move_right”? Si intentes executar el codi ara, no funcionarà. Godot no sap quines tecles són aquestes. Has de configurar-les tu per definir què significa “moure a la dreta” (pot ser la tecla D, la Fletxa Dreta o el Joystick).
Configura-ho ara:
- Ves a Project > Project Settings > Input Map.
- Escriu
move_righta la barra “Add New Action” i prem Add. - Busca la nova acció a la llista de sota i prem el botó + a la seva dreta.
- Prem la tecla D (i opcionalment la tecla Fletxa Dreta) i dona-li a OK.
- Repeteix el procés per a la resta:
move_left: Tecla A.move_up: Tecla W.move_down: Tecla S.
Si vols, pots addicionalment en cada acció afegir per exemple el joystick (stick esquerre) i al creueta (d-pad).
4. velocity = direction * 300
La direcció és un vector de magnitud 1. En multiplicar per 300, obtenim “300 píxels per segon en aquesta direcció”. És com dir: “Apunta cap allà i ve a 300 de velocitat”.
5. move_and_slide()
Aquesta funció llegeix velocity automàticament (no cal passar-li) i mou el node. A més, gestiona col·lisions: si hi ha una paret, “llisca” en lloc de quedar-se enganxat. Per això es diu “move and slide”.
4. Per què corres més en diagonal? (Pitàgores)
Si prems Dreta (1, 0) i Avall (0, 1) alhora, Godot suma els vectors. El resultat és (1, 1).
Quant mesura aquest vector (1, 1)? Aquí és on entra el nostre amic grec.
Imagina que ets en una ciutat quadriculada (com l’Eixample de Barcelona o Manhattan):
- Caminar 1 carrer a l’Est costa 1 minut.
- Caminar 1 carrer al Sud costa 1 minut.
- Si camines Est i després Sud, trigues 2 minuts en total.
Però, i si travesses el carrer en diagonal? Aquí entra el senyor Pitàgores amb el seu famós Teorema. No t’espantis, anem a disseccionar-lo peça a peça:
Què significa cada lletra?
- (Hipotenusa): És la distància llarga, la longitud (magnitud) del vector.
- i (Catets): Són els costats rectes. En el nostre cas, el moviment en X (1 pas) i en Y (1 pas).
Treu la calculadora! De debò, fes-ho. Anem a substituir les lletres per els nostres números (1 dreta, 1 avall):
-
Elevem al quadrat: significa . Segueix sent .
Resultat:
-
Sumem: .
Resultat:
-
Arrel quadrada: Busca el botó a la teva calculadora del mòbil i posa .
Resultat:
Diccionari Gamer 🤓
En videojocs i matemàtiques, a aquesta longitud li diem Magnitud o Mòdul del vector. També és coneguda com Distància Euclidiana (la línia recta entre dos punts).
Quan fem input_vector.length() en Godot, el motor fa aquest càlcul de Pitàgores per tu.
Aquí ho tens! Si no fem res, la teva direcció té una força de 1.41. Comparat amb la força de 1.0 quan vas recte, estàs corrent un 41% més ràpid!
Simulador
(0, 0)0.000💤 Nave parada.
Això és un problema clàssic (“Speedrunning hack”). En un joc competitiu, tothom aniria en diagonal per arribar abans.
La Solució: Normalitzar
Normalitzar un vector significa: “No m’importa com de llarg sigui el vector (la fletxa), talla’l perquè mesuri exactament 1, però mantingues la direcció”.
Godot és llest. Input.get_vector() JA normalitza el resultat per defecte.
És important saber-ho perquè en matemàtiques de shaders o IA, hauràs d’utilitzar .normalized() manualment moltes vegades quan només t’importi la direcció () i no la força.
5. Què dimonis és delta?
Veuràs delta en tots els tutorials.
position += velocity * deltaposition += velocity * deltaImagina que el teu joc va a 60 FPS (imatges per segon). El teu codi s’executa 60 vegades per segon. Ara imagina que al teu amic li va a 144 FPS. El seu codi s’executa 144 vegades per segon.
Si sumes position += 1 a cada frame:
- Tu avances 60 píxels cada segon.
- El teu amic avança 144 píxels cada segon.
El teu amic és més ràpid només per tenir millor PC! Això és injust (i trenca la física).
Delta és el temps que ha passat des de l’últim frame.
- Si vas a 60 FPS, delta és
1/60(aprox 0.016s). - Si vas a 144 FPS, delta és
1/144(aprox 0.006s).
Si multipliques per delta:
- Tu:
1 * 60 vegades * 0.016 = 1metre per segon. - Amic:
1 * 144 vegades * 0.006 = 1metre per segon.
Conclusió: Multiplicar per Delta converteix “Píxels per Frame” en “Píxels per Segon”. Fa que el joc vagi igual en una patata que en un PC de la NASA.
Nota de Godot
move_and_slide() utilitza delta automàticament a dins, per això al codi de dalt no el veus multiplicant. Però si mous coses manualment canviant position, SEMPRE utilitza delta.
6. Script Final (Per ara)
Abans de copiar, mira dos detalls nous:
@export: Aquesta paraula màgica fa que la variablespeedaparegui a l’Inspector de Godot. Així pots ajustar la velocitat escrivint un número a la capseta sense tornar a obrir l’script.direction * speed: Recorda quedirectionval 1 (és un Vector Unitari). És només la direcció pura. En multiplicar-lo per 400, convertim aquest 1 en 400 píxels per segon.- Això significa: “Mou-te 400 píxels per segon en aquesta direcció”.
extends CharacterBody2D
@export var speed = 400
func _physics_process(delta):
var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = direction * speed
move_and_slide()extends CharacterBody2D
@export var speed = 400
func _physics_process(delta):
var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = direction * speed
move_and_slide()Felicitats! Tens una nau que es mou consistentment en totes direccions.
7. Provant-ho tot: El Teu Primer Nivell (Sandbox)
Fins ara hem treballat al “taller” (l’escena del Player). Però una nau necessita un univers.
Si li dones al botó de Play (F5) ara, et demanarà una escena principal. Anem a crear-la:
- Crea una Nova Escena.
- Tria 2D Scene (Node2D). Aquesta vegada no volem físiques, només un contenidor per al món. Anomena’l
Level(PascalCase, perquè és un Node). - Desa-la a
scenes/level.tscn(snake_case, perquè és un fitxer).
Instanciant al Jugador (El “Link”)
Aquí passa la màgia de Godot. No “copiarem i enganxarem” al jugador. El referenciarem.
- Busca el teu fitxer
player.tscna la carpetascenes(a baix a l’esquerra). - Arrossega’l des de l’explorador de fitxers i deixa’l anar dins del panell de Scene (a dalt a l’esquerra, sota on diu
Level). Això el col·locarà automàticament a la posició(0, 0), perfectament centrat. - Veuràs que apareix amb una icona de claqueta de cinema 🎬 al costat. Això significa que és una Instància. Si canvies el
player.tscnoriginal (ex: el pintes de vermell), aquest també canviarà!
Llums, Càmera… Coordenades!
Per facilitar-nos les matemàtiques (on 0 és a dalt i el màxim és a baix):
- A l’escena
Level, afegeix un node fillCamera2D. - A l’Inspector, busca Anchor Mode i canvia’l a
Fixed Top Left.- Això fa que la cantonada superior esquerra sigui el (0,0), com a les pantalles de píxels clàssiques.
Centrant la Nau
Ara que el (0,0) és la cantonada, la teva nau (que està a 0,0) es veurà tallada a dalt a l’esquerra. Anem a col·locar-la al centre exacte usant lògica:
- La resolució per defecte de Godot és 1152 x 648 (massa ampla per al nostre joc retro). Anem a canviar-la a 800 x 600:
- Ves a Project > Project Settings > Display > Window.
- Canvia
Viewport Widtha 800. - Canvia
Viewport Heighta 600.
- El centre ara és:
800 / 2 = 400(X) i600 / 2 = 300(Y). - Selecciona el node
player(la instància). - A Inspector > Transform > Position, escriu:
- X:
800/2 - Y:
600/2
- X:
Truc: Godot sap matemàtiques!
Pots escriure literalment 800/2 a la casella i ell farà el compte. O també simplement pots moure el Player amb el ratolí fins on vulguis a la vista 2D.
A Jugar!
- Prem F5 (o el botó de Play a dalt a la dreta).
- Et preguntarà: “No Main Scene defined. Select one?”. Digues-li que Select Current.
- Aquí ho tens! La teva nau movent-se al teu nou món.
La meva nau marxa de la pantalla?
És normal! Encara no hem posat límits (parets). La teva nau viatja feliçment cap a l’infinit de coordenades matemàtiques. En propers capítols posarem murs a aquest univers.