Muchos comentarios de ¿cómo hacer que mi personaje tenga un ataque especial? ¿cómo hago otras animaciones? ¿puedo acceder a una variable del personaje desde otro script? pueden responderse en un artículo (en realidad dos). Por esa razón hoy les hablaré sobre cómo diseñar un personaje que después podamos reutilizar en otra característica del proyecto e incluso otro.
No son cosas tan complicadas, pero pueden olvidarse rápidamente. Vamos a repasar las bases y después proseguiremos con Godot Engine.
¿Qué personaje estamos creando?
Cada quien tiene su propio proyecto y quiere algo específico. Piensen una cosa: ¿el personaje hace algo similar o igual a otro elemento del juego? Si es así, podemos diseñar unas piezas que nos permitan dar funcionalidad a cualquier otro elemento que comparta alguna característica con el personaje.
Por ejemplo: si hablamos del personaje controlado por el jugador, ¿podemos ver la diferencia con un personaje controlado por la inteligencia artificial? Ambos podrían moverse, sólo que uno necesita un teclado y otro no. Estas cosas deben idearse antes de empezar a desarrollar su personaje, porque tardaría mucho más volver a programar toda la lógica para el movimiento de varios elementos, que diseñar desde el principio una pieza para manejar todos los casos de movimiento.
Así como suelen decir a menudo: “Si escribes muchas líneas de código, probablemente estás mal, encapsula en una función y úsala donde haga falta”, cuando estamos haciendo escenas (que en Godot son como una clase) en algún momento tendremos una que comparte muchas características con otra, y eso gasta más tiempo: ¿Por qué tienes que volver a crear los mismos nodos en dos escenas, si podías tener esto en cuenta y usar una clase específica?
Si no estás familiarizado con los términos de la programación orientada a objetos (POO), estás de suerte. Vamos a repasar juntos.
Clases y objetos
Las clases son como un script que contiene funciones/métodos que pueden producir un objeto determinado en base a lo que necesitamos. Los objetos son esas instancias personalizadas de una clase, que tienen sus propias funciones para hacer las cosas que hacen falta en nuestro programa, según los datos que facilita su clase. Todos los objetos pertenecen a una clase, la combinación de clases y objetos es lo que determina la complejidad de nuestro proyecto.
Por ejemplo: la clase Espada tiene un objeto llamado “Mango” y un objeto llamado “Hoja”. Imaginen a ambos objetos como dos scripts que tienen sus variables y funciones que los hacen útiles (también se puede dar el caso de que el objeto “Mango” pertenezca a la clase Mangos y “Hoja” a la clase Hojas). Cuando queramos crear una espada, podemos hacer una nueva instancia de la clase Espada y estaríamos llamando al mismo tiempo los dos objetos, quienes también pertenecen a sus propias clases.
Si imaginamos la clase como un script, también podemos tener en cuenta que existe la posibilidad de heredar características de otra clase. Cuando un script hereda de otro, tiene accedo a todas las funciones, variables, constantes (todo el contenido) del primero. En el ejemplo anterior, si quisiéramos que nuestra espada herede de una clase superior ¿cuál creen que sería esta? Podemos estar hablando de la clase “Armas”, con unas variables para determinar la cantidad de espadas, poder, u otra cosa.
Sabiendo eso ya podemos entender qué es el árbol de nodos que usamos en Godot: una selección entre diferentes clases.
Por ejemplo: La clase Control, quien hereda de una clase llamada CanvasItem que también hereda de una clase llamada Node. Debemos aprender a trabajar entre clases, para que podamos dominar la programación orientada a objetos, y diseñemos clases (escenas) eficientes.
Anoten para no olvidar: en Godot todas las escenas, nodos y scripts son una clase. Tienen que tenerlo en mente cuando me refiera a las escenas o scripts de ahora en adelante. Los objetos son las instancias de una clase, que están configurados para hacer algo, pero al mismo tiempo pueden formar su propia clase (Por eso los nodos se pueden ver como un objeto también).
Diseñar una buena escena
Cuando estamos creando nuestra propia escena, que puede ser cualquier cosa (desde un nivel completo hasta un personaje) hay algo muy importante que se recomienda hacer para que en un futuro podamos volver a utilizar la misma escena en otro proyecto (o una versión más actualizada del anterior).
Básicamente es que una clase (vamos a llamarla “B”) que forma parte de una clase superior (es hija de la clase A) no debería nunca ejecutar por sí misma a un método de su padre A. ¿Por qué? El problema de esto es que puede darse el caso de que la clase hija no siempre tenga un padre de la misma clase ¿Entienden? Piensen que si el padre actual de la clase es el único con un método llamado “sumar_puntos()”, ¿qué pasaría si lo cambiamos por otra clase que no utiliza ese método? En ese caso, la clase hija que mencioné, estaría haciendo algo como esto: “get_parent().sumar_puntos()”, sin saber que su padre ya no es la misma clase. Obviamente tendríamos un error de tipo “No se encuentra el método en esta clase”.
Para que los objetos/clases tengan sentido, sólo deben ejecutar sus propios métodos en base a las propiedades que tienen ellas mismas (las propiedades de una clase son las variables, listas, diccionarios, etc, que tienen definidas en su script).
Sigamos con otro ejemplo: la clase A se llama Personaje, y tiene las siguientes variables:
- Salud.
- Herramienta.
- Nombre.
- Velocidad.
- Saltos.
- …
Puede tener muchas otras, pero con esas bastan (para el ejemplo). Son unas propiedades básicas que prácticamente cualquier personaje de un juego de plataformas puede tener. Si nuestro personaje de pronto no tiene un método para manejar la velocidad, o su herramienta, simplemente no le pasaremos (o no usaremos) la información de la respectiva propiedad. Ya con esta clase creada (es un nuevo script que se extiende de KinematicBody2D), podemos añadir una nueva escena cuyo nodo principal sea un KinematicBody2D. Para que esa escena herede toda la información de la clase “Personajes”, hay que añadirle un script que no se extienda directamente de la clase KinematicBody2D, sino de la nueva clase llamada “Personajes”. En Godot 3.2 se introduce la palabra clave “class_name <nombre>”, que nos permite guardar nuestras propias clases. Les recomiendo añadir esa palabra al inicio del primer script, así en la nueva instancia (la nueva escena) que estamos produciendo, podemos extendernos directamente de la clase “Personajes” y tener acceso a todas las propiedades que necesitamos.
Script de la clase Personajes:
Si queremos, por ejemplo, cambiar nuestro nombre, hay que buscar la forma de modificar las variables que pone a nuestra disposición el script principal. Lo más fácil que se nos puede ocurrir es que dentro de _ready() escribamos “nombre = ‘Pepe’”. Este método que nos ofrece GDScript sólo se ejecuta cuando el nodo esté listo para servir en la escena. Si tiene nodos hijos, el _ready() tiene que esperar a que cada hijo ejecute su propia función de “listo”.
Esto sucede en micro segundos, así que en una simple escena no notaremos nada… sin embargo también hay una función que se ejecuta justo cuando el objeto/clase está iniciando, se trata de _init(). Dentro de ella podemos asignar un valor diferente para las variables que queramos tomar de la clase Personajes.
Script de una instancia:
El resto sería definir funciones dentro de ese personaje particular y ejecutarlos cuando haga falta. Podemos hasta agregar una máquina de estados y pasarle a un estado de movimiento el valor de nuestra variable “Velocidad” o “Saltos”… esto lo haremos en la segunda parte de este tutorial.
Creando un personaje desde 0
Voy a suponer que ya saben qué personaje crear. Mi objetivo es hacer una clase que me permita producir diferentes personajes fácilmente, y al momento de exportarla funcionar en otro proyecto. Para conseguir eso haremos lo que he estado comentando a lo largo de este totorial:
- Crearemos una clase para personajes.
- Produciremos un personaje para nuestro jugador.
- Produciremos un personaje NPC.
La últimas dos partes del tutorial vendrán en otro video/artículo donde planeo volver a hablar sobre máquinas de estados. Por ahora continuemos con la creación de las clases:
1. Creación de clases
Vamos a empezar con la clase “Personajes”. Para eso añadimos un nuevo script que se extienda del nodo que siempre será el principal en nuestras clases. Generalmente para este tipo de elemento (los personajes) se usa la física cinemática para así tener más control sobre el cuerpo físico que estamos haciendo. Teniendo eso en cuenta, haremos que la clase Personajes se extienda del KinematicBody2D… por cierto: también se puede dar el caso de que quieran usar un nodo más general, como un Node2D para añadir dentro de él un KinematicBody2D o incluso Area2D, también pueden hacer eso. Yo por mi parte, sé que la mayoría de mis personaje casi siempre usan el nodo KinematicBody2D.
Lo primero que haremos dentro del script es añadir una nueva clase llamada “Personajes”, para en un futuro crear uno nuevo en base a esto.
¿Qué personaje estamos creando? Depende del tipo de juego. Para algunos puede resultar de utilidad que añadamos una variable que controle los saltos que realiza el personaje, mientras que en otro juego eso no existe o no hace falta. Tengo ganas de cubrir diferentes casos y para variar hoy de los juegos de plataformas, esta vez crearemos una nave espacial.
Si estamos en un juego de naves espaciales, es normal que necesitemos diferentes variables para el movimiento, velocidad, dirección… Sólo añadiré esas para que vayamos construyendo la clase.
Hasta ahora hemos creado una clase que sólo identificamos en GDScript. Podemos mejorar esto si agregamos una escena con los nodos predeterminados de un personaje. Mi árbol de nodos sería algo así:
- KinematicBody2D (con el script de la clase Personajes)
- Sprite (Imagen principal del personaje)
- CollisionShape2D o CollisionPolygon (para detectar las colisiones con otros cuerpos)
- AnimationPlayer (para controlar las animaciones)
- Sprite (Imagen principal del personaje)
Con esta estructura básica podemos hacer nuevas instancias de un personaje. Guarden la escena como “plantilla_para_personajes” o algo así. Ahora cuando sea necesario crear un nuevo personaje, nos podemos ahorrar tiempo usando la plantilla general y añadiendo cosas nuevas que sólo son válidas para el tipo de personaje que vamos a crear. Hagan clic en “Nueva Escena Heredada” y seleccionen la plantilla.
Todos los nodos ahora se ven más oscuros. Esto significa que son nodos heredados de otra clase. Un nodo que heredas no se puede cambiar desde una instancia, sólo puedes realizar cambios a la plantilla cuando la estas editando directamente. Es un recurso muy útil, porque ahora podemos tener un jugador con las mismas características de un personaje normal, mientras está la posibilidad de añadirle otros nodos o escenas para ampliar sus capacidades.
Hasta aquí vamos a dejar la primera parte del tutorial. Seguimos dentro de poco con los puntos de arriba. ¡Gracias por leer! Comenta si tienes alguna duda o sugerencia.
Hola me gusta mucho tu contenido
Muchas gracias amigo :). Ya voy a publicar la segunda parte.
Excelente tutorial.
Saludos
es interesante, aunque sigo sin entender algunas cosas
¿Qué te gustaría reforzar amigo?
Hola Cesar, buenas!. Muy interesante tu tutorial de crear un personaje, sobre todo lo de generar una plantilla multiproposito. Hace poco descubrí Godot y se ve genial.
Cuándo subes la próxima lección? Gracias y felicitaciones por compartir!
Hola Fabio, me alegra que te haya gustado :). Ya he publicado la segunda parte en Youtube, pero tengo tiempo por los momentos de traer la versión escrita. Quizás en unos días.