Эта страница ещё не переведена — показана английская версия.
Bots
Бот входит в игру как обычный игрок. Отдельно живёт только его мозг. Та же сессия, та же проверка при входе, те же правила, тот же ACL. Комната не отличает бота от человека — так задумано. Поэтому боты играют по настоящим правилам вашей игры, а античиту никогда не нужны исключения для ботов.
Когда это нужно
- Лобби нужно заполнять в часы с низким онлайном:
FillRoomдобирает матчи до квоты, а когда приходят люди, боты уступают им места (seat). - Боты должны играть по настоящим правилам — проверка при входе, ACL, видимость, — чтобы античиту не требовались исключения для ботов.
- У вас свой внешний мозг (brain) — например, обученная модель или отдельный сервис, — и он подключается через
ConnectAsBot, как любой игрок (player). - Сущность (entity) отключившегося игрока должна перейти к боту, а после переподключения вернуться обратно, и ни место, ни предсказание не должны этого заметить.
- Не нужен, если персонаж ничего не решает. Диалоговый NPC без мозга относится к объектам мира.
Эта страница о том, как подключить бота: ввести его в комнату, добрать лобби до квоты, передать место между ботом и человеком. Как написать мозг, который принимает решения, описано на странице Как написать мозг бота.
Кто что делает
| Актор | Что делает на этой странице |
|---|---|
bot-brain | подключается как игрок; получает восприятие; отправляет команды |
room-owner | объявляет профили, заполняет комнаты до квоты, передаёт управление между ботом и человеком |
Коротко
filler: параметры сложности, мозг на системе полезности (utility) и FillRoom до квоты[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)Как объявлять через атрибуты в Go, пока не решено (O-1). Примеры появятся, когда выберут способ записи.
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.[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);Внешний мозг — более тяжёлый ИИ, обученная модель, отдельный сервис — подключается как любой игрок:
ConnectAsBot подключает внешний мозг как игрока: на входе те же дельты, на выходе тот же ввод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 inputsconst 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 inputsbot = 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Как объявлять через атрибуты в Go, пока не решено (O-1). Примеры появятся, когда выберут способ записи.
// 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.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Модель
Своих понятий бот не вводит. Это учётные данные актора плюс то, что уже объявлено для комнат, данных и перемещения.
Что содержит объявление бота.
| Что объявляется | Что это |
|---|---|
тик мышления | как часто опрашиваются мозги. Это не тик симуляции: мозги работают снаружи, и сетевой вызов на каждый тик невозможен |
где работают мозги | в облачной функции, на бэкенде студии или на её игровом сервере. Конкретное место в контракт не входит, и переезд между ними не ломает совместимость |
пресет актора | права бота в виде обычного пресета актора |
видимость метки бота | сообщают ли участникам, что это бот. Сама метка существует всегда, и платформа всегда её видит. Видят ли её игроки, объявляет тип комнаты (room): на одних рынках раскрывать ИИ-соперника обязательно по закону, на других это продуктовое решение |
поведение при недоступности мозгов | один из трёх вариантов, без значения по умолчанию: do nothing · leave the room · fall back to built-in default behaviour |
добор состава | его объявляет тип комнаты: сколько ботов, при каком условии и до какого момента. Модуль Matchmaking о ботах ничего не знает: он подбирает акторов и не решает, кем добирать состав |
Что верно для любого бота.
| Всегда | Что это |
|---|---|
актор, а не игрок | у бота есть учётные данные актора, но нет провайдера входа, привязанных аккаунтов и сессий |
экономическое владение | отсутствует: ни прав владения, ни покупок, ни записей в таблицах лидеров (leaderboard) |
восприятие | такое же, как у игрока: та же зона видимости, тот же предикат, тот же лимит на число объектов. Бот и игрок в одной и той же позиции получают один и тот же набор объектов, поэтому видеть сквозь стены бот может не больше, чем игрок |
расширенное восприятие | это свойство пресета актора, а не бота. Отладочный режим или режим «всевидящего тренера» объявляется пресетом с более широким предикатом |
между решениями | действует последняя команда, а что с ней будет дальше, уже объявлено в типе движения для устаревшего ввода. Бот, чей мозг ещё думает, — тот же случай, что и игрок, у которого пропала сеть |
вместимость комнаты | учитывает бота: он занимает место наравне со всеми |
Ошибки
- Комната не принимает ботов — это конфликт, и повтор не поможет.
- Лимит ботов исчерпан — это конфликт, а не forbidden: разрешение (permission) добавить бота у вас есть, просто комната заполнена. Повторить стоит, когда место освободится.
- Команда боту от актора без нужного разрешения получает ответ forbidden, и повторять её бессмысленно.
- Недоступность мозгов — не ошибка, а одно из трёх объявленных выше поведений.
- Объявлено при деплое — и отклонено при деплое: бот, указанный владельцем записи в таблице лидеров, и отсутствующий тик мышления — это ошибки валидации при деплое, а не сюрпризы в живой комнате.
Ограничения
Конкретные числа появятся вместе с главой об ограничениях платформы, а поведение на каждом пределе зафиксировано уже сейчас.
- Боты в комнате: добавление отклоняется как конфликт. Чтобы освободить место, существующих ботов никогда не удаляют.
- Боты на проект: тот же конфликт.
- Нижняя граница тика мышления: объявление с частотой выше этой границы отклоняется при деплое, потому что сетевой вызов на каждый тик нереален.
- Частота команд для одного бота: ограничение частоты со сроком.
- Срок ответа от мозгов: когда он истекает, включается объявленное поведение на случай недоступности.