Skip to main content

Cómo construir un plugin

Este tutorial fue escrito en febrero de 2026, para la v2 del motor.

Los plugins te permiten añadir métodos nuevos al motor y envolver los existentes — todo sin tocar el código del motor. Son perfectos para cosas que te encuentras reconstruyendo en cada proyecto: overlays de depuración, sacudida de pantalla, sistemas de partículas, ese tipo de cosas.

Vamos a construir un plugin de overlay de depuración desde cero. Mostrará los FPS y las coordenadas del ratón en pantalla, activado y desactivado con una sola llamada. En el camino cubriremos toda la API de plugins, y luego recorreremos cómo crear, probar y publicar tu plugin en Floaty.

The Plugin API

Un plugin es una función que pasas a engine.use(). Recibe dos herramientas: register para añadir métodos nuevos y wrap para decorar los existentes:

engine.use((register, wrap) => {
    // register('name', function(...args) { })
    // adds a brand-new method to the engine

    // wrap('name', function(original, ...args) { })
    // decorates an existing method
});

Eso es todo. register crea algo nuevo, wrap modifica algo que ya existe. Veamos cada uno.

Registering New Methods

register(name, fn) añade un método completamente nuevo al motor. Una vez registrado, está disponible en todas partes — en la instancia del motor directamente, y dentro de los callbacks de scope() junto a los métodos integrados.

Aquí hay un plugin que registra un método greet. Dentro de la función, this es la instancia del motor, así que tienes acceso a todos los métodos de dibujo y entrada:

engine.use((register) => {
    register('greet', function (x, y, color) {
        this.text('hello!', x, y, color);
    });
});

engine.scope(({ start, cls, greet }) => {
    function draw() {
        cls(0);
        greet(10, 10, 7);
    }

    start({ sprites: {}, sounds: {}, draw, target });
});

Fíjate que greet se desestructura junto con start, cls y el resto. Tu método de plugin es un ciudadano de primera clase.

Consejo: Si intentas registrar un nombre que ya existe — por ejemplo, cls o un método de otro plugin — el motor lanza un error útil que te indica usar wrap en su lugar. Sin sobreescrituras silenciosas.

Wrapping Existing Methods

wrap(name, fn) te permite interceptar un método existente. Tu función envolvente recibe el original como primer argumento — llámalo para mantener el comportamiento por defecto, y luego añade tu propia lógica antes o después.

Aquí hay un plugin que envuelve cls para dibujar un borde blanco alrededor de la pantalla después de cada limpieza:

engine.use((register, wrap) => {
    wrap('cls', function (original, color) {
        original(color);
        this.rect(0, 0, 127, 127, 7);
    });
});

Cada vez que algo llama a cls, primero ocurre la limpieza original, y luego el borde se dibuja encima. El resto de tu código del juego no sabe ni le importa.

Puedes envolver el mismo método varias veces, incluso desde plugins diferentes. Se encadenan automáticamente — cada envolvente llama al anterior a través de original, como capas de una cebolla.

Building a Debug Overlay

Vamos a construir algo real. Un overlay de depuración es de las primeras cosas que quieres en cualquier proyecto — contador de FPS, coordenadas del ratón, y una forma de activarlo y desactivarlo.

Empezaremos con el interruptor. Necesitamos un método debug() que alterne una bandera. Como la función del plugin es un closure, podemos almacenar el estado en una variable simple — sin necesidad de ponerlo en ningún lugar del motor:

engine.use((register) => {
    let enabled = false;

    register('debug', function () {
        enabled = !enabled;
        return enabled;
    });
});

Llama a debug() una vez para activarlo, otra vez para desactivarlo. La variable enabled vive en el closure del plugin, así que persiste entre frames y permanece privada a este plugin.

Drawing the Overlay

Ahora la parte visual. Envolvemos cls para que después de cada limpieza, si el modo de depuración está activo, dibujemos el contador de FPS y la posición del ratón en la esquina superior izquierda.

Aquí está el plugin completo — register y wrap trabajando juntos:

engine.use((register, wrap) => {
    let enabled = false;

    register('debug', function () {
        enabled = !enabled;
        return enabled;
    });

    wrap('cls', function (original, color) {
        original(color);

        if (!enabled) return;

        const { x, y } = this.mouse();

        this.text('fps:' + this.currentFps, 1, 1, 7);
        this.text('x:' + x + ' y:' + y, 1, 8, 7);
    });
});

El envolvente lee this.currentFps para la tasa de frames medida y this.mouse() para la posición del cursor. La bandera enabled viene del closure — compartida entre debug y el envolvente de cls sin tocar ningún estado del motor.

Consejo: El overlay se dibuja después de cls pero antes de todo lo demás en tu función draw(). Si quieres que el overlay se renderice por encima de todo, envuelve un método diferente — o llama a un drawDebug() dedicado al final de tu función draw.

Creating Your Plugin on Floaty

Los fragmentos de código anteriores muestran el plugin ejecutándose localmente con engine.use(). Para usarlo en Floaty, necesitas crearlo a través del editor de plugins.

Inicia sesión y ve a tu página de plugins. Crea un plugin nuevo — el editor se abre con una plantilla. El formato es un poco diferente de la versión con engine.use(): los plugins en el sitio usan export default function:

export default function (register, wrap) {
    let enabled = false;

    register('debug', function () {
        enabled = !enabled;
        return enabled;
    });

    wrap('cls', function (original, color) {
        original(color);

        if (!enabled) return;

        const { x, y } = this.mouse();

        this.text('fps:' + this.currentFps, 1, 1, 7);
        this.text('x:' + x + ' y:' + y, 1, 8, 7);
    });
}

La lógica es idéntica — el único cambio es el envolvente exterior. Pega esto en el editor, dale un nombre a tu plugin y guarda.

Testing in Draft Mode

Abre cualquier playground que tengas — o crea uno nuevo. Abre la barra lateral de plugins y busca en "Tus Plugins." Tu overlay de depuración debería aparecer ahí. Haz clic en Añadir.

Por defecto, tus propios plugins se adjuntan en modo borrador. Verás una casilla "Usar borrador" que ya está marcada. Esto significa que el playground carga tu último código guardado del editor, no una versión publicada. Es tu ciclo de desarrollo:

  1. Edita el código del plugin en el editor de plugins
  2. Guarda
  3. Recarga tu playground
  4. Ve los cambios inmediatamente

Para probar el overlay, llama a debug() en la función init() de tu juego (o actívalo con una tecla). El contador de FPS y las coordenadas del ratón deberían aparecer en la esquina superior izquierda.

Publishing a Release

Cuando estés contento con tu plugin, abre el editor de plugins y haz clic en "Releases" en la barra superior. La barra lateral muestra tu historial de versiones. Crea una nueva versión — esto captura tu código actual como una versión numerada.

Otros usuarios pueden entonces encontrar y añadir tu plugin a sus playgrounds. Su playground se fija a la versión que añadieron — cuando publiques una nueva versión, verán un indicador y podrán elegir actualizar desde el menú desplegable de versiones.

Going Further

  • Sacudida de pantalla — registra un método shake(intensity, duration) y envuelve métodos de dibujo para aplicar un desplazamiento aleatorio cada frame mientras el temporizador corre
  • Sistema de partículas — registra métodos emit() y particles(), envuelve cls para actualizar y dibujar partículas después de cada limpieza
  • Múltiples plugins — los plugins se componen sin conflictos. Registra métodos en uno, envuélvelos en otro. El motor mantiene todo en orden
  • Compartir públicamente — marca tu plugin como público en el editor para que otros puedan buscarlo y añadirlo a sus playgrounds