HTML. Unidad 7. Formato de texto avanzado.

Presentación

Infografías

Diapositivas y vídeos

Podrás familiarizarte con los contenidos clave de esta unidad a través de esta presentación y esta otra; estas diapositivas; y este vídeo y finalmente, este otro .

Introducción

HTML dispone de numerosos elementos para dar formato al texto que no abordamos en las unidades anteriores. Aunque las etiquetas que se describen en esta unidad son menos conocidas o de uso más específico, conocerlas resulta de gran utilidad para enriquecer nuestros documentos. Aprenderás a realizar el marcado semántico correcto de abreviaturas, citas y referencias, fragmentos de código informático, ecuaciones matemáticas y datos de contacto.

Abreviaturas

El elemento de abreviatura de HTML (<abbr>) representa una abreviatura o un acrónimo. A través del atributo opcional title, podemos proporcionar el significado desarrollado del término o una descripción legible para el usuario.

Por lo general, los navegadores presentan este texto mediante una etiqueta emergente (tooltip) que aparece automáticamente cuando se pasa el cursor del ratón sobre el elemento. Es importante tener en cuenta que, si se incluye el atributo title, este debe contener única y exclusivamente la descripción completa, sin añadir texto adicional.

A continuación, veamos algunos ejemplos prácticos en los que resulta adecuado el uso de abreviaturas o acrónimos:

Usamos HTML para estructurar nuestros documentos web.
Puedes utilizar CSS para dar estilo a tu HTML.
La NASA realiza un trabajo fascinante, sin duda.
El chiste de Ashok me hizo LOL muchísimo.

Veamos ahora qué código HTML debemos escribir para conseguir ese resultado:

<p>Usamos <abbr title="Lenguaje de Marcado de Hipertexto">HTML</abbr> para estructurar nuestros documentos web.</p>

<p>Puedes utilizar <abbr title="Hojas de Estilo en Cascada">CSS</abbr> para dar estilo a tu <abbr title="Lenguaje de Marcado de Hipertexto">HTML</abbr>.</p>

<p>La <abbr title="Administración Nacional de Aeronáutica y el Espacio">NASA</abbr> realiza un trabajo fascinante, sin duda.</p>

<p>El chiste de Ashok me hizo <abbr title="Reír a carcajadas">LOL</abbr> muchísimo.</p>

El objetivo principal de este elemento es aportar valor semántico y facilitar información adicional. Aunque todos los navegadores lo muestran como un elemento en línea (inline) de forma predeterminada, su estilo visual por defecto no es consistente y varía según el navegador (si bien esto siempre puede unificarse añadiendo reglas CSS).

Las diferencias de estilo más comunes son las siguientes:

  • Sin estilo visual: Algunos navegadores, como Internet Explorer, no le aplican ninguna apariencia distintiva, mostrándolo exactamente igual que si fuera un elemento <span>.
  • Subrayado: Navegadores como Opera o Firefox suelen añadir un subrayado de puntos al texto de la abreviatura para diferenciarlo.
  • Versalitas: Unos pocos navegadores van un paso más allá y, además del subrayado punteado, transforman el texto a versalitas (small caps).

Ejercicio propuesto: Ejemplo de uso de abreviaturas

Crea una página web utilizando el código del ejemplo anterior, y añade varios párrafos nuevos que contengan algunas abreviaturas y verifica el resultado en tu navegador. Recuerda incluir el resto de etiquetas básicas de HTML necesarias para la estructura del documento y no olvides validar tu código.

Ejercicio propuesto: Abreviaturas para chatear

El mundo del correo electrónico, los mensajes de texto y la mensajería instantánea han dado lugar a toda una serie de acrónimos y abreviaturas que permiten a los usuarios redactar sus mensajes con mayor rapidez. En este ejercicio deberás crear un par de listas. La primera incluirá la relación de abreviaturas que se enumeran a continuación (puedes añadir otras que conozcas). La segunda lista deberá contener al menos veinte frases que ilustren cómo se utilizan dichas abreviaturas en una conversación de chat.

Además, utiliza encabezados <h1> y <h2> para añadir títulos a las listas y proporcionar así un texto descriptivo previo.

Por ejemplo, la primera lista podría mostrar las abreviaturas de la siguiente manera:

  • bn – Bien
  • bss – Besos
  • finde – Fin de semana
  • hl – Hola
  • kdms – Quedamos
  • npi – Ni puñetera idea
  • ns / nc – No sabe / No contesta
  • pf / xfa – Por favor
  • pq / xq – Por qué
  • q tal – Qué tal
  • rt – Retuit (compartido / de acuerdo)
  • salu2 – Saludos
  • tb – También
  • tqm – Te quiero mucho
  • tpc – Tampoco
  • vdd – Verdad
  • xo – Pero
  • +o- – Más o menos

La segunda lista deberá incluir al menos veinte frases en las que se empleen dichas abreviaturas. Por ejemplo:

  • Espero que te encuentres bn, hace tiempo que no hablamos.
  • Me tengo que ir corriendo, hablamos luego. ¡bss!
  • ¿Tenéis algún plan interesante para este finde?
  • hl, ¿te ha llegado ya el paquete que te envié?
  • Si os parece bien, kdms a las 20:00 en la plaza mayor.
  • No me preguntes cómo arreglarlo porque no tengo npi.
  • Le he preguntado si vendrá a la cena de empresa, pero de momento ns / nc.

Puedes utilizar el siguiente código como ejemplo para saber qué etiquetas debes utilizar para obtener ambas listas:

<h1>Algunas abreviaturas que uso para chatear</h1>

<h2>Las abreviaturas</h2>

<ul>
    <li>bn - Bien</li>
    <li>bss - Besos</li>
    <li>finde - Fin de semana</li>
    <li>hl - Hola</li>
    <li>kdms - Quedamos</li>
    <li>npi - Ni puñetera idea</li>
    <li>ns / nc - No sabe / No contesta</li>
    <li>...</li>
</ul>

<h2>Algunos ejemplos</h2>

<ul>
    <li>Espero que te encuentres <abbr title="Bien">bn</abbr>, hace tiempo que no hablamos.</li>
    <li>Me tengo que ir corriendo, hablamos luego. ¡<abbr title="Besos">bss</abbr>!</li>
    <li>¿Tenéis algún plan interesante para este <abbr title="Fin de semana">finde</abbr>?</li>
    <li><abbr title="Hola">hl</abbr>, ¿te ha llegado ya el paquete que te envié?</li>
    <li>Si os parece bien, <abbr title="Quedamos">kdms</abbr> a las 20:00 en la plaza mayor.</li>
    <li>No me preguntes cómo arreglarlo porque no tengo <abbr title="Ni puñetera idea">npi</abbr>.</li>
    <li>Le he preguntado si vendrá a la cena de empresa, pero de momento <abbr title="No sabe / No contesta">ns / nc</abbr>.</li>
    <li>...</li>
</ul>

Citas

El elemento <q>

El elemento HTML <q> indica que el texto que encierra es una cita corta en línea (inline). La gran mayoría de los navegadores modernos interpretan este elemento envolviendo automáticamente el texto entre comillas.

Asimismo, puedes utilizar el atributo cite para especificar una URL que señale el documento de origen o la fuente de la información citada, aunque es importante tener en cuenta que, por defecto, el navegador no muestra esta URL al usuario de forma visible.

A continuación mostramos un par de ejemplos analizando primero el resultado visual (en la mayoría de los casos la única diferencia es que las comillas aparecen automáticamente para acotar la cita):

Cuando Dave le pide a HAL que abra las compuertas de la cápsula, HAL responde: "Lo siento, Dave. Me temo que no puedo hacerlo".

Según el sitio web de Mozilla, "Firefox 1.0 se lanzó en 2004 y se convirtió en un gran éxito".


Y ahora el código fuente correspondiente:

<p>
    Cuando Dave le pide a HAL que abra las compuertas de la cápsula, HAL responde: <q cite="https://www.imdb.com/es-es/title/tt0062622/">"Lo siento, Dave. Me temo que no puedo hacerlo"</q>.
</p>

<p>
    Según el sitio web de Mozilla,
    <q cite="https://www.mozilla.org/en-US/about/history/details/">"Firefox 1.0 se lanzó en 2004 y se convirtió en un gran éxito"</q>.
</p>

Importante: El elemento <q> está diseñado para citas breves que no requieren saltos de párrafo. Para citas extensas, utiliza el elemento <blockquote>.

Ejercicio propuesto: Citas en línea

Crea una página web utilizando el código del ejemplo anterior, y luego añade varios párrafos que contenga citas de tu elección y verifica el resultado en el navegador. Recuerda incluir el resto de etiquetas básicas de HTML para estructurar el documento y no olvides validar tu código.

Puedes usar por ejemplo citas de tu película favorita, tal como aparecen en «https://www.fotogramas.es/noticias-cine/g32183447/star-wars-mejores-frases-de-la-saga/«.

El elemento <blockquote>

El elemento HTML <blockquote> (o elemento de cita en bloque) indica que el texto que encierra constituye una cita extensa. Por lo general, los navegadores representan este elemento visualmente aplicando una sangría o indentación al texto.

Es posible especificar la URL de la fuente de la cita mediante el atributo cite, pero para indicar la referencia de la fuente en formato de texto visible se emplea el elemento completo <cite>. Por ejemplo:

Vuestro trabajo va a llenar gran parte de vuestra vida, y la única forma de estar realmente satisfecho es hacer lo que creéis que es un gran trabajo. Y la única forma de hacer un gran trabajo es amar lo que hacéis. Si aún no lo habéis encontrado, seguid buscando. No os conforméis. Como con todo lo que tiene que ver con el corazón, lo sabréis cuando lo encontréis. Steve Jobs
<blockquote cite="https://www.brainyquote.com/quotes/steve_jobs_416859">
    Vuestro trabajo va a llenar gran parte de vuestra vida, y la única forma de estar realmente satisfecho es hacer lo que creéis que es un gran trabajo. Y la única forma de hacer un gran trabajo es amar lo que hacéis. Si aún no lo habéis encontrado, seguid buscando. No os conforméis. Como con todo lo que tiene que ver con el corazón, lo sabréis cuando lo encontréis.
    <a href="https://www.brainyquote.com/quotes/steve_jobs_416859"><cite>Steve Jobs</cite></a>
</blockquote>

Ejercicio propuesto: Citas célebres

Crea una página web que recopile al menos veinte de las citas más famosas de la historia. Puedes encontrar muchas de ellas en sitios como «https://www.proverbia.net«, «https://www.mundifrases.com/«, o también puedes utilizar cualquier otra fuente que prefieras.

Debes utilizar el elemento <blockquote> para envolver la cita completa, y el elemento <cite> dentro de cada cita para indicar el nombre del autor, tal como se muestra en el ejemplo anterior y en los siguientes:
La mayor gloria de vivir no radica en no caer nunca, sino en levantarnos cada vez que caemos. Nelson Mandela

La manera de empezar es dejar de hablar y comenzar a actuar. Walt Disney

Vuestro tiempo es limitado, así que no lo malgastéis viviendo la vida de otro. No os dejéis atrapar por el dogma, que es vivir según los resultados del pensamiento de otros. No dejéis que el ruido de las opiniones de los demás ahogue vuestra propia voz interior. Y lo más importante, tened el coraje de seguir a vuestro corazón y vuestra intuición. Steve Jobs

Si la vida fuera predecible, dejaría de ser vida y no tendría sabor. Eleanor Roosevelt

Si miras lo que tienes en la vida, siempre tendrás más. Si miras lo que no tienes en la vida, nunca tendrás suficiente. Oprah Winfrey

Combinando los elementos <q>, <blockquote> y <cite>

El elemento de cita HTML (<cite>) se utiliza para referenciar el título de una obra creativa citada (como un libro, un artículo, un ensayo, una película, etc.). Según las convenciones de metadatos más apropiadas para el contexto, esta referencia puede presentarse de forma abreviada. Veamos un ejemplo práctico en el que resulta útil combinar estos tres elementos para crear una mejor estructura semántica:

Hola y bienvenidos a mi página de motivación. Como dice el sitio Citas de Confucio :

No importa lo lento que vayas mientras no te detengas.

También me encanta el concepto del pensamiento positivo y la necesidad de «mantener tus pensamientos positivos» (como se menciona en Citas Positivas ).


Y ahora echemos un vistazo al código fuente que debemos escribir para obtener ese resultado:

<p>
    Hola y bienvenidos a mi página de motivación. Como dice el sitio 
    <a href="https://proverbia.net/autor/frases-de-confucio">
        <cite>Citas de Confucio</cite>
    </a>:
</p>

<blockquote cite="https://www.proverbia.net/citasautor/confucio">
    No importa lo lento que vayas mientras no te detengas.
</blockquote>

<p>
    También me encanta el concepto del pensamiento positivo y la necesidad de 
    <q cite="https://www.mundifrases.com/tema/pensamiento-positivo/">
        mantener tus pensamientos positivos
    </q> 
    (como se menciona en 
    <a href="https://psicologiaymente.com/reflexiones/frases-positivas">
        <cite>Citas Positivas</cite>
    </a>).
</p>

Ejercicio propuesto: Ejemplo de citas completas

Crea una página web utilizando primero el código del ejemplo anterior y verifica el resultado en tu navegador. A continuación añade otro bloque similar combinando las tres etiquetas mencionadas, y vuelve a comprobar el resultado en el navegador.

No olvides incluir el resto de las etiquetas básicas de HTML (incluyendo títulos <h1> y <h2>) y valida tu código para asegurar que es correcto.

Representación de código informático

HTML pone a nuestra disposición una serie de elementos específicos para el marcado de código informático:

  • <code>: Se utiliza para marcar fragmentos genéricos de código informático.
  • <pre>: Se usa para preservar los espacios en blanco (generalmente empleado para bloques de código). Si utilizas sangría o espacios en blanco adicionales dentro del texto, los navegadores los ignorarán por defecto y no se verán reflejados en la página renderizada. No obstante, como ya explicamos en una unidad anterior, si envuelves el texto entre etiquetas <pre></pre>, los espacios en blanco se mostrarán idénticos a como aparecen en tu editor de texto.
  • <var>: Sirve para marcar específicamente nombres de variables.
  • <kbd>: Se utiliza para marcar la entrada de teclado (u otros tipos de entrada de datos) introducida en el ordenador.
  • <samp>: Se emplea para marcar la salida (output) de un programa informático.

El elemento <code>

El elemento HTML <code> muestra su contenido con un estilo visual diseñado para indicar que el texto es un fragmento breve de código informático. Por defecto, los navegadores muestran el texto de este elemento utilizando la fuente monoespaciada predeterminada del sistema.

Veamos un par de ejemplos:

El método push() añade uno o más elementos al final de un array y devuelve la nueva longitud del mismo.

La función selectAll() resalta todo el texto en el campo de entrada para que el usuario pueda, por ejemplo, copiar o eliminar el texto.


Y este sería el código HTML del ejemplo anterior:

<p>
    El método <code>push()</code> añade uno o más elementos al final de un array y devuelve la nueva longitud del mismo.
</p>

<p>
    La función <code>selectAll()</code> resalta todo el texto en el campo de entrada para que el usuario pueda, por ejemplo, copiar o eliminar el texto.
</p>

Ejercicio propuesto: Código en línea

Crea una página web con el código del ejemplo anterior, luego añade algunos ejemplos más, y verifica los resultados en tu navegador.

No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código.

Los elementos <pre> + <code>

El elemento <code> por sí solo representa únicamente una instrucción o una única línea de código. Para representar múltiples líneas de código (un bloque), debemos envolver el elemento <code> dentro de un elemento <pre>. De esta forma, respetaremos los espacios y saltos de línea. Por ejemplo:

if (a > b) {
    console.log('¡Hola!'); // Ejemplo de código en el lenguaje JavaScript
}

Código HTML del ejemplo:

<pre><code>
if (a > b) {
    console.log('¡Hola!'); // Ejemplo de código en el lenguaje JavaScript
}
</code></pre>

Ejercicio propuesto: Bloque de código

Crea una página web con el código del ejemplo anterior, añade algunos bloque adicionales con cualquier fragmento de código de cualquier lenguaje y comprueba los resultados en tu navegador.

No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código

El elemento <var>

El elemento de variable HTML (<var>) representa el nombre de una variable dentro de una expresión matemática o en un contexto de programación. Por lo general, se presenta visualmente utilizando una versión en cursiva de la tipografía actual, aunque este comportamiento depende de cada navegador.

Por ejemplo:

Una ecuación simple: x = y + 2

El volumen de una caja es l × w × h, donde l representa la longitud, w la anchura y h la altura de la caja.

Las variables minSpeed y maxSpeed controlan la velocidad mínima y máxima del aparato en revoluciones por minuto (RPM).


Código HTML del ejemplo:

<p>
    Una ecuación simple: <var>x</var> = <var>y</var> + 2
</p>

<p>
    El volumen de una caja es <var>l</var> × <var>w</var> × <var>h</var>, donde <var>l</var> 
    representa la longitud, <var>w</var> la anchura y <var>h</var> la 
    altura de la caja.
</p>

<p>
    Las variables <var>minSpeed</var> y <var>maxSpeed</var> controlan la velocidad mínima y máxima 
    del aparato en revoluciones por minuto (RPM).
</p>

Ejercicio propuesto: Ecuaciones y variables

Crea una página web con el código del ejemplo anterior, y añade un par de párrafos adicionales con cualquier ecuación que desees y comprueba los resultados en tu navegador.

No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código

El elemento <kbd>

El elemento HTML de entrada de teclado (<kbd>) representa un fragmento de texto en línea que denota una entrada del usuario, ya sea a través de un teclado convencional, comandos de voz o cualquier otro dispositivo de entrada de texto.

Por convención, el navegador renderiza el contenido de un elemento <kbd> utilizando su fuente monoespaciada predeterminada para que destaque visualmente. Veamos algunos ejemplos:

Por favor, pulsa Ctrl + Mayús + R para recargar una página de MDN.

Usa el comando help micomando para ver la documentación del comando «micomando».

Puedes crear un nuevo documento utilizando el atajo de teclado Ctrl + N.


<p>
    Por favor, pulsa <kbd>Ctrl</kbd> + <kbd>Mayús</kbd> + <kbd>R</kbd> para recargar una página de MDN.
</p>

<p>
    Usa el comando <kbd>help micomando</kbd> para ver la documentación del comando "micomando".
</p>

<p>
    Puedes crear un nuevo documento utilizando el atajo de teclado <kbd>Ctrl</kbd> + <kbd>N</kbd>.
</p>

Ejercicio propuesto: Atajos de teclado

Crea una página web con el código del ejemplo anterior, añade un par de párrafos con cualquier atajo de teclado que conozcas y comprueba los resultados en tu navegador.

No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código.

El elemento <samp>

El elemento de muestra HTML (<samp>) se utiliza para encerrar texto en línea que representa la salida (output) de muestra o el resultado devuelto por un programa informático. Su contenido se muestra habitualmente utilizando la fuente monoespaciada predeterminada del navegador (como Courier o Lucida Console) para diferenciarlo del texto normal.

Veamos un par de ejemplos:

Estaba intentando arrancar mi ordenador, pero me salió este mensaje tan irónico:

Teclado no encontrado
Pulse F1 para continuar

Cuando el proceso finalice, la utilidad mostrará el texto Escaneo completado. Se han encontrado N resultados. Entonces podrás proceder al siguiente paso.


Código HTML del ejemplo:

<p>Estaba intentando arrancar mi ordenador, pero me salió este mensaje tan irónico:</p>

<p>
    <samp>Teclado no encontrado <br>Pulse F1 para continuar</samp>
</p>

<p>...</p>

<p>
    Cuando el proceso finalice, la utilidad mostrará el texto <samp>Escaneo completado. Se han encontrado <em>N</em> resultados.</samp> Entonces podrás proceder al siguiente paso.
</p>

Ejercicio propuesto: Salida de muestra

Crea una página web con el código del ejemplo anterior, junto con un par de párrafos más, y comprueba los resultados en tu navegador.

No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código para asegurar que es correcto

Un ejemplo completo

Veamos ahora un ejemplo completo que combina todos estos elementos (<code>, <pre>, <var>, <kbd>, <samp>) para ver cómo interactúan entre sí.

A continuación mostramos el resultado visual:

var para = document.querySelector('p');

para.onclick = function() {
  alert('¡Ay, deja de pincharme!');
}

No deberías utilizar elementos de presentación como <font> y <center>.

En el ejemplo de JavaScript anterior, para representa un elemento de párrafo.

Selecciona todo el texto con Ctrl/Cmd + A.

$ ping mozilla.org
PING mozilla.org (63.245.215.20): 56 data bytes
64 bytes from 63.245.215.20: icmp_seq=0 ttl=40 time=158.233 ms

Y aquí el código fuente correspondiente:

<pre><code>var para = document.querySelector('p');

para.onclick = function() {
  alert('¡Ay, deja de pincharme!');
}</code></pre>

<p>No deberías utilizar elementos de presentación como <code>&lt;font&gt;</code> y <code>&lt;center&gt;</code>.</p>

<p>En el ejemplo de JavaScript anterior, <var>para</var> representa un elemento de párrafo.</p>

<p>Selecciona todo el texto con <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> + <kbd>A</kbd>.</p>

<pre>$ <kbd>ping mozilla.org</kbd>
<samp>PING mozilla.org (63.245.215.20): 56 data bytes
64 bytes from 63.245.215.20: icmp_seq=0 ttl=40 time=158.233 ms</samp></pre>

Ejercicio propuesto: Código, atajos y salida

Crea una página web con el código del ejemplo anterior, junto con algún bloque <pre>...</pre> adicional, y comprueba los resultados en tu navegador.

No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código .

Ejercicio propuesto: Comandos de Linux

Crea una página web que muestre una tabla con algunos de los comandos de Linux más importantes y su descripción de uso, siguiendo aproximadamente el modelo de la tabla que se muestra a continuación.

Debes cumplir los siguientes requisitos semánticos:
1. Utiliza la etiqueta <code> para los comandos en la columna de la izquierda.
2. Utiliza la etiqueta <kbd> para los ejemplos de entrada del usuario (user’s input) dentro de las descripciones.
3. Recuerda que la tabla debe tener encabezados (<th>) y un título o leyenda (<caption>).
Comandos de Linux
Comandos Descripción
passwd Cambia tu contraseña de usuario:
1. Escribe tu antigua contraseña
2. Introduce la nueva contraseña
3. Confirma la nueva contraseña
~ Directorio personal del usuario (Home)
(atajo para: /home/usuario)
ls Lista las carpetas y archivos del directorio actual
mkdir Crea un nuevo directorio dentro del actual:
mkdir nuevodir
cd Cambia de directorio:
cd test (ir a un directorio llamado ‘test’)
cd .. (ir al directorio padre/superior)
cd ~ (ir al directorio personal)
rm Elimina el archivo o directorio especificado:
rm nombrearchivo (elimina un solo archivo)
rm *.txt (elimina TODOS los archivos .txt del directorio actual)
rm -r nombredir (elimina el directorio y sus archivos)

¡Por favor, ten cuidado al usar la opción -f!
rmdir Elimina el directorio VACÍO especificado
rmdir nombredir
pwd Imprime la ruta absoluta actual
man Muestra la página de manual del comando especificado:
man ls (muestra la ayuda de ls)
vi x.sh VI es un editor de texto. Si x.sh no existe, vi crea un nuevo archivo llamado x.sh y lo abre;
de lo contrario, simplemente abre el archivo existente.
less archivotexto less es un paginador de texto. Abre (solo lectura) el archivo archivotexto. Puedes usar las flechas arriba y abajo para desplazarte por el texto; comparte muchos comandos con VI.
chmod Cambia los permisos POSIX de un archivo o directorio. Permite proteger archivos contra accesos no deseados:
r : permiso de lectura
w : permiso de escritura
x : permiso de ejecución

chmod +x archivo.sh (permite la ejecución)
chmod -w archivo.sh (deniega la escritura)
chown Cambia el propietario de un archivo o directorio:
chown usuario archivo.sh
top Muestra los procesos en ejecución actualmente
cat Imprime el contenido de un archivo en pantalla
grep Filtra el archivo de texto especificado y muestra las líneas que contienen el patrón:
grep patron archivo.sh
También puedes usar tuberías (pipes) con la salida de otro comando:
cat archivo.sh | grep home
cat archivo.sh | grep "home page"

Marcado de datos de contacto

HTML proporciona un elemento específico para marcar semánticamente la información de contacto: <address>. Este elemento es muy versátil y puede utilizarse en diversos contextos; por ejemplo, para facilitar los datos de contacto de una empresa en el encabezado o pie de página de un sitio web, o para indicar quién es el autor de un artículo o una publicación concreta.

Su funcionamiento es sencillo: basta con envolver los detalles de contacto dentro de esta etiqueta. Veamos un ejemplo básico:

<address>
    <p>Fernando Ruiz, IES San Vicente, España</p>
</address>

No obstante, el contenido del elemento <address> puede albergar un marcado mucho más complejo. De hecho, la información suministrada puede adoptar la forma que mejor se ajuste al contexto, incluyendo cualquier dato necesario: dirección física, URL, correo electrónico, número de teléfono, redes sociales, coordenadas geográficas, etc. .

Regla importante: Debes tener en cuenta que siempre hay que incluir, como mínimo, el nombre de la persona, grupo u organización a la que hacen referencia dichos datos de contacto.

Veamos un ejemplo más completo y estructurado:

<address>
    <p>
        Juan Pérez<br>
        Calle Mayor, 10<br>
        Madrid, 28013<br>
        España
    </p>

    <ul>
        <li>Tel: 91 123 45 67</li>
        <li>Email: [email protected]</li>
    </ul>
</address>

También es correcto utilizar este elemento para referenciar información de contacto que se encuentra en otra página o enlace, como en el siguiente caso:

<address>
    Puedes contactar con el autor en <a href="http://www.midominio.com/contacto">www.midominio.com</a>.<br>

    Si encuentras algún error, por favor <a href="mailto:[email protected]">contacta con el webmaster</a>.<br>

    También puedes visitarnos en:<br>
    Fundación Mozilla<br>
    331 E Evelyn Ave<br>
    Mountain View, CA 94041<br>
    EE. UU.
</address>

Ejercicio propuesto: Información de contacto

Crea una página web utilizando el código de los ejemplos anteriores y verifica el resultado en tu navegador.

No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código para asegurar que la estructura es correcta .

El elemento <figure>

El elemento HTML <figure> (Figura con leyenda opcional) representa contenido independiente (self-contained content), el cual puede ir acompañado de un título o leyenda opcional especificado mediante el elemento <figcaption>.

La figura, su leyenda y su contenido se referencian semánticamente como una única unidad.

Figuras con imágenes

Este es el uso más común: asociar imágenes con sus pies de foto. Por ejemplo:

Paisaje nórdico.
Paisaje nórdico

Perro pidiendo algo de comida
Perro pidiendo comida

Código HTML del ejemplo:

<figure>
    <img src="https://picsum.photos/id/235/300/200"
         alt="Paisaje nórdico.">
    <figcaption>Paisaje nórdico</figcaption>
</figure>

<hr>

<figure>
    <img src="https://picsum.photos/id/237/300/200"
         alt="Perro pidiendo algo de comida">
    <figcaption>Perro pidiendo comida</figcaption>
</figure>

Ejercicio propuesto: Imágenes con leyendas

Crea una página web utilizando el código del ejemplo anterior, añade un par de figuras (<figure>) nuevas con imágenes y elige una leyenda adecuada (<figcaption>) para cada una. Finalmente, comprueba los resultados en tu navegador. No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código.

Puedes utilizar cualquier imagen que te guste; por ejemplo, las del servicio «https://picsum.photos/images«, tal como hicimos en ejercicios de la unidad anterior.

Figuras con poemas

El elemento <figure> no sirve solo para imágenes; también es excelente para enmarcar poemas o fragmentos literarios, como por ejemplo:

Caminante, son tus huellas
el camino y nada más;
Caminante, no hay camino,
se hace camino al andar.
Al andar se hace el camino,
y al volver la vista atrás
se ve la senda que nunca
se ha de volver a pisar.

Proverbios y cantares, de Antonio Machado

Código HTML del ejemplo:

<figure>
    <p>
        Caminante, son tus huellas<br>
        el camino y nada más;<br>
        Caminante, no hay camino,<br>
        se hace camino al andar.<br>
        Al andar se hace el camino,<br>
        y al volver la vista atrás<br>
        se ve la senda que nunca<br>
        se ha de volver a pisar.
    </p>
    <figcaption>
        <cite>Proverbios y cantares</cite>, de Antonio Machado
    </figcaption>
</figure>

Ejercicio propuesto: Poemas

Crea una página web con el código del ejemplo anterior (el de Machado), añade una nueva figura con otro poema que te guste (quizás de Lorca, Bécquer o Neruda) y comprueba los resultados en tu navegador.

No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código .

Figuras con código

El elemento <figure> es ideal para mostrar fragmentos de código que tienen un título descriptivo o que funcionan como ejemplos independientes.

Veamos un ejemplo práctico:

Obtener detalles del navegador usando navigator:
function EjemploNavigator() {
  var txt;
  txt = "Nombre en clave: " + navigator.appCodeName + "; ";
  txt+= "Nombre del navegador: " + navigator.appName + "; ";
  txt+= "Versión del navegador: " + navigator.appVersion + "; ";
  txt+= "Cookies habilitadas: " + navigator.cookieEnabled + "; ";
  txt+= "Plataforma: " + navigator.platform + "; ";
  txt+= "Cabecera User-agent: " + navigator.userAgent + "; ";
  
  console.log("EjemploNavigator", txt);
}

Código HTML del ejemplo:

<figure>
    <figcaption>Obtener detalles del navegador usando <code>navigator</code>:</figcaption>
    
    <pre><code>
      function EjemploNavigator() {
        var txt;
        txt = "Nombre en clave: " + navigator.appCodeName + "; ";
        txt+= "Nombre del navegador: " + navigator.appName + "; ";
        txt+= "Versión del navegador: " + navigator.appVersion + "; ";
        txt+= "Cookies habilitadas: " + navigator.cookieEnabled + "; ";
        txt+= "Plataforma: " + navigator.platform + "; ";
        txt+= "Cabecera User-agent: " + navigator.userAgent + "; ";
  
        console.log("EjemploNavigator", txt);
      }
    </code></pre>

</figure>

Ejercicio propuesto: Bloque de código en figura

Crea una página web con el código del ejemplo anterior, junto con otro más, y comprueba los resultados en tu navegador.

No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código .

Figuras con citas

Como vimos anteriormente, podemos usar <blockquote> por sí solo, pero si envolvemos la cita dentro de un elemento <figure>, podemos usar <figcaption> para indicar el autor o la fuente con mayor implicación semánticamente. Por ejemplo:

Edsger Dijkstra:

Si depurar es el proceso de eliminar errores de software, entonces programar debe ser el proceso de introducirlos.

Código HTML del ejemplo:

<figure>
    <figcaption><cite>Edsger Dijkstra:</cite></figcaption>
    <blockquote>
        Si depurar es el proceso de eliminar errores de software, 
        entonces programar debe ser el proceso de introducirlos.
    </blockquote>
</figure>

Ejercicio propuesto: Citas famosas (versión con figuras)

Crea una página web con el código del ejemplo anterior y añade algunas citas famosas más utilizando este mismo formato (<figure> + <figcaption> + <blockquote>) para mostrar al menos diez citas.

Finalmente, comprueba los resultados en tu navegador. No olvides incluir el resto de las etiquetas básicas de HTML y validar tu código.

Puedes reutilizar las citas que empleaste en ejercicios anteriores o buscar nuevas en los sitios que recomendamos previamente «https://proverbia.net/«, «https://www.mundifrases.com/«, o alguna en inglés como «https://www.brainyquote.com/«, o cualquier otra página web que conozcas.

Test

Comprueba tus conocimientos con este test sobre tablas y otros conceptos relacionados con esta unidad.

Aprende el abecedario en tres idiomas

Español

Resumen de audio

Resumen de vídeo

Canción del abecedario

A de abeja vuela sin parar,
Be de barco que va por el mar,
Ce de casa donde voy a jugar,
De de dado me gusta lanzar.

E de elefante grandote es,
Efe de flor perfumada a la vez,
Ge de gato saltando otra vez,
Hache de héroe valiente ya ves.

(Estribillo)
A be ce, vamos a aprender,
cantando juntos lo vas a saber.
Del a a la zeta lo vas a lograr,
el abecedario vas a dominar.

I de isla en el medio del mar,
Jota de jirafa que quiere bailar,
Ka de koala que quiere abrazar,
Ele de luna que se ve al brillar.

Eme de manzana roja y genial,
Ene de nube que vuela en el cielo,
Eñe de ñandú caminando ligero,
O de oso en un bosque de hielo.

(Estribillo)
A be ce, vamos a aprender,
cantando juntos lo vas a saber.
De la a a la zeta lo vas a lograr,
el abecedario vas a dominar.

Pe de pez nadando feliz,
Cu de queso que guardo yo aquí,
Erre de rana que hace “croac” así,
Ese de sol que ilumina mi país.

Te de tren que hace “chucu chucu” ya,
U de unicornio con magia y paz,
Uve de vaca que hace “muuu” de verdad,
Uve doble wifi pa’ conectarnos más.

Equis de xilófono que suena genial,
I griega de yogur que me voy a tomar,
Zeta de zorro que saluda al final.

(Estribillo final)
A be ce, ya lo sabes bien,
siempre cantando lo harás muy muy bien.
A be ce, vamos a aplaudir,
con el abecedario puedes sonreír.

Inglés

Resumen de audio

Resumen de vídeo

Catalán

Resumen de audio

Resumen de vídeo

Serpiente v2, un juego con música, efectos de sonido, colisiones, comida, imagen de fondo y game over, hecho con Godot

Assets

Pasos a seguir

Esqueleto del juego

extends Node2D

# ----------------------------------------
# SERPIENTE (SNAKE) v2 GODOT 4.x
# ----------------------------------------
#
# - Incorpora música de fondo, efectos de sonido (comer, morir).
# - Fondo de pantalla (imagen).
# - Imágenes para los segmentos de la serpiente (cabeza, cuerpo, cola) y la comida.
# - Pantalla de "Game Over" con imagen.
# - Botón de cerrar el juego.
# - Estructura de funciones similar al ejemplo de Asteroides.
#
# INPUT MAP necesario (Proyecto → Configuración del Proyecto → Mapa de Entrada):
#   "ui_left" (← / A), "ui_right" (→ / D), "ui_up" (↑ / W), "ui_down" (↓ / S)
#   "ui_accept" (Enter/Espacio) para reiniciar.
#
# Imágenes y sonidos en la carpeta 'assets':
#   cabeza_serpiente.png, cuerpo_serpiente.png, cola_serpiente.png, comida_manzana.png
#   fondo_jungla.png, game_over_snake.png, boton_cerrar.png
#   musica_fondo_snake.mp3, sfx_comer.mp3, sfx_muerte_snake.mp3

# -------------------------
# CONSTANTES DE CONFIGURACIÓN
# -------------------------
const TAM_CUADRICULA = 75    # Tamaño de cada "cuadrado" de la cuadrícula en píxeles
const TAM_CABEZA = 90        # Tamaño de la cabeza en píxeles
const TAM_COMIDA = 90        # Tamaño de la comida en píxeles
const VELOCIDAD_JUEGO = 0.2  # Segundos entre cada movimiento (más bajo = más rápido)
const TAM_BOTON_CERRAR = 50
const TAM_TEXTO = 40
const PAUSA_GAME_OVER = 1

# Colores (para el texto de la UI)
const COLOR_TEXTO = Color(1, 1, 1, 0.8)

# Imágenes (pon los archivos en la carpeta 'assets')
const TEX_FONDO: Texture2D = preload("res://assets/fondo.png")
const TEX_CABEZA_SERPIENTE: Texture2D = preload("res://assets/cabeza.png")
const TEX_CUERPO_SERPIENTE: Texture2D = preload("res://assets/cuerpo.png")
const TEX_COLA_SERPIENTE: Texture2D = preload("res://assets/cola.png")
const TEX_COMIDA: Texture2D = preload("res://assets/comida.png")
const TEX_GAME_OVER: Texture2D = preload("res://assets/game_over.png")
const TEX_BOTON_CERRAR: Texture2D = preload("res://assets/boton_cerrar.png")

# Audios (pon los archivos en la carpeta 'assets')
const MUSICA_FONDO: AudioStream = preload("res://assets/musica_fondo.mp3")
const SFX_COMER: AudioStream = preload("res://assets/sfx_comer.mp3")
const SFX_MUERTE: AudioStream = preload("res://assets/sfx_muerte.mp3")

# -------------------------
# ESTADO DEL JUEGO
# -------------------------
var pantalla: Vector2
var cuadricula_ancho: int
var cuadricula_alto: int

# La serpiente es un array de posiciones en la *cuadrícula* (no píxeles)
# La cabeza es el *último* elemento (serpiente.back())
var serpiente: Array[Vector2] = []
var comida: Vector2 # Posición de la comida en la cuadrícula
var direccion: Vector2 = Vector2.RIGHT
var proxima_direccion: Vector2 = Vector2.RIGHT

var muerto: bool = false
var crecer_serpiente: bool = false
var puntuacion: int = 0

# Control del "tick" del juego usando _process
var tiempo_acumulado: float = 0.0

# UI
var etiqueta_puntuacion: Label
var boton_cerrar: Rect2

# Audio players
var musica_fondo_player: AudioStreamPlayer
var sfx_comer_player: AudioStreamPlayer
var sfx_muerte_player: AudioStreamPlayer

# -------------------------
# CICLO DE VIDA PRINCIPAL
# -------------------------
func _ready():
	# Ejecutar al iniciar la escena
	randomize()
	_inicializar_pantalla()
	_crear_ui()
	_crear_boton_cerrar()
	_inicializar_audio()
	_reproducir_audio(musica_fondo_player) # Reproducir música de fondo en bucle
	_iniciar_juego()

func _process(delta: float):
	# Ejecutar la lógica del juego en cada frame
	
	# Si estamos muertos o en pausa, no hacemos nada más que esperar el reinicio
	if muerto: return

	# Acumulamos el tiempo para simular un "tick" de juego
	tiempo_acumulado += delta
	
	# Si no ha pasado suficiente tiempo, salimos
	if tiempo_acumulado < VELOCIDAD_JUEGO: return
	
	# ¡Tick! Es hora de moverse. Reiniciamos el acumulador
	tiempo_acumulado = 0.0
	
	# La lógica principal se ejecuta aquí, a la velocidad de VELOCIDAD_JUEGO
	_actualizar_movimiento()
	
	# Pedir a Godot que vuelva a dibujar la pantalla
	queue_redraw()

func _draw():
	# Dibujar en pantalla los elementos del juego
	# El orden importa (lo que se dibuja último, queda encima)
	_dibujar_fondo()
	_dibujar_serpiente()
	_dibujar_comida()
	_mostrar_boton_cerrar()
	if muerto: _mostrar_game_over()


func _input(event: InputEvent):
	# Comprobar la entrada de teclado, ratón o táctil
	_comprobar_controles(event)

# -------------------------
# INICIALIZACIÓN BÁSICA
# -------------------------
func _inicializar_pantalla():
	# Guardar el tamaño actual de la pantalla (ancho y alto)
	pass

# -------------------------
# AUDIO
# -------------------------
func _crear_audio_player(stream: AudioStream, bus: String, volumen = 0.0):
	# Instanciar y configurar un AudioStreamPlayer con stream, bus y volumen inicial, y devolverlo
	pass

func _inicializar_audio():
	# Construir y registrar players de música y SFX con buses/volúmenes apropiados
	pass

func _reproducir_audio(audio_player: AudioStreamPlayer):
	# Reproducir el AudioStreamPlayer
	pass

func _detener_audio(audio_player: AudioStreamPlayer):
	# Parar el AudioStreamPlayer
	pass

# -------------------------
# UI (INTERFAZ DE USUARIO)
# -------------------------
func _crear_ui():
	# Crear un Label para la puntuación
	pass

func _actualizar_etiqueta_puntuacion():
	# Muestra la puntuación actual en pantalla
	pass

# -------------------------
# BOTÓN CERRAR
# -------------------------
func _crear_boton_cerrar():
	# Definir el área clicable del botón de cierre en la esquina superior derecha
	pass

func _mostrar_boton_cerrar():
	# Dibujar la textura del botón de cierre dentro de su rectángulo clicable
	pass

# -------------------------
# LÓGICA DEL JUEGO (MOVIMIENTO)
# -------------------------
func _iniciar_juego():
	# (Re)iniciar todas las variables del juego
	pass

func _actualizar_movimiento():
	# Esta función se llama en cada "tick" (definido por VELOCIDAD_JUEGO)
	pass

# -------------------------
# SERPIENTE
# -------------------------
func _mover_serpiente(nueva_cabeza: Vector2):
	# Añadir la nueva cabeza al final del array
	pass

func _convertir_direccion_a_angulo(dir: Vector2):
	# Convertir dirección (Vector2) a ángulo en radianes (0° = derecha)
	pass

func _dibujar_textura_rotada_centrada(tex: Texture2D, centro: Vector2, angulo: float, tamano: Vector2):
	# Dibujar textura rotada alrededor de un punto central
	pass

func _dibujar_serpiente():
	pass

# -------------------------
# COMIDA
# -------------------------
func _mover_comida():
	# Mover la comida a una posición aleatoria que no esté sobre la serpiente
	pass

func _dibujar_comida():
	# Dibujar la comida en su posición
	pass

func _comprobar_comida(cabeza: Vector2):
	# Comprobar si la cabeza está en la misma casilla que la comida
	pass

# -------------------------
# CONTROLES
# -------------------------
func _comprobar_controles(event: InputEvent):
	# Comprobación del botón de cerrar
	pass

# -------------------------
# COLISIONES Y GAME OVER
# -------------------------
func _comprobar_colisiones(cabeza: Vector2):
	# Comprueba si la cabeza ha chocado con algo. Devuelve 'true' si hay colisión.
	pass

func _pausa_game_over():
	# Activar una pausa breve no bloqueante tras el Game Over esperando a un temporizador asincrónico
	pass

func _game_over():
	# Termina el juego
	pass

func _reiniciar_juego():
	# Reiniciar la escena actual para devolver todo a su estado inicial
	pass

# -------------------------
# FONDO Y CAPAS
# -------------------------
func _dibujar_fondo():
	# Dibujar la imagen de fondo que cubre toda la pantalla
	pass

func _mostrar_game_over():
	# Dibujar la textura de Game Over. Podemos modularla para un efecto.
	pass

inicializar_pantalla()

Esta función se encarga de configurar las dimensiones del juego. Primero, obtiene el tamaño actual de la ventana o viewport (el área visible del juego) y lo almacena en la variable pantalla. Luego, basándose en el tamaño de la pantalla en píxeles y la constante TAM_CUADRICULA (que define el tamaño de un «cuadrado» de nuestro tablero), calcula cuántos cuadrados caben a lo ancho (cuadricula_ancho) y a lo alto (cuadricula_alto). Esto nos permite trabajar con coordenadas de cuadrícula (ej. 0,0 o 5,10) en lugar de píxeles (ej. 0,0 o 640,1280).

func _inicializar_pantalla():
	# Guardar el tamaño actual de la pantalla (ancho y alto)
	pantalla = get_viewport_rect().size
	# Calcular cuántos "cuadrados" de la cuadrícula caben en la pantalla
	cuadricula_ancho = int(pantalla.x / TAM_CUADRICULA)
	cuadricula_alto = int(pantalla.y / TAM_CUADRICULA)

crear_audio_player()

Esta es una función «ayudante» (o helper function) diseñada para crear y configurar reproductores de sonido de forma limpia y reutilizable. Recibe el archivo de audio (stream), el canal por donde debe sonar (bus, ej: «Music» o «SFX») y un volumen opcional. Crea un nuevo nodo AudioStreamPlayer, le asigna estas propiedades, lo añade como hijo a la escena actual (usando add_child, lo cual es crucial para que funcione) y finalmente devuelve el nodo ya configurado.

func _crear_audio_player(stream: AudioStream, bus: String, volumen = 0.0):
	# Instanciar y configurar un AudioStreamPlayer con stream, bus y volumen inicial, y devolverlo
	var player = AudioStreamPlayer.new()
	player.stream = stream
	player.bus = bus
	player.volume_db = volumen
	add_child(player)
	return player

inicializar_audio()

Esta función utiliza la función ayudante _crear_audio_player (que acabamos de ver) para preparar todos los sonidos que el juego necesitará. Crea tres reproductores de audio y los asigna a las variables globales del script: musica_fondo_player (asignado al bus «Music»), sfx_comer_player (asignado al bus «SFX») y sfx_muerte_player (también en «SFX»).

func _inicializar_audio():
	# Construir y registrar players de música y SFX con buses/volúmenes apropiados
	musica_fondo_player  = _crear_audio_player(MUSICA_FONDO, "Music", -5.0)
	sfx_comer_player     = _crear_audio_player(SFX_COMER, "SFX", 0.0)
	sfx_muerte_player    = _crear_audio_player(SFX_MUERTE, "SFX")
	
	# La música de fondo se reproduce en bloque
	musica_fondo_player.finished.connect(func(): musica_fondo_player.play())

reproducir_audio()

Esta función se usa para reproducir un sonido. Recibe el audio_player que debe sonar. Primero comprueba que el player existe (if audio_player) y que tiene un archivo de sonido cargado (and audio_player.stream). Si es así, inicia la reproducción con play(). Adicionalmente, tiene un parámetro loop (bucle) que si se establece en true, conectará una señal (finished) para que, cuando el sonido termine, vuelva a empezar automáticamente.

func _reproducir_audio(audio_player: AudioStreamPlayer):
	# Reproducir el AudioStreamPlayer
	if audio_player and audio_player.stream: audio_player.play()

detener_audio()

Función simple para parar un sonido. Comprueba si el audio_player existe y si se está reproduciendo actualmente (.playing). Si ambas condiciones son ciertas, detiene la reproducción inmediatamente usando stop().

func _detener_audio(audio_player: AudioStreamPlayer):
	# Parar el AudioStreamPlayer
	if audio_player and audio_player.playing: audio_player.stop()

crear_ui()

Prepara la interfaz de usuario básica. En este caso, crea un nuevo nodo de tipo Label (etiqueta de texto) y lo asigna a la variable etiqueta_puntuacion. Establece su posición en la esquina superior izquierda (10, 10). Luego, configura programáticamente su apariencia (tamaño de fuente TAM_TEXTO y color COLOR_TEXTO) usando un objeto LabelSettings. Finalmente, añade la etiqueta a la escena con add_child().

func _crear_ui():
	# Crear un Label para la puntuación
	etiqueta_puntuacion = Label.new()
	etiqueta_puntuacion.position = Vector2(10, 10)
	
	# Configurar la fuente (programáticamente)
	var fuente_puntuacion = LabelSettings.new()
	fuente_puntuacion.font_size = TAM_TEXTO
	fuente_puntuacion.font_color = COLOR_TEXTO
	etiqueta_puntuacion.label_settings = fuente_puntuacion
	add_child(etiqueta_puntuacion)

actualizar_etiqueta_puntuacion()

Esta función se llama cada vez que la puntuación cambia (al comer una fruta o al iniciar el juego). Simplemente actualiza la propiedad text de la etiqueta_puntuacion (creada en la función anterior) para mostrar el texto «Puntuación: » seguido del valor actual de la variable puntuacion.

func _actualizar_etiqueta_puntuacion():
	# Muestra la puntuación actual en pantalla
	etiqueta_puntuacion.text = "Puntuación: %s" % puntuacion

crear_boton_cerrar()

Esta función no crea un nodo de botón, sino que define el área donde el botón de cerrar será «clicable». Crea un Rect2 (un rectángulo) usando las coordenadas de la pantalla (pantalla.x) y el tamaño del botón (TAM_BOTON_CERRAR) para posicionarlo en la esquina superior derecha, con un pequeño margen de 10 píxeles. Esta área se almacenará en la variable boton_cerrar y se usará después en _input para detectar clics.

func _crear_boton_cerrar():
	# Definir el área clicable del botón de cierre en la esquina superior derecha
	boton_cerrar = Rect2(pantalla.x - TAM_BOTON_CERRAR - 10, 10, TAM_BOTON_CERRAR, TAM_BOTON_CERRAR)

mostrar_boton_cerrar()

Esta función se llama dentro de _draw(). Se encarga de dibujar la textura del botón (TEX_BOTON_CERRAR) en la pantalla, usando exactamente el rectángulo (boton_cerrar) que definimos en la función anterior. También le aplica un tinte (un color gris semitransparente) para que no sea tan brillante.

func _mostrar_boton_cerrar():
	# Dibujar la textura del botón de cierre dentro de su rectángulo clicable
	draw_texture_rect(TEX_BOTON_CERRAR, boton_cerrar, false, Color(0.8, 0.8, 0.8, 1.0))

iniciar_juego()

Esta es la función clave para empezar (o reiniciar) una partida. Restablece todas las variables de estado a sus valores iniciales: vacía la serpiente (serpiente.clear()), pone la puntuación a 0, actualiza la etiqueta, quita el estado muerto, resetea la direccion a Vector2.RIGHT y reinicia el tiempo_acumulado. Después, crea la serpiente inicial añadiendo tres segmentos (cola, cuerpo y cabeza) al array serpiente en posiciones de cuadrícula fijas. Finalmente, llama a _mover_comida() para colocar la primera manzana.

func _iniciar_juego():
	# (Re)iniciar todas las variables del juego
	serpiente.clear()
	puntuacion = 0
	_actualizar_etiqueta_puntuacion()
	muerto = false
	crecer_serpiente = false
	direccion = Vector2.RIGHT
	proxima_direccion = Vector2.RIGHT
	tiempo_acumulado = 0.0

	# Crear serpiente inicial (en posiciones de cuadrícula)
	serpiente.push_back(Vector2(3, 5)) # Cola
	serpiente.push_back(Vector2(4, 5)) # Cuerpo
	serpiente.push_back(Vector2(5, 5)) # Cabeza

	_mover_comida()

actualizar_movimiento()

Este es el corazón de la lógica del juego, se ejecuta en cada «tick» (controlado por VELOCIDAD_JUEGO en _process). Primero, actualiza la direccion actual con la proxima_direccion (que guardó el input). Segundo, calcula cuál será la nueva_cabeza sumando la dirección a la posición de la cabeza actual. Tercero, comprueba si esa nueva_cabeza choca con algo (_comprobar_colisiones); si choca, llama a _game_over() y se detiene. Cuarto, comprueba si come (_comprobar_comida). Quinto y último, llama a _mover_serpiente() para efectuar el movimiento.

func _actualizar_movimiento():
	# Esta función se llama en cada "tick" (definido por VELOCIDAD_JUEGO)
	
	# 1. Actualizar dirección
	direccion = proxima_direccion
	
	# 2. Calcular nueva posición de la cabeza
	var cabeza_actual = serpiente.back() # .back() es el último elemento
	var nueva_cabeza = cabeza_actual + direccion
	
	# 3. Comprobar colisiones
	if _comprobar_colisiones(nueva_cabeza):
		_game_over()
		return # Detener movimiento si morimos
	
	# 4. Comprobar si comemos
	_comprobar_comida(nueva_cabeza)
	
	# 5. Mover serpiente
	_mover_serpiente(nueva_cabeza)

mover_serpiente()

Esta función actualiza el array serpiente para simular el movimiento. Siempre añade la nueva_cabeza al final del array (push_back). Luego, comprueba la variable crecer_serpiente. Si es false (no hemos comido), elimina el primer segmento del array (pop_front), que es la cola. Esto hace que la serpiente mantenga su tamaño pero avance. Si crecer_serpiente es true (hemos comido), no elimina la cola, haciendo que la serpiente crezca un segmento, y resetea el flag a false.

func _mover_serpiente(nueva_cabeza: Vector2):
	# Añadir la nueva cabeza al final del array
	serpiente.push_back(nueva_cabeza)

	# Si no hemos comido (no crecemos), borramos el primer segmento (la cola)
	if not crecer_serpiente:
		serpiente.pop_front() # .pop_front() elimina el primer elemento
	else:
		# Si crecimos, reseteamos el flag y no borramos la cola
		crecer_serpiente = false

convertir_direccion_a_angulo()

Esta función recibe una dirección representada como un vector 2D (como Vector2.UP, Vector2.RIGHT, etc.) y devuelve el ángulo correspondiente en radianes, asumiendo que la orientación por defecto apunta hacia la derecha (orientación estándar en muchos motores 2D). Usa una estructura match para evaluar la dirección de entrada: si es UP, devuelve -90 grados convertidos a radianes (lo que apunta hacia arriba); si es DOWN, devuelve 90 grados (hacia abajo); si es LEFT, devuelve 180 grados (hacia la izquierda); y para cualquier otro caso —especialmente Vector2.RIGHT—, devuelve 0.0, ya que esa es la orientación base. Esta función es útil para rotar texturas (como la cabeza de la serpiente) de forma que siempre miren en la dirección en la que se están moviendo.

func _convertir_direccion_a_angulo(dir: Vector2):
	# Convertir dirección (Vector2) a ángulo en radianes (0° = derecha)
	match dir:
		Vector2.UP:    return deg_to_rad(-90)
		Vector2.DOWN:  return deg_to_rad(90)
		Vector2.LEFT:  return deg_to_rad(180)
		_:             return 0.0  # Vector2.RIGHT

dibujar_textura_rotada_centrada()

Esta función se encarga de dibujar una textura (tex) en la pantalla rotada alrededor de su propio centro, en una posición específica (centro). Para lograrlo, primero aplica una transformación de dibujo con draw_set_transform(centro, angulo, Vector2(1, 1)), que desplaza el origen del sistema de coordenadas al punto centro y lo rota según el ángulo dado (en radianes). Luego, dibuja la textura usando draw_texture_rect, pero con un rectángulo cuyo origen está en -tamano / 2.0, lo que centra la textura respecto al nuevo origen (el punto centro). Finalmente, restablece la transformación de dibujo a la identidad con draw_set_transform(Vector2.ZERO, 0.0, Vector2(1, 1)) para que los siguientes dibujos no se vean afectados por esta rotación ni traslación. Es una forma común y eficaz de dibujar sprites rotados centrados en Godot usando el sistema de dibujo personalizado (_draw).

func _dibujar_textura_rotada_centrada(tex: Texture2D, centro: Vector2, angulo: float, tamano: Vector2):
	# Dibujar textura rotada alrededor de un punto central
	draw_set_transform(centro, angulo, Vector2(1, 1))
	draw_texture_rect(tex, Rect2(-tamano / 2.0, tamano), false)
	draw_set_transform(Vector2.ZERO, 0.0, Vector2(1, 1))

dibujar_serpiente()

Esta función se encarga de renderizar visualmente todos los segmentos de la serpiente en la pantalla, diferenciando entre cabeza, cuerpo y cola, y orientando cada parte según la dirección en la que apunta. Primero verifica que la serpiente no esté vacía; si lo está, no hace nada. Luego, recorre cada segmento (almacenado como coordenadas en una cuadrícula) y convierte su posición a píxeles. Calcula el centro de la celda para dibujar la textura centrada. Dependiendo de la posición del segmento en el array (i), asigna la textura correspondiente: la cabeza (último elemento) se orienta según la dirección actual de movimiento (direccion); la cola (primer elemento) se orienta según la dirección desde la cola hacia el siguiente segmento (serpiente[1] - serpiente[0]); y los segmentos intermedios del cuerpo se orientan según la dirección entre el segmento actual y el siguiente (serpiente[i + 1] - serpiente[i]). En todos los casos, usa _dibujar_textura_rotada_centrada() para dibujar la textura rotada correctamente alrededor de su centro, logrando una serpiente visualmente coherente y alineada con su trayectoria.

func _dibujar_serpiente():
	# Dibujar todos los segmentos de la serpiente (cabeza, cuerpo, cola) en la pantalla
	if serpiente.is_empty(): return

	for i in range(serpiente.size()):
		var posicion_celda = serpiente[i]
		var posicion_pixel = posicion_celda * TAM_CUADRICULA
		var centro = posicion_pixel + Vector2(TAM_CUADRICULA, TAM_CUADRICULA) / 2.0
		var textura: Texture2D
		var angulo_rotacion = 0.0

		if i == serpiente.size() - 1:  # Cabeza
			textura = TEX_CABEZA_SERPIENTE
			angulo_rotacion = _convertir_direccion_a_angulo(direccion)
			_dibujar_textura_rotada_centrada(textura, centro, angulo_rotacion, Vector2(TAM_CABEZA, TAM_CABEZA))
		elif i == 0:  # Cola
			textura = TEX_COLA_SERPIENTE
			var dir_cola = serpiente[1] - serpiente[0]
			angulo_rotacion = _convertir_direccion_a_angulo(dir_cola)
			_dibujar_textura_rotada_centrada(textura, centro, angulo_rotacion, Vector2(TAM_CUADRICULA, TAM_CUADRICULA))
		else:  # Cuerpo
			textura = TEX_CUERPO_SERPIENTE
			var dir_cuerpo = serpiente[i + 1] - serpiente[i]
			angulo_rotacion = _convertir_direccion_a_angulo(dir_cuerpo)
			_dibujar_textura_rotada_centrada(textura, centro, angulo_rotacion, Vector2(TAM_CUADRICULA, TAM_CUADRICULA))

mover_comida()

Esta función coloca la comida en una posición aleatoria dentro de la cuadrícula del juego, asegurándose de que no aparezca encima de ningún segmento de la serpiente. Para ello, entra en un bucle infinito (while true) que genera repetidamente coordenadas aleatorias (nueva_pos) usando randi_range, dentro de los límites horizontales (cuadricula_ancho) y verticales (cuadricula_alto). Cada vez que genera una posición, verifica si esa coordenada no está ya ocupada por la serpiente (con nueva_pos not in serpiente). En cuanto encuentra una ubicación libre, sale del bucle con break y asigna esa posición a la variable global comida. Este enfoque garantiza que la comida siempre aparezca en un lugar accesible.

func _mover_comida():
	# Mover la comida a una posición aleatoria que no esté sobre la serpiente
	var nueva_pos: Vector2
	
	# Generar posiciones aleatorias hasta encontrar una que no esté ocupada por la serpiente
	while true:
		nueva_pos = Vector2(
			randi_range(0, cuadricula_ancho - 1),
			randi_range(0, cuadricula_alto - 1)
		)
		if nueva_pos not in serpiente: break  # Salir del bucle cuando la posición sea válida
	
	# Asignar la posición válida a la comida
	comida = nueva_pos

dibujar_comida()

Esta función se encarga de renderizar la comida en la pantalla de forma centrada dentro de su celda de la cuadrícula. Primero calcula el centro exacto de la celda donde se encuentra la comida: multiplica las coordenadas de la cuadrícula (comida) por el tamaño de cada celda (TAM_CUADRICULA) para obtener la esquina superior izquierda, y luego suma la mitad del tamaño de la celda (TAM_CUADRICULA / 2) en ambas direcciones (usando Vector2.ONE) para llegar al centro. Luego, llama a la función auxiliar _dibujar_textura_rotada_centrada() pasando la textura de la comida (TEX_COMIDA), ese punto central, un ángulo de rotación de 0.0 (ya que la comida no necesita girar) y su tamaño personalizado (TAM_COMIDA). Esto asegura que la comida se dibuje perfectamente centrada en su celda, independientemente del tamaño de la textura o de la cuadrícula, manteniendo una apariencia limpia y alineada con el resto del juego.

func _dibujar_comida():
	# Dibujar la comida en su posición, centrada y con su tamaño personalizado
	
	# 1. Calcular el centro de la celda de la cuadrícula
	var centro_celda = (comida * TAM_CUADRICULA) + (Vector2.ONE * TAM_CUADRICULA / 2.0)
	
	# 2. Llamar a nuestra función auxiliar para dibujar la comida
	_dibujar_textura_rotada_centrada(
		TEX_COMIDA,
		centro_celda,
		0.0, # Ángulo (0.0 para la comida)
		Vector2(TAM_COMIDA, TAM_COMIDA) # Tamaño
	)

comprobar_comida()

Esta función, llamada en _actualizar_movimiento, comprueba si la cabeza de la serpiente ha aterrizado en la misma casilla que la comida. Si cabeza == comida, activa el flag crecer_serpiente a true (para que _mover_serpiente la haga crecer), incrementa la puntuacion, actualiza la etiqueta de texto, llama a _mover_comida() para buscar una nueva posición, y reproduce el sonido de comer (sfx_comer_player).

func _comprobar_comida(cabeza: Vector2):
	# Comprobar si la cabeza está en la misma casilla que la comida
	if cabeza == comida:
		crecer_serpiente = true # Marcar para crecer en el próximo movimiento
		puntuacion += 1
		_actualizar_etiqueta_puntuacion()
		_mover_comida() # Mover la comida a un nuevo sitio
		_reproducir_audio(sfx_comer_player) # Reproducir SFX de comer

comprobar_controles()

Esta función gestiona toda la entrada del jugador de forma ordenada y segura. Primero, verifica si el usuario ha hecho clic (con ratón o pantalla táctil) dentro del área del botón de cerrar; si es así, finaliza la aplicación de forma segura usando get_tree().quit.call_deferred(). Luego, si el juego ya terminó (muerto == true), únicamente permite reiniciar al pulsar la acción "ui_accept" (como Enter o Espacio), ignorando cualquier otro input. Finalmente, cuando el juego está en marcha, traduce las entradas de dirección (ui_up, ui_down, etc.) a vectores (Vector2.UP, DOWN, etc.), pero evita giros completos sobre sí misma (por ejemplo, de abajo a arriba) comparando la dirección propuesta con la opuesta a la actual (-direccion). Solo si la nueva dirección es válida (distinta de la opuesta), se asigna a proxima_direccion, lo que garantiza un movimiento fluido y evita que la serpiente se suicide al retroceder directamente sobre su propio cuerpo.

func _comprobar_controles(event: InputEvent):
	# Comprobación del botón de cerrar
	if (event is InputEventScreenTouch or event is InputEventMouseButton) and event.pressed:
		if boton_cerrar.has_point(event.position):
			if get_tree(): get_tree().quit.call_deferred() # Cerrar de forma segura
			return

	# Si el juego ha terminado, solo escuchamos 'Enter' para reiniciar
	if muerto:
		# 'ui_accept' es 'Enter' o 'Espacio'
		if event.is_action_pressed("ui_accept"): _reiniciar_juego()
		return

	# Si el juego está activo, leemos las flechas de dirección
	var direccion_opuesta = -direccion
	var nueva_dir = null

	if   event.is_action_pressed("ui_up"):    nueva_dir = Vector2.UP
	elif event.is_action_pressed("ui_down"):  nueva_dir = Vector2.DOWN
	elif event.is_action_pressed("ui_left"):  nueva_dir = Vector2.LEFT
	elif event.is_action_pressed("ui_right"): nueva_dir = Vector2.RIGHT
	
	# Evitar que la serpiente se dé la vuelta sobre sí misma	
	if nueva_dir and nueva_dir != direccion_opuesta: proxima_direccion = nueva_dir

comprobar_colisiones()

Esta función recibe la posición de la cabeza y devuelve true si hay colisión, o false si es seguro. Comprueba dos cosas: 1) Si la cabeza está fuera de los límites de la cuadrícula (ej. cabeza.x < 0). 2) Recorre el array serpiente (excepto el último segmento, que es la cabeza actual) y comprueba si la cabeza es igual a la posición de alguno de esos segmentos. Si cualquiera de estas comprobaciones es positiva, devuelve true.

func _comprobar_colisiones(cabeza: Vector2):
	# Comprueba si la cabeza ha chocado con algo. Devuelve 'true' si hay colisión.

	# Colisión con bordes
	if cabeza.x < 0 or cabeza.x >= cuadricula_ancho or \
	   cabeza.y < 0 or cabeza.y >= cuadricula_alto:
		return true

	# Colisión con el cuerpo (excluyendo la cabeza)
	return cabeza in serpiente.slice(0, -1)

pausa_game_over()

Esta es una función asíncrona (usa await) que sirve para crear una pequeña pausa después de morir. Al ser llamada, crea un temporizador (create_timer) con la duración PAUSA_GAME_OVER (1 segundo) y «espera» (await) a que ese temporizador emita la señal timeout. Esto evita que el jugador pueda reiniciar el juego instantáneamente al morir, dándole un segundo para ver qué pasó.

func _pausa_game_over():
	# Activar una pausa breve no bloqueante tras el Game Over esperando a un temporizador asincrónico
	await get_tree().create_timer(PAUSA_GAME_OVER).timeout

game_over()

Esta función se llama cuando _comprobar_colisiones detecta un choque. Establece el estado muerto a true, lo que detiene el bucle principal en _process. Detiene la música de fondo y reproduce el sonido de muerte. Llama a queue_redraw() para forzar un último dibujado (que mostrará la pantalla de Game Over) y finalmente llama a _pausa_game_over() para iniciar la pausa antes de poder reiniciar.

func _game_over():
	# Termina el juego
	muerto = true
	_detener_audio(musica_fondo_player)
	_reproducir_audio(sfx_muerte_player)
	queue_redraw() # Forzar un último dibujado para mostrar la pantalla de Game Over
	_pausa_game_over() # Añadir una breve pausa ant

reiniciar_juego()

La forma más sencilla y robusta de reiniciar el juego. Simplemente le pide al árbol de escenas (get_tree()) que vuelva a cargar la escena actual (reload_current_scene()). Esto restablece todas las variables y nodos a su estado inicial, como si el juego se acabara de abrir.

func _reiniciar_juego():
	# Reiniciar la escena actual para devolver todo a su estado inicial
	get_tree().reload_current_scene()

dibujar_fondo()

Función de dibujo llamada en _draw(). Dibuja la textura TEX_FONDO ocupando toda la pantalla (desde Vector2.ZERO hasta pantalla). Le aplica un tinte gris semitransparente para oscurecerla ligeramente y que la serpiente y la comida destaquen más.

func _dibujar_fondo():
	# Dibujar la imagen de fondo que cubre toda la pantalla
	draw_texture_rect(TEX_FONDO, Rect2(Vector2.ZERO, pantalla), false, Color(0.5, 0.5, 0.5))

mostrar_game_over()

Esta función de dibujo solo se activa (gracias a la comprobación if muerto: en _draw()) cuando el juego ha terminado. Dibuja la textura TEX_GAME_VER ocupando toda la pantalla, también con un tinte semitransparente.

func _mostrar_game_over():
	# Dibujar la textura de Game Over. Podemos modularla para un efecto.
	draw_texture_rect(TEX_GAME_OVER, Rect2(Vector2.ZERO, pantalla), false, Color(0.75, 0.75, 0.75, 0.75))

El resultado

Serpiente, un juego muy básico de movimiento y colisiones, hecho con Godot

Pasos a seguir

El esqueleto del juego

extends Node2D
# ----------------------------------------
# SERPIENTE (SNAKE) v1 GODOT 4.x
# ----------------------------------------
#
# - Usa _process y _draw para la lógica y el dibujado (sin Timer).
# - No usa imágenes, solo 'draw_rect' para los gráficos.
# - La UI (puntuación y Game Over) se crea como nodos Label.
#
# INPUT MAP necesario (Proyecto → Configuración del Proyecto → Mapa de Entrada):
#   "ui_left" (← / A), "ui_right" (→ / D), "ui_up" (↑ / W), "ui_down" (↓ / S)
#   "ui_accept" (Enter/Espacio) para reiniciar.
#

# -------------------------
# CONSTANTES DE CONFIGURACIÓN
# -------------------------
const TAM_CUADRICULA = 20   # Tamaño de cada "cuadrado" de la cuadrícula en píxeles
const VELOCIDAD_JUEGO = 0.1 # Segundos entre cada movimiento (más bajo = más rápido)

# Colores (en lugar de texturas)
const COLOR_FONDO = Color(0.1, 0.1, 0.1)
const COLOR_SERPIENTE = Color(0.2, 1, 0.2)
const COLOR_COMIDA = Color(1, 0, 0)
const COLOR_TEXTO = Color(1, 1, 1, 0.8)
const COLOR_TEXTO_GAME_OVER = Color(1, 0.8, 0.8)

# -------------------------
# ESTADO DEL JUEGO
# -------------------------
var pantalla: Vector2
var cuadricula_ancho: int
var cuadricula_alto: int

# La serpiente es un array de posiciones en la *cuadrícula* (no píxeles)
# La cabeza es el *último* elemento (serpiente.back())
var serpiente: Array[Vector2] = []
var comida: Vector2             # Posición de la comida en la cuadrícula
var direccion: Vector2 = Vector2.RIGHT
var proxima_direccion: Vector2 = Vector2.RIGHT # Para bufferizar el input

var muerto: bool = false
var crecer_serpiente: bool = false
var puntuacion: int = 0

# Control del "tick" del juego usando _process
var tiempo_acumulado: float = 0.0

# UI
var etiqueta_puntuacion: Label
var etiqueta_game_over: Label

# -------------------------
# CICLO DE VIDA PRINCIPAL
# -------------------------
func _ready():
    # Ejecutar al iniciar la escena
    randomize()
    _inicializar_pantalla()
    _crear_ui()
    _iniciar_juego()

func _process(delta: float):
    # Ejecutar la lógica del juego en cada frame
    
    # Si estamos muertos, no hacemos nada más que esperar el reinicio
    if muerto: return

    # Acumulamos el tiempo para simular un "tick" de juego
    tiempo_acumulado += delta
    
    # Si no ha pasado suficiente tiempo, salimos
    if tiempo_acumulado < VELOCIDAD_JUEGO: return
    
    # ¡Tick! Es hora de moverse. Reiniciamos el acumulador
    tiempo_acumulado = 0.0
    
    # La lógica principal se ejecuta aquí, a la velocidad de VELOCIDAD_JUEGO
    _actualizar_movimiento()
    
    # Pedir a Godot que vuelva a dibujar la pantalla
    queue_redraw()

func _draw():
    # Dibujar en pantalla los elementos del juego
    # El orden importa (lo que se dibuja último, queda encima)
    _dibujar_fondo()
    _dibujar_comida()
    _dibujar_serpiente()

func _input(event: InputEvent):
    # Comprobar la entrada de teclado, ratón o táctil
    _comprobar_controles(event)


# -------------------------
# INICIALIZACIÓN BÁSICA
# -------------------------
func _inicializar_pantalla():
    # Guardar el tamaño actual de la pantalla y calcular la cuadrícula
    pass

# -------------------------
# UI (INTERFAZ DE USUARIO)
# -------------------------
func _crear_ui():
    # Crear y configurar los nodos Label para la UI
    pass

func _actualizar_etiqueta_puntuacion():
    # Actualizar el texto del Label de puntuación
    pass

# -------------------------
# LÓGICA DEL JUEGO (MOVIMIENTO)
# -------------------------
func _iniciar_juego():
    # (Re)iniciar todas las variables del juego
    pass

func _actualizar_movimiento():
    # Esta función se llama en cada "tick"
    pass

# -------------------------
# SERPIENTE
# -------------------------
func _mover_serpiente(nueva_cabeza: Vector2):
    # Añadir nueva cabeza y quitar la cola (si no crecemos)
    pass

func _dibujar_serpiente():
    # Dibujar cada segmento de la serpiente
    pass

# -------------------------
# COMIDA
# -------------------------
func _mover_comida():
    # Mover la comida a una posición aleatoria válida
    pass

func _dibujar_comida():
    # Dibujar la comida en su posición
    pass

func _comprobar_comida(cabeza: Vector2):
    # Comprobar si la cabeza ha comido la comida
    pass

# -------------------------
# CONTROLES
# -------------------------
func _comprobar_controles(event: InputEvent):
    # Leer el input del jugador para cambiar de dirección o reiniciar
    pass

# -------------------------
# COLISIONES Y GAME OVER
# -------------------------
func _comprobar_colisiones(cabeza: Vector2):
    # Comprobar si la cabeza choca con los bordes o consigo misma
    pass # Reemplazar con 'return false' por ahora

func _game_over():
    # Termina el juego y muestra la pantalla de Game Over
    pass

# -------------------------
# FONDO
# -------------------------
func _dibujar_fondo():
    # Dibujar el rectángulo del fondo
    pass

inicializar_pantalla()

Esta función se llama una sola vez al principio, desde _ready, porque solo necesitamos calcular estos valores una vez. Su trabajo es «medir» la pantalla. Primero, get_viewport_rect().size nos da el tamaño en píxeles de la ventana del juego (por ejemplo, 1024×600). Luego, dividimos ese ancho y alto por nuestra TAM_CUADRICULA (que es 20). Si la pantalla mide 1024 píxeles de ancho, 1024 / 20 = 51.2. Al usar int(), nos quedamos con la parte entera (51). Esto nos dice que nuestra cuadrícula de juego tiene 51 celdas de ancho. Guardamos estos valores en cuadricula_ancho y cuadricula_alto para que más tarde, en _comprobar_colisiones, podamos saber si la serpiente se ha chocado contra el «borde».

func _inicializar_pantalla():
	# Guardar el tamaño actual de la pantalla (ancho y alto)
	pantalla = get_viewport_rect().size
	# Calcular cuántos "cuadrados" de la cuadrícula caben en la pantalla
	cuadricula_ancho = int(pantalla.x / TAM_CUADRICULA)
	cuadricula_alto = int(pantalla.y / TAM_CUADRICULA)

crear_ui()

Esta función también se llama desde _ready porque queremos que la Interfaz de Usuario (UI) exista desde el principio. En lugar de añadir los nodos Label arrastrándolos en el editor de escenas de Godot, los creamos «programáticamente» (por código) usando Label.new(). Esto mantiene nuestra escena principal más limpia y nos da control total. Para cada etiqueta, creamos una instancia, le damos propiedades (como la position o el text), y luego configuramos su estilo (fuente, color, tamaño) creando un recurso LabelSettings. Lo más importante es que, después de crearlos, debemos «engancharlos» a nuestra escena principal usando add_child(), de lo contrario, existirían en la memoria pero no se verían. Fíjate que la etiqueta etiqueta_game_over se crea, se centra, y al final se oculta con hide(); está lista y esperando, pero no la mostraremos hasta que el jugador pierda.

func _crear_ui():
	# Crear un Label para la puntuación
	etiqueta_puntuacion = Label.new()
	etiqueta_puntuacion.position = Vector2(10, 10)
	
	# Configurar la fuente (programáticamente)
	var fuente_puntuacion = LabelSettings.new()
	fuente_puntuacion.font_size = 24
	fuente_puntuacion.font_color = COLOR_TEXTO
	etiqueta_puntuacion.label_settings = fuente_puntuacion
	add_child(etiqueta_puntuacion) # ¡Importante añadirlo a la escena!

	# Crear un Label para el Game Over
	etiqueta_game_over = Label.new()
	etiqueta_game_over.text = "GAME OVER\nPulsa 'Enter' para reiniciar"
	etiqueta_game_over.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
	
	# Configurar la fuente
	var fuente_game_over = LabelSettings.new()
	fuente_game_over.font_size = 32
	fuente_game_over.font_color = COLOR_TEXTO_GAME_OVER
	fuente_game_over.outline_size = 4
	fuente_game_over.outline_color = Color(0,0,0)
	etiqueta_game_over.label_settings = fuente_game_over
	
	# Centrarla
	etiqueta_game_over.size.x = pantalla.x
	etiqueta_game_over.position = Vector2(0, pantalla.y / 2 - 50)
	etiqueta_game_over.hide() # Ocultarla al inicio
	add_child(etiqueta_game_over) # ¡Añadirlo también!

actualizar_etiqueta_puntuacion()

Es una función «ayudante» (o helper) muy simple. Podríamos haber escrito etiqueta_puntuacion.text = ... en dos sitios distintos (en _iniciar_juego y en _comprobar_comida), pero es una buena práctica de programación crear una función para tareas que se repiten (principio «Don’t Repeat Yourself» o DRY). Su única tarea es actualizar la propiedad text de nuestra etiqueta_puntuacion. La línea "...Puntuación: %s" % puntuacion usa un formato de string: el %s actúa como un «marcador de posición» (placeholder) que es reemplazado por el valor de la variable puntuacion. Godot convertirá automáticamente el número puntuacion a un string de texto.

func _actualizar_etiqueta_puntuacion():
	etiqueta_puntuacion.text = "Puntuación: %s" % puntuacion

iniciar_juego()

Esta es la función clave de (re)inicio. La llamamos en _ready para empezar la primera partida, pero también la llamaremos cada vez que el jugador pierda y pulse ‘Enter’. Por eso, tiene que ser un «reseteo» total. «Limpia» todas las variables de estado: serpiente.clear() vacía el Array de la serpiente anterior; puntuacion vuelve a 0 (e informamos a la UI con _actualizar_etiqueta_puntuacion()); muerto se pone en false para que _process vuelva a funcionar; y se resetea la dirección. Luego, «construimos» la serpiente inicial: usamos push_back() para añadir tres Vector2 (posiciones de cuadrícula) al array. El orden es importante: primero añadimos la cola (3,5), luego el cuerpo (4,5) y finalmente la cabeza (5,5). Al final del array (serpiente.back()) siempre estará la cabeza. Por último, llamamos a _mover_comida() para que ponga la primera manzana en el tablero.

func _iniciar_juego():
	# (Re)iniciar todas las variables del juego
	serpiente.clear()
	puntuacion = 0
	_actualizar_etiqueta_puntuacion()
	muerto = false
	crecer_serpiente = false
	direccion = Vector2.RIGHT
	proxima_direccion = Vector2.RIGHT
	etiqueta_game_over.hide()
	tiempo_acumulado = 0.0

	# Crear serpiente inicial (en posiciones de cuadrícula)
	# La cabeza será el último elemento (5,5)
	serpiente.push_back(Vector2(3, 5)) # Cola
	serpiente.push_back(Vector2(4, 5)) # Cuerpo
	serpiente.push_back(Vector2(5, 5)) # Cabeza

	_mover_comida()

actualizar_movimiento()

¡Este es el corazón de la lógica del juego! Esta función se llama en cada «tick» (es decir, cada VELOCIDAD_JUEGO segundos, gracias a nuestro contador en _process). Orquesta la secuencia completa de lo que debe pasar en un solo «paso» de la serpiente. El orden es muy importante: 1) Leemos la proxima_direccion (que el jugador pulsó) y la convertimos en la direccion actual. 2) Calculamos dónde estará la cabeza en el siguiente fotograma (nueva_cabeza). 3) ¡Paramos! Antes de movernos, comprobamos si esa nueva_cabeza choca con algo (_comprobar_colisiones). Si choca, llamamos a _game_over() y usamos return para salir de la función inmediatamente, cancelando el movimiento. 4) Si no chocamos, comprobamos si la nueva_cabeza está sobre la comida (_comprobar_comida). 5) Finalmente, y solo si hemos superado las comprobaciones, le decimos a la serpiente que se mueva a esa nueva_cabeza (_mover_serpiente).

func _actualizar_movimiento():
	# Esta función se llama en cada "tick" (definido por VELOCIDAD_JUEGO)
	
	# 1. Actualizar dirección
	direccion = proxima_direccion
	
	# 2. Calcular nueva posición de la cabeza
	var cabeza_actual = serpiente.back() # .back() es el último elemento
	var nueva_cabeza = cabeza_actual + direccion
	
	# 3. Comprobar colisiones
	if _comprobar_colisiones(nueva_cabeza):
		_game_over()
		return # Detener movimiento si morimos
	
	# 4. Comprobar si comemos
	_comprobar_comida(nueva_cabeza)
	
	# 5. Mover serpiente
	_mover_serpiente(nueva_cabeza)

mover_serpiente()

Esta función se encarga de la mecánica de «oruga» de la serpiente. Es un truco muy ingenioso que usa un Array (una lista). La serpiente siempre se mueve añadiendo una nueva cabeza en la posición calculada: serpiente.push_back(nueva_cabeza). Esto añade un elemento al final del array. Ahora mismo, la serpiente tiene un segmento de más. Entonces, comprobamos la variable crecer_serpiente. Si es false (no hemos comido en este tick), tenemos que mantener el tamaño original, así que eliminamos el primer segmento del array (la cola) usando serpiente.pop_front(). Si crecer_serpiente es true (porque acabamos de comer), ¡simplemente no llamamos a pop_front()! El resultado es que la serpiente tiene un segmento más, y hemos crecido. Reseteamos la variable a false para que en el próximo tick, si no comemos, volvamos a movernos con normalidad.

func _mover_serpiente(nueva_cabeza: Vector2):
	# Añadir la nueva cabeza al final del array
	serpiente.push_back(nueva_cabeza)

	# Si no hemos comido (no crecemos), borramos el primer segmento (la cola)
	if not crecer_serpiente:
		serpiente.pop_front() # .pop_front() elimina el primer elemento
	else:
		# Si crecimos, reseteamos el flag y no borramos la cola
		crecer_serpiente = false

dibujar_serpiente()

Esta función se llama automáticamente cada vez que Godot ejecuta _draw (lo cual forzamos en _process con queue_redraw()). Su trabajo es dibujar la serpiente, sin importar dónde esté o cuán larga sea. Lo hace recorriendo cada elemento (cada Vector2 de posición) en nuestro array serpiente con un bucle for segmento in serpiente:. Para cada uno de esos segmentos, tiene que convertir su posición de cuadrícula (ej: (5,5)) a píxeles en pantalla (ej: (100,100)). Esto lo hace con una simple multiplicación: pos_pixel = segmento * TAM_CUADRICULA. Finalmente, usa el comando draw_rect para dibujar un rectángulo de COLOR_SERPIENTE en esa posición de píxeles.

func _dibujar_serpiente():
	# Dibujar cada segmento de la serpiente
	for segmento in serpiente:
		# Convertir la posición de cuadrícula (ej: 5,5) a píxeles (ej: 100,100)
		var pos_pixel = segmento * TAM_CUADRICULA
		var rect = Rect2(pos_pixel, Vector2(TAM_CUADRICULA, TAM_CUADRICULA))
		draw_rect(rect, COLOR_SERPIENTE)

mover_comida()

Esta función tiene que encontrar un nuevo lugar para la manzana. No podemos simplemente ponerla en un sitio aleatorio, porque podría aparecer debajo de la serpiente, ¡haciendo imposible comerla! Por eso usamos un bucle while not pos_valida. Es un bucle que dice: «Repítete mientras no encuentres una posición válida». Dentro del bucle, primero somos optimistas (pos_valida = true). Luego, generamos una nueva_pos aleatoria en la cuadrícula (usando randi_range). Entonces, comprobamos esa nueva_pos contra todos los segmentos de la serpiente. Si encontramos una coincidencia (if segmento == nueva_pos), ponemos pos_valida = false y rompemos el bucle for. El bucle while ve que pos_valida es falsa y vuelve a empezar, buscando otra posición. Solo cuando el bucle for termina sin encontrar coincidencias, pos_valida se mantiene true y el while por fin termina. Asignamos esa nueva_pos 100% segura a la variable comida.

func _mover_comida():
	# Mover la comida a una posición aleatoria que no esté sobre la serpiente
	var pos_valida = false
	var nueva_pos: Vector2
	
	while not pos_valida:
		# Calcular una posición aleatoria en la cuadrícula
		nueva_pos = Vector2(
			randi_range(0, cuadricula_ancho - 1),
			randi_range(0, cuadricula_alto - 1)
		)
		
		# Comprobar si esa posición está sobre la serpiente
		pos_valida = true # Asumir que es válida...
		for segmento in serpiente:
			if segmento == nueva_pos:
				pos_valida = false # ...ops, no lo era. Repetir el 'while'.
				break
	
	# Si salimos del 'while', 'nueva_pos' es válida
	comida = nueva_pos

dibujar_comida()

Se llama dentro de la función principal _draw. Es muy similar a _dibujar_serpiente, pero mucho más simple porque solo hay una comida. No necesita un bucle for. Simplemente coge la posición de cuadrícula actual de la variable comida (que _mover_comida nos aseguró que es válida), la convierte a píxeles en pantalla (multiplicando por TAM_CUADRICULA), y dibuja un solo rectángulo de COLOR_COMIDA en esa posición usando draw_rect.

func _dibujar_comida():
	# Dibujar la comida en su posición
	var pos_pixel = comida * TAM_CUADRICULA
	var rect = Rect2(pos_pixel, Vector2(TAM_CUADRICULA, TAM_CUADRICULA))
	draw_rect(rect, COLOR_COMIDA)

comprobar_comida()

Se llama desde _actualizar_movimiento en cada tick. Recibe la nueva_cabeza (dónde va a estar la serpiente) como argumento. Su trabajo es una simple comprobación: if cabeza == comida:. Si la posición de la cabeza es exactamente la misma que la de la comida, ¡hemos comido! Esto dispara tres acciones: 1) Activamos el flag crecer_serpiente = true. _mover_serpiente verá esto en un momento y sabrá que no debe borrar la cola. 2) Aumentamos la puntuacion y llamamos a _actualizar_etiqueta_puntuacion para que el jugador vea su nuevo punto. 3) Inmediatamente llamamos a _mover_comida() para que busque un nuevo sitio para la siguiente manzana, antes de que volvamos a dibujar.

func _comprobar_comida(cabeza: Vector2):
	# Comprobar si la cabeza está en la misma casilla que la comida
	if cabeza == comida:
		crecer_serpiente = true # Marcar para crecer en el próximo movimiento
		puntuacion += 1
		_actualizar_etiqueta_puntuacion()
		_mover_comida() # Mover la comida a un nuevo sitio

comprobar_controles()

Esta función se llama automáticamente en _input cada vez que Godot detecta una entrada (como pulsar una tecla). A diferencia de _process, que se ejecuta constantemente, _input solo se ejecuta cuando algo pasa. Primero, comprueba si estamos muerto. Si es así, ignora todas las flechas y solo escucha ui_accept (Enter/Espacio) para reiniciar el juego. Si estamos vivos, escucha las flechas. La parte más importante es que no cambiamos direccion directamente, sino proxima_direccion. Esto es un «buffer» de entrada. ¿Por qué? Primero, evita que el jugador pulse dos teclas tan rápido dentro de un mismo tick que confunda al juego. Segundo, nos permite la lógica anti-suicidio: if direccion != Vector2.DOWN:. Esto comprueba que, si el jugador pulsa «arriba», nosotros no estemos ya yendo «abajo». Esto evita que la serpiente se choque consigo misma instantáneamente. La direccion real solo se actualiza en _actualizar_movimiento, al ritmo del «tick» del juego.

func _comprobar_controles(event: InputEvent):
	# Si el juego ha terminado, solo escuchamos 'Enter' para reiniciar
	if muerto:
		if event.is_action_pressed("ui_accept"): # 'ui_accept' es 'Enter' o 'Espacio'
			_iniciar_juego()
		return

	# Si el juego está activo, leemos las flechas de dirección
	if event.is_action_pressed("ui_up"):
		# Evitar que la serpiente se dé la vuelta sobre sí misma
		if direccion != Vector2.DOWN:
			proxima_direccion = Vector2.UP
	elif event.is_action_pressed("ui_down"):
		if direccion != Vector2.UP:
			proxima_direccion = Vector2.DOWN
	elif event.is_action_pressed("ui_left"):
		if direccion != Vector2.RIGHT:
			proxima_direccion = Vector2.LEFT
	elif event.is_action_pressed("ui_right"):
		if direccion != Vector2.LEFT:
			proxima_direccion = Vector2.RIGHT

comprobar_colisiones()

Esta función crucial decide si morimos o no. Fíjate en -> bool: esto significa que la función está obligada a devolver (return) un valor booleano (true o false). Se llama desde _actualizar_movimiento antes de mover la serpiente. Comprueba dos tipos de colisión: 1) Colisión con los bordes: Comprueba si la cabeza se ha salido de los límites de la cuadrícula. Es decir, si su x es menor que 0 (borde izquierdo) o mayor o igual que cuadricula_ancho (borde derecho), y lo mismo para y (arriba y abajo). 2) Colisión consigo misma: Recorre cada segmento en el array serpiente y comprueba si la nueva_cabeza está en la misma posición que cualquier otro segmento. Si cualquiera de estas dos condiciones es cierta, la función devuelve true (¡colisión!). Si se comprueban todos los bordes y todos los segmentos y no hay colisión, la función devuelve false (seguro).

func _comprobar_colisiones(cabeza: Vector2):
	# Comprueba si la cabeza ha chocado con algo. Devuelve 'true' si hay colisión.

	# 1. Colisión con los bordes de la pantalla (en coordenadas de cuadrícula)
	if cabeza.x < 0 or cabeza.x >= cuadricula_ancho or \
	   cabeza.y < 0 or cabeza.y >= cuadricula_alto:
		return true # Hay colisión

	# 2. Colisión consigo misma (chocar con cualquier segmento)
	for segmento in serpiente:
		if cabeza == segmento:
			return true # Hay colisión

	# Si no hemos chocado con nada
	return false

game_over()

Se llama desde _actualizar_movimiento tan pronto como _comprobar_colisiones devuelve true. Esta función simplemente activa el flag muerto = true. Al principio de _process, tenemos una línea que dice if muerto: return. Así que, al poner muerto = true, estamos deteniendo toda la lógica de movimiento y el juego se «congela» eficazmente. Segundo, llama a show() en la etiqueta de «Game Over» que creamos y ocultamos al inicio, mostrando el mensaje de reinicio al jugador.

func _game_over():
	# Termina el juego
	muerto = true
	etiqueta_game_over.show()
	queue_redraw() # Forzar un último dibujado (aunque _process esté parado)

dibujar_fondo()

La última función de dibujado, y la más simple. Es muy importante que sea la primera función que llamamos dentro de _draw. El dibujado en Godot funciona como un pintor: lo que dibujas primero, queda debajo. Por lo tanto, dibujamos el fondo primero, luego la comida, y finalmente la serpiente, para que la serpiente aparezca «encima» de la comida y del fondo. La función simplemente usa draw_rect para dibujar un rectángulo gigante del COLOR_FONDO que ocupa toda la pantalla, desde (0,0) (Vector2.ZERO) hasta el tamaño de la pantalla.

func _dibujar_fondo():
	# Dibujar un rectángulo grande del color de fondo que cubra toda la pantalla
	draw_rect(Rect2(Vector2.ZERO, pantalla), COLOR_FONDO)

El resultado

Desde Godot podemos exportar este mismo proyecto para poder jugar en cualquier navegador. Puedes ver el resultado y jugar directamente mediante el siguiente enlace: