Saltar al contenido principal
El juego en vivo

Esta página aún no está traducida: se muestra la versión en inglés.

Bots

Un bot entra como un jugador más. Solo el cerebro vive en otro lugar. La misma sesión, la misma validación de ingreso, las mismas reglas, la misma ACL. La sala no puede notar la diferencia, y es a propósito: así los bots ponen a prueba las reglas reales de tu juego y el antitrampas nunca necesita una excepción para bots.

Cuándo usarlo

  • Necesitas llenar tus lobbies en horarios de poca actividad: FillRoom completa las partidas hasta una cuota, y los bots ceden su lugar (seat) a medida que llegan humanos.
  • Los bots tienen que jugar con las reglas reales (validación de ingreso, ACL, visibilidad), así que el antitrampas nunca necesita una excepción para bots.
  • Traes un cerebro (brain) externo, como una política aprendida o un servicio, que se une con ConnectAsBot como cualquier jugador.
  • La entidad de un jugador desconectado tiene que pasar a un bot y volver al jugador cuando se reconecta, sin que el lugar ni la predicción lo noten.
  • No lo uses si el personaje nunca decide nada. Un NPC de diálogo sin cerebro pertenece a los objetos del mundo.

Esta página cubre cómo conectar un bot: meterlo en una sala, llenar un lobby hasta la cuota y pasar un lugar entre un bot y un humano. Escribir el cerebro que decide es tema de Cómo escribir el cerebro de un bot.

Quién hace qué

ActorQué hace en esta página
bot-brainse conecta como jugador; recibe la percepción; envía comandos
room-ownerdeclara perfiles, llena salas (room) hasta la cuota y hace el traspaso entre bot y humano

En resumen

El perfil filler: números de dificultad, un cerebro de tipo utility y FillRoom hasta una cuota
[BotProfile("filler")]
[Brain(Kind.Utility)]
public static class Filler
{
    public static Difficulty Difficulty = Difficulty.Of(reactionMs: 250, aimJitter: 0.08f);

    [Consider(Targeting.NearestEnemy)] public static Behaviour Target;
    [Steer(Steering.SeekAndStrafe)]    public static Behaviour Move;
    [UseAbilities(When.Ready)]         public static Behaviour Fire;
}

PlayServ.Bots.FillRoom("battle", toQuota: 8, profile: "filler", minHumans: 1);
@BotProfile('filler')
@Brain({ kind: 'utility' })
export class Filler {
  static difficulty = Difficulty.of({ reactionMs: 250, aimJitter: 0.08 });

  @Consider(Targeting.nearestEnemy) target: Behaviour;
  @Steer(Steering.seekAndStrafe) move: Behaviour;
  @UseAbilities(When.ready) fire: Behaviour;
}

PlayServ.bots.fillRoom('battle', { toQuota: 8, profile: 'filler', minHumans: 1 });
@bot_profile("filler")
@brain(kind="utility")
class Filler:
    difficulty = Difficulty.of(reaction_ms=250, aim_jitter=0.08)

    target = consider(Targeting.NEAREST_ENEMY)
    move = steer(Steering.SEEK_AND_STRAFE)
    fire = use_abilities(When.READY)

playserv.bots.fill_room("battle", to_quota=8, profile="filler", min_humans=1)
Próximamente — Go

Todavía no está decidido cómo declarar con atributos en Go (O-1). Los ejemplos llegarán cuando se elija la forma de escribirlo.

USTRUCT(PSBotProfile = "filler", PSBrain = (Kind = "Utility"))
struct FFiller
{
    GENERATED_BODY()

    UPROPERTY(PSDifficulty = (ReactionMs = 250, AimJitter = "0.08"))
    FPSDifficulty Difficulty;

    UPROPERTY(PSConsider = (Targeting = "NearestEnemy")) FPSBehaviour Target;
    UPROPERTY(PSSteer    = (Steering = "SeekAndStrafe")) FPSBehaviour Move;
    UPROPERTY(PSUseAbilities = (When = "Ready"))         FPSBehaviour Fire;
};
// declarations compile into the same pushed model — playserv push from the UE project or CI

// a host tops up the room it serves
Client->Bots->FillRoom(PSKeys::Rooms::Battle,
    FPSFillRoomParams{ .ToQuota = 8, .Profile = TEXT("filler"), .MinHumans = 1 });
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.
Unity C# is the same C# API here — los mismos atributos compilan en Unity; FillRoom necesita derechos de host, y un build de cliente maestro los tiene
[BotProfile("filler")]
[Brain(Kind.Utility)]
public static class Filler
{
    public static Difficulty Difficulty = Difficulty.Of(reactionMs: 250, aimJitter: 0.08f);

    [Consider(Targeting.NearestEnemy)] public static Behaviour Target;
    [Steer(Steering.SeekAndStrafe)]    public static Behaviour Move;
    [UseAbilities(When.Ready)]         public static Behaviour Fire;
}

PlayServ.Bots.FillRoom("battle", toQuota: 8, profile: "filler", minHumans: 1);

Un cerebro externo (una IA más pesada, una política aprendida, un servicio) se conecta como cualquier jugador:

ConnectAsBot une un cerebro externo como jugador: entran los mismos deltas y salen las mismas entradas
var bot = await PlayServ.ConnectAsBot(projectKey, botId: "trainer-07");
var seat = await bot.Matchmaking.Find("battle");
var room = await bot.Rooms.Join(seat);
// perception in ← the same deltas a player receives; commands out ← the same inputs
const bot = await PlayServ.connectAsBot(projectKey, { botId: 'trainer-07' });
const seat = await bot.matchmaking.find('battle');
const room = await bot.rooms.join(seat);
// perception in ← the same deltas a player receives; commands out ← the same inputs
bot = await PlayServ.connect_as_bot(project_key, bot_id="trainer-07")
seat = await bot.matchmaking.find("battle")
room = await bot.rooms.join(seat)
# perception in ← the same deltas a player receives; commands out ← the same inputs
Próximamente — Go

Todavía no está decidido cómo declarar con atributos en Go (O-1). Los ejemplos llegarán cuando se elija la forma de escribirlo.

// An Unreal-based trainer client is a legitimate brain — it connects as a player.
FPlayServClient::ConnectAsBot(ProjectKey, TEXT("trainer-07"),
    TPSOnResult<FPlayServClient*>::CreateLambda([](const TPSResult<FPlayServClient*>& Result)
    {
        if (!Result.HasValue()) { return; }
        FPlayServClient* Bot = Result.Value();
        Bot->Matchmaking->Of<FBattleQueue>()->Tickets->Create(FPSTicketClaim{ .Mode = TEXT("battle") },
            TPSOnResult<FPSTicket*>::CreateLambda([Bot](const TPSResult<FPSTicket*>& TicketResult)
            {
                if (!TicketResult.HasValue()) { return; }
                TPSSubscription Placement = TicketResult.Value()->Subscribe(
                    [Bot](const FPSSeat& Seat) { Bot->Rooms->Join(Seat); });
            }));
    }));
// perception in ← the same deltas a player receives; commands out ← the same inputs
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.
Unity C# is the same C# API here — se ejecuta tal cual en Unity con los tipos generados
var bot = await PlayServ.ConnectAsBot(projectKey, botId: "trainer-07");
var seat = await bot.Matchmaking.Find("battle");
var room = await bot.Rooms.Join(seat);
// perception in ← the same deltas a player receives; commands out ← the same inputs

El modelo

Un bot no introduce ninguna noción propia. Es una credencial de actor que usa lo que ya declaran las salas, los datos y el desplazamiento.

Qué lleva la declaración de un bot.

Qué se declaraQué es
tick de pensamientocon qué frecuencia se consulta a los cerebros, y no es el tick de simulación: los cerebros se ejecutan afuera, y una llamada de red en cada tick no es viable
dirección de los cerebrosdónde se ejecutan — una función en la nube, el backend del estudio, su servidor de juego. Cuál de ellos no forma parte del contrato, y pasar de uno a otro no es un cambio incompatible
preset de actorlos derechos del bot, como un preset de actor común
visibilidad de la marca de botsi se les informa a los participantes. La marca en sí siempre existe y la plataforma siempre la observa; si la ven los jugadores lo declara el tipo de sala, porque en algunos mercados revelar que un oponente es una IA es una obligación y en otros es una decisión de producto
comportamiento cuando los cerebros no están disponiblesuno de tres, sin valor por defecto: do nothing · leave the room · fall back to built-in default behaviour
llenado de la lista de participanteslo declara el tipo de sala: cuántos, bajo qué condición y hasta qué momento. El módulo Matchmaking no sabe nada de bots: empareja actores y no decide con quién completar

Qué se cumple para todo bot.

SiempreQué es
un actor, no un jugadortiene una credencial de actor, pero no tiene proveedor de inicio de sesión, ni vínculos, ni sesiones
propiedad económicano tiene ninguna: ni derechos de uso (entitlement), ni compras, ni entradas en tablas de clasificación
percepciónes la de un jugador: la misma zona de visibilidad, el mismo predicado, el mismo límite de cantidad de objetos. Un bot y un jugador en la misma posición reciben el mismo conjunto de objetos, así que un bot no puede hacer wallhack, igual que un jugador
percepción ampliadaes un preset de actor, no una propiedad del bot: un modo de depuración o de “entrenador omnisciente” se declara como un preset con un predicado más amplio
entre pensamientosse aplica el último comando, y su destino es el que el tipo de movimiento ya declara para la entrada obsoleta — un bot cuyo cerebro está pensando es el mismo caso que un jugador que se quedó sin red
capacidad de la salacuenta al bot: ocupa un lugar como cualquier otro

Errores

  • La sala no acepta bots: es un conflicto, y repetir no va a ayudar.
  • Se agotó el límite de bots: es un conflicto, no forbidden — el permiso para agregar un bot lo tienes; lo que pasa es que la sala está llena. Vale la pena repetir cuando se libere lugar.
  • Un comando para un bot enviado por un actor sin el permiso recibe forbidden, y repetir no sirve de nada.
  • Que los cerebros no estén disponibles no es un error: es uno de los tres comportamientos declarados arriba.
  • Se declara en el deploy y se rechaza en el deploy: un bot designado como dueño de una entrada en una tabla de clasificación (leaderboard) y un tick de pensamiento que falta son fallas de validación en el deploy, no sorpresas en una sala en vivo.

Límites

Los números llegarán con el capítulo de límites de la plataforma; lo que pasa en cada tope ya está definido.

  • Bots en una sala: agregar uno más se rechaza como conflicto; nunca se eliminan bots existentes para hacer lugar.
  • Bots por proyecto: el mismo conflicto.
  • Tick de pensamiento por debajo del mínimo: una declaración más rápida que el piso se rechaza en el deploy, porque una llamada de red por tick no es práctica.
  • Frecuencia de comandos para un bot: un rate limit con plazo.
  • Plazo de respuesta de los cerebros: cuando vence, se aplica el comportamiento declarado para cuando no están disponibles.