Aprende como programar Bots, NPCs, Rovers...
Los mapas se cargan desde la carpeta maps como PNG transparentes.
Donde la imagen sea negra o transparente se podrá circular; el resto se considera muro.
Por ahora sólo está disponible el modo todos contra todos.
Se usa sólo si el modo es "Punto fijo". Si el punto está dentro de un muro, el mundo intentará buscar un sitio cercano libre.
Puedes añadir varios bots del mismo tipo o de tipos distintos.
Jugador 1: flechas para mover y Intro para disparar.
Jugador 2: W/S avanzar/retroceder, A/D girar, espacio = disparar.
Debes definir una función createBot(world)
que devuelva un objeto con el método update(state).
El código de abajo ya es un bot operativo.
Arrastra bloques para definir la lógica de tu bot. Luego pulsa
“Volcar a código”
para generar el
createBot(world) en modo código.
Los bots guardados se almacenan en este navegador usando localStorage. Se puede modificar el comportamiento de los bots del sistema cargando y registando el nuevo comportamiento.

En Mundo de Bots puedes programar tanques que se mueven y disparan solos. No hace falta saber mucha programación: iremos paso a paso.
Un bot es un tanque que piensa por sí mismo. El mundo le dice lo que está pasando (por ejemplo “veo un enemigo delante”) y el bot responde diciendo lo que quiere hacer (moverse, girar, disparar…).
state.cmd.miPrimerBot.
Mi Primer Bot.
#ff8800.id (por ejemplo miPrimerBot).
Si cambias el código del bot en el editor y pulsas otra vez “Registrar en el mundo”, las instancias de ese bot usarán el nuevo código automáticamente.
Todos los bots siguen la misma estructura:
function createBot(world) {
return {
update: function (state) {
const cmd = {
move: 0, // -1 = hacia atrás, 1 = hacia delante
turn: 0, // -1 = girar izquierda, 1 = girar derecha
shoot: false // true = disparar
};
// aquí decides qué hace el bot
return cmd;
}
};
}
La función update(state) se ejecuta muchas veces por segundo.
Cada vez miras el state y devuelves un cmd con las órdenes.
state?Dentro de update(state) puedes usar:
state.self.x y state.self.y:
posición del bot en el mapa (números).
state.self.angle:
ángulo al que apunta el tanque (no hace falta entender los radianes,
sólo saber que cambiarlo hace que mire a otra dirección).
state.enemyVisible:
true si el bot ve algún enemigo en su cono de visión.
state.enemyDistance:
distancia al enemigo más cercano (o null si no ve a nadie).
state.enemyDirection:
indica si el enemigo está a la izquierda, centro o derecha:
state.canShoot:
true si el bot puede disparar en este momento
(hay un tiempo de recarga entre disparos).
state.wallAhead:
true si el bot tiene un muro (o el borde del mapa)
justo delante en la dirección a la que mira, dentro de un cierto alcance.
state.wallDistance:
distancia en píxeles hasta ese muro de delante, o
null si no hay ninguno cerca en esa dirección.
cmd?cmd.move – cómo se mueve el bot:
1 → avanzar hacia delante (rápido).-1 → ir hacia atrás.0.5 → avanzar más despacio.0 → no moverse.cmd.turn – cómo gira:
1 → girar fuerte a la derecha.-1 → girar fuerte a la izquierda.0.3 → girar un poco.0 → no girar.cmd.shoot – disparar o no:
true → intenta disparar.false → no dispara.function createBot(world) {
return {
update: function (state) {
const cmd = {
move: 0.5, // avanza
turn: 0.3, // gira despacio a la derecha
shoot: false
};
return cmd;
}
};
}
function createBot(world) {
return {
update: function (state) {
const cmd = { move: 0, turn: 0, shoot: false };
if (state.enemyVisible) {
// gira hacia el enemigo
cmd.turn = state.enemyDirection;
// avanza rápido
cmd.move = 1;
// dispara si puede
if (state.canShoot) {
cmd.shoot = true;
}
} else {
// no ve a nadie: gira buscando
cmd.turn = 0.4;
cmd.move = 0.2;
}
return cmd;
}
};
}
function createBot(world) {
return {
update: function (state) {
const cmd = { move: 0, turn: 0, shoot: false };
// Si hay un muro muy cerca delante, giramos para evitarlo
if (state.wallAhead && state.wallDistance !== null && state.wallDistance < 40) {
cmd.move = -0.2; // retrocede un poco
cmd.turn = 1; // gira fuerte a la derecha
return cmd;
}
if (state.enemyVisible) {
// Gira hacia el enemigo y avanza
cmd.turn = state.enemyDirection;
cmd.move = 0.8;
if (state.canShoot) {
cmd.shoot = true;
}
} else {
// Patrulla buscando enemigos
cmd.turn = 0.3;
cmd.move = 0.4;
}
return cmd;
}
};
}
{ } y paréntesis
( ) estén bien cerrados.
move y turn.console.log("state:", state); dentro de
update. Verás los mensajes tanto en la
Consola del bot (debajo del editor) como en la
consola del navegador (F12).
TypeError)
también aparecerán con su mensaje y detalles en la
Consola del bot, para que sea más fácil encontrar
qué línea está fallando. (a la linea del primer error hay que restarle 3, es decir,
si indica fallo en la linea 30 realmente esta en la 3, esto se debe al encapsulado que
hacemos del codigo del bot )
state.wallAhead y state.wallDistance.Si ya dominas lo básico, el mundo ofrece sensores avanzados que permiten crear bots mucho más inteligentes. No hace falta usarlos todos: están ahí para que experimentes cuando tengas curiosidad.
El estado incluye sensores para saber si una bala va directa hacia tu bot:
state.bulletIncoming // true si una bala se acerca
state.bulletDistance // distancia a la bala más cercana
state.bulletDirection // -1 izquierda, 1 derecha
Ejemplo: esquivar balas girando hacia el lado contrario:
if (state.bulletIncoming && state.bulletDistance < 60) {
cmd.turn = -state.bulletDirection; // giro en sentido opuesto
cmd.move = 1; // acelero para escapar
return cmd;
}
Además de wallAhead, tienes información de muros a cada lado:
state.wallRadar.front
state.wallRadar.back
state.wallRadar.left
state.wallRadar.right
Ejemplo: centrarse en pasillos evitando muros laterales:
if (state.wallRadar.left < 30) cmd.turn = 0.5;
if (state.wallRadar.right < 30) cmd.turn = -0.5;
state.enemyPos → posición exacta del enemigo.state.enemyAngle → ángulo del enemigo.state.enemyHp → vida del enemigo (si se usa).state.enemyDensity → cuántos enemigos hay delante, detrás, izquierda o derecha.Ejemplo: atacar si el enemigo está centrado en nuestro FOV:
if (state.enemyVisible && Math.abs(state.enemyDirection) < 0.2) {
cmd.move = 1;
if (state.canShoot) cmd.shoot = true;
}
Tu bot puede recordar cosas entre frames usando state.memory.
Solo tienes que devolver cmd.memory en la salida.
// Ejemplo: girar durante 30 frames y luego cambiar de estrategia
if (!state.memory.turnTime) state.memory.turnTime = 0;
state.memory.turnTime++;
cmd.turn = 1;
if (state.memory.turnTime > 30) {
cmd.turn = -1;
}
cmd.memory = state.memory; // guardar cambios
El mundo calcula si tu bot ha logrado moverse o ha chocado:
state.last.moved // true si se movió desde el último frame
state.last.collided // true si chocó contra un muro
Ejemplo: desatascarse automáticamente:
if (!state.last.moved && state.last.collided) {
cmd.move = -1; // retroceder
cmd.turn = 1; // girar fuerte
return cmd;
}
Estos sensores permiten crear bots más complejos: exploradores, bots que patrullan, bots francotiradores, bots que huyen inteligentemente, o incluso bots que “aprenden” patrones sencillos usando memoria.
Consejo: empieza por un comportamiento muy simple, y añade ideas avanzadas poco a poco. ¡El mejor bot es el que te diviertes creando!
¡Y ya está! Experimenta, rompe cosas y vuelve a probar. Cada vez que cambias el código y vuelves a registrar el bot, estás un paso más cerca de tener el bot más fuerte del mundo. 💥🤖
Si quieres que los avanzados dejen de hacer el ridículo:
En tu función update(state) puedes añadir al final un pequeño
“filtro agresivo” para que el bot no se quede parado ni mareado:
if (!state.enemyVisible) {
// Patrulla agresiva por defecto
cmd.turn ||= 0.3;
cmd.move ||= 0.7;
} else {
// Si veo enemigo, siempre algo de avance y de giro
if (Math.abs(cmd.move) < 0.2) cmd.move = 0.4;
if (Math.abs(cmd.turn) < 0.1) cmd.turn = state.enemyDirection || 0.2;
}
Así te aseguras de que el bot no se quede parado o dando vueltas sin sentido.
Si el bot entra en “modo pánico” al esquivar balas, ese modo debe terminar después de unos cuantos ticks, o se quedará esquivando para siempre.
if (state.bulletIncoming && state.bulletDistance !== null && state.bulletDistance < 80) {
// 1–2 ticks de esquiva fuerte
cmd.move = 1;
cmd.turn = state.bulletDirection > 0 ? -0.8 : 0.8;
cmd.memory = cmd.memory || {};
cmd.memory.dodgeUntil = (cmd.memory.dodgeUntil || 0) + 1;
} else if (cmd.memory && cmd.memory.dodgeUntil > 0) {
// Reducimos el contador y volvemos poco a poco al comportamiento normal
cmd.memory.dodgeUntil--;
}
Si no le pones “caducidad” a esos modos de pánico, el bot entra en un bucle de evasión infinita y no vuelve al plan ofensivo.
Para francotiradores y bots avanzados, es mejor disparar “bastante bien” que esperar al tiro perfecto:
if (state.enemyVisible && state.canShoot) {
const dist = state.enemyDistance || 9999;
// Rango óptimo de francotirador
if (dist < 180 && dist > 40) {
cmd.shoot = true;
}
// Ajuste de posición
if (dist <= 60) {
cmd.move = -0.5; // se aleja un poco
} else if (dist > 160) {
cmd.move = 0.6; // se acerca un poco
}
cmd.turn = state.enemyDirection;
}
La clave: no esperar al tiro perfecto. Disparar con un poco de error sigue funcionando muy bien.
| Id | Nombre | Líneas JS | Bloques | Tamaño | Acciones |
|---|