Godot
Godot és un motor de jocs amb IDE per a programar-los. Si vols aprendre Godot, el millor és anar al manual oficial i després anar fent exemples i tutorials. Aquest article és un tutorial més, adaptat més o menys al nivell de 2on de la ESO i pensat per a que siga didàctic per a gent que no ha programat abans.
El teu primer joc
Anem a fer el joc per fases i explicarem el perquè de cadascuna.
Configuració inicial
El primer que tenim que fer és crear el projecte. Ens tenim que descarregar els recursos de la web oficial en aquest enllaç. Després, creem un projecte nou i descomprimim els recursos en la carpeta del projecte.
Dins de godot, tenim que fer que la pantalla tinga unes dimensions determinades de 480x720 i crear el jugador com un node Area2D amb nom Player:
Una escena en Godot és qualsevol element del joc que té entitat pròpia. En el nostre cas el jugador, els enemics i la pantalla principal. La creació del player és la primera escena que anem a fer i quan es guarde tindrà un arxiu de nom Player.tscn.
L'escena del Player
Animació
A continuació cal crear l'animació del node Player. En aquest cas, triem un AnimatedSprite al que donarem varis moviments. Amb l'opció Frames.
Per últim, el farem un poc més menut:
La forma de col·lisió
Ara toca definir la forma per la qual detectarà les col·lisions. En el nostre cas, la que més es sembla és la càpsula:
És important que, una vegada estan tots els nodes fills creats, bloquegem al node Player de manera que no es puga seleccionar nodes fills. Això es fa en aquest botó:
El moviment del Player
El nostre jugador es mourà amb les fletxes del teclat. Per aconseguir-ho, tenim que crear un script per al node que detecte el teclat i canvie la posició del node en la pantalla. Aquest és l'script:
extends Area2D
export (int) var speed # velocita del jugador (pixels/sec).
var screensize # mida de la pantalla
func _ready():
screensize = get_viewport_rect().size
func _process(delta):
var velocity = Vector2() # Vector de velocitat (x,y)
if Input.is_action_pressed("ui_right"):
velocity.x += 1
if Input.is_action_pressed("ui_left"):
velocity.x -= 1
if Input.is_action_pressed("ui_down"):
velocity.y += 1
if Input.is_action_pressed("ui_up"):
velocity.y -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite.play() # Si hi ha moviment es mou
else:
$AnimatedSprite.stop() # Si no hi ha moviment para l'animació
position += velocity * delta # Calcula la posició
position.x = clamp(position.x, 0, screensize.x)
position.y = clamp(position.y, 0, screensize.y)
if velocity.x != 0: # Si es mou de costat
$AnimatedSprite.animation = "right"
$AnimatedSprite.flip_v = false
$AnimatedSprite.flip_h = velocity.x < 0 # Dreta o esquerra
elif velocity.y != 0: # Si es mou en vertical
$AnimatedSprite.animation = "up"
$AnimatedSprite.flip_v = velocity.y > 0 # Dalt o baix
Els enemics
Els enemics han d'estar en una altra escena. Cal crear un RigidBody2D i crear aquest arbre:
I cal afegir algunes propietats al node:
D'aquesta manera als enemics no els afecta la gravetat, i no col·lisionen entre ells.
Cal afegir els sprites corresponents a l'enemic:
Per últim, els donem una escala de 0.75 i configurem la seua CollisionShape2D per a ser una càpsula com el player, però amb una rotació de 90 graus:
L'script dels enemics Fes un script per al Mob i pega aquest codi:
extends RigidBody2D
export (int) var min_speed # Minimum speed range.
export (int) var max_speed # Maximum speed range.
var mob_types = ["walk", "swim", "fly"]
func _ready():
$AnimatedSprite.animation = mob_types[randi() % mob_types.size()]
Recorda omplir les noves propietats min_speed i max_speed amb 150 i 250 respectivament.
Ara, cal connectar l'esdeveniment de que l'enemic a abandone la pantalla en una funció que elimine a l'enemic per a que no ocupe memòria.
La pantalla principal
Fins al moment, hem creat el jugador i l'enemic i ara falta anar juntant-ho tot en la pantalla principal que implementarà les regles del joc.
Cal crear els nodes per a tindre:
Cal configurar els Timers de manera que el WaitTime quede així:
- ModTimer: 0.5
- ScoreTimer: 1
- StartTimer: 2 OneShot: On
- En StartPosition cal ficar en Position 240 x 450
Ara hi ha que definir la posició inicial dels enemics. Com poden eixir per qualsevol costat de la pantalla, cal crear un Path2D i després seleccionar una posició aleatòria en el path. De moment, anem a dibuixar el path:
A continuació, cal afegir un PathFollow2D i modificar el nom:
Després, anem a afegir aquest script a la pantalla principal:
extends Node
export (PackedScene) var Mob # Afegir l'escena dels enemics
var score # La variable de la puntuació
func _ready():
randomize()
I ara cal afegir a la nova variable exportada Mob l'escena dels enemics:
Les col·lisions
Anem a preparar el joc per detectar col·lisions.
El primer que tenim que fer és afegir la capacitat de enviar una senyal de colisió "hit":
D'aquesta manera, el protagonista té un esdeveniment nou:
Com que els enemics són Rigidbody2D, anem a detectar quan entra un 'body':
Ací tens el codi que pots pegar:
hide() # El jugador desapareix quan el toquen
emit_signal("hit") # El jugador emet la senyal hit
$CollisionShape2D.disabled = true # Es deshabilita la forma de la col·lisió per no seguir detectant.
També pots pegar aquest codi que es pot cridar per a iniciar al jugador després:
func start(pos):
position = pos # Situar al jugador en la posició inicial
show() # Mostrar al jugador
$CollisionShape2D.disabled = false # tornar a hablitat la la forma de la col·lisió
La lògica del joc
Fins al moment, hem creat per separat als enemics, al player y la pantalla principal. En l'anterior exercici hem començat a ajuntar-ho tot. En aquest ja anem a donar-li una relació entre la pantalla i els altres elements i veure cóm interactuen en col·lisions i moviment del enemics.
Afegeix a l'script del main aquest codi:
func game_over():
$ScoreTimer.stop() # Quan el joc acaba, es paren els temporitzadors
$MobTimer.stop()
func new_game():
score = 0
$Player.start($StartPosition.position) # Crida a la funció start de player
$StartTimer.start()
També anem a afegir la lògica dels temporitzadors. Però per a fer-ho, cal connectar el timeout() dels temporitzadors en la seua funció:
I aquest és el codi que han de tindre:
func _on_StartTimer_timeout():
$MobTimer.start() # començar els temporitzadors de l'enemic i de la puntació
$ScoreTimer.start()
func _on_ScoreTimer_timeout():
score += 1 # Cada segon augmenta en 1 la puntuació
A continuació, anem a fer que apareguen els enemics amb el temporitzador ModTimer:
I sols cal afegir aquest codi:
# Elegir una posició aleatòria en el Modpath per a que comencen els enemics
$MobPath/MobSpawnLocation.set_offset(randi())
# Crear un enemic (mob) nou
var mob = Mob.instance()
add_child(mob)
# Fer que l'enemic tinga una direcció cap a dins de la pantalla
var direction = $MobPath/MobSpawnLocation.rotation + PI / 2
# Posar l'enemic a una posició aleatoria del path
mob.position = $MobPath/MobSpawnLocation.position
# Afegir certa aleatorietat a la direcció de l'enemic
direction += rand_range(-PI / 4, PI / 4)
mob.rotation = direction
# Triar una velocitat aleatoria per a l'enemic
mob.set_linear_velocity(Vector2(rand_range(mob.min_speed, mob.max_speed), 0).rotated(direction))
Per a que funcione el joc, podem afegir aquest codi al main.gb:
func _process(delta):
if Input.is_action_pressed("ui_accept"):
new_game()