Карты, движение и геометрия
Этот документ описывает переиспользуемые примитивы координат карты, движения, поиска пути, трассировки линий и загрузки карт, которыми пользуются клиентская и серверная среды выполнения и инструменты.
Синтаксис разделов .fomap, идентичность и владение размещёнными объектами, обратимый цикл Mapper, зависящий от стороны baking и материализацию содержимого в runtime см. в формате карт. Эта страница отвечает за семантику геометрии и движения.
Используйте её при изменении Source/Common/Geometry.*, LineTracer.*, Movement.*, PathFinding.*, MapLoader.*, поведения map baker или тестов карт и движения.
Модель владения
Движок владеет типами координат, алгоритмами пути, интерполяцией движения и механизмами загрузки файлов карт. Подключающий игровой проект владеет конкретными картами, расположением блокировок, правилами случайных встреч и игровыми решениями о перемещении.
Не помещайте сюда содержимое карт конкретного проекта и правила заданий или навигации.
Типы координат и направлений
Source/Common/Geometry.h определяет экспортируемые value-типы, используемые runtime, сгенерированным API и скриптами:
mpos— позиция гекса или тайла на карте, хранящаяint16 xиint16 y;msize— размер карты какint16 widthиint16 height;hdir— дискретное направление гекса или тайла;mdir— угол направления движения, преобразуемый в направление гекса и обратно.
msize предоставляет проверяющие и ограничивающие помощники:
clamp_pos()ограничивает сырые координаты границами карты;from_raw_pos()утверждает, что сырые координаты уже находятся в границах.
Не заменяйте эти типы универсальными ipos/isize в открытых API карты без проверки сгенерированных метаданных и видимых скриптам представлений value-типов.
Соглашение о смещении внутри гекса
Пиксельные смещения внутри гекса измеряются относительно его визуального центра и ограничены ±{MAP_HEX_WIDTH/2, MAP_HEX_HEIGHT/2}. Единое соглашение применяется к HexOffset криттера, начальным и конечным смещениям MovingContext, смещениям цели движения (MoveToHex), смещениям transparent egg и значению, возвращаемому MapView::GetHexAtScreen / Client_Map_GetHexAtScreenPos. Поэтому смещение клика или курсора можно непосредственно использовать как смещение движения или криттера без преобразования. Новые производители и потребители смещений должны следовать этому соглашению.
Важно различать две точки привязки реализации:
GeometryHelper::GetHexPos/MapView::GetHexMapPos, как и сеткаOffsetкаждогоField, возвращают левый верхний origin рисования ячейки гекса, двигаясь шагами полногоMAP_HEX_WIDTH/MAP_HEX_LINE_HEIGHT.- Спрайты криттеров и предметов привязываются к визуальному центру гекса: левый верхний угол плюс
{MAP_HEX_WIDTH/2, MAP_HEX_HEIGHT/2}, см.HexView::AddSprite.MapView::GetHexScreenPosвозвращает тот же визуальный центр, где рисуются ноги центрированного криттера.
Поскольку визуальный центр смещён от origin ячейки на половину гекса, код, разрешающий или применяющий смещение относительно центра через GetHexMapPos, обязан прибавить {MAP_HEX_WIDTH/2, MAP_HEX_HEIGHT/2}. GetHexAtScreen смещает точку поиска на -{half hex}, чтобы GetHexPosCoord выбрал гекс с ближайшим визуальным центром; возвращаемое смещение уже относится к центру и находится в допустимом диапазоне без ограничения. UpdateTransparentEgg и основанный на криттере SetTransparentEgg прибавляют или вычитают ту же половину гекса, чтобы сохранённое смещение от центра рисовалось в нужной точке. Выражение GetHexScreenPos(hex) + offset непосредственно проецирует пару (гекс, смещение относительно центра) на экран без дополнительной поправки.
Режимы геометрии
hdir компилируется по-разному в зависимости от FO_GEOMETRY:
FO_GEOMETRY == 1— шесть направлений: северо-восток, восток, юго-восток, юго-запад, запад, северо-запад;FO_GEOMETRY == 2— восемь направлений: шесть перечисленных плюс юг и север.
GameSettings::MAP_DIR_COUNT участвует в нормализации направлений. При изменении геометрии проверяйте compile-time настройки, сгенерированные value-типы, тесты поиска пути и код рендеринга, проецирующий позиции карты.
mdir хранит нормализованные углы, а hdir — дискретные направления карты. При движении или развороте используйте общие помощники преобразования: в квадратной геометрии север имеет угол 0, поэтому самописное разбиение угла обязано корректно обрабатывать переход 360/0.
Проекция камеры карты
Камера карты использует world frame +X вправо, +Y вверх и +Z на юг карты; одна world unit равна одному pixel hex spacing. Parallel projection наклоняет мир вокруг X на MAP_CAMERA_ANGLE (arcsin(sqrt(3)/4)): northing земли сокращается в sin(angle) раз, а elevation — в cos(angle). Ground point с z = legacy_y / sin(angle) заставляет ProjectWorldToMap точно воспроизводить legacy-позицию GetHexPos при нулевой высоте.
ProjectWorldToMap является эталонной формой без scroll и zoom. Он возвращает map-space pixels в .x/.y и view depth в .z; больший depth ближе к камере. Пара (.y, .z) — ортонормальное вращение world (Z, Y), а не shear.
MakeMapCameraView — GPU-форма со scroll, zoom и вращением yaw_deg вокруг вертикальной оси. При нулевом yaw она воспроизводит (ProjectWorldToMap(world).xy - scroll) * zoom с неизменным depth. Backend ortho компонуется поверх этой matrix, поэтому sprites, 3D models и particles используют одно world-to-clip преобразование. Map sprites также записывают world depth с DepthFunc = LessEqual: CPU painter order продолжает обслуживать blended layers, а общий depth buffer разрешает occlusion между разными типами. Test_Geometry.cpp закрепляет обе формы проекции друг относительно друга и относительно GetHexPos. Он также закрепляет GetHexOffset(from, to) == GetHexPos(to) - GetHexPos(from), поэтому изменение view origin является uniform pixel translation; MapView использует этот инвариант при сдвиге cached light primitives во время scroll.
Обязанности GeometryHelper
GeometryHelper — статический служебный класс. Он отвечает за проекцию координат и математику направлений:
- преобразование гекса или тайла в экранные координаты карты:
GetHexPos(),GetHexPosCoord(),GetHexOffset(); - канонизацию гекса и смещения:
NormalizeHexOffset()переписывает проецируемую точку как ближайший находящийся в границах гекс и малое локальное смещение; - помощники осевых координат:
GetHexAxialCoord(),GetAxialHexes(); - расстояния и направления:
GetDistance(),GetHexDir(),GetDirAngle()и помощники разности углов; - радиусы и проверки линии с окружностью:
HexesInRadius(),IntersectCircleLine(); - шаг движения:
MoveHexByDir(),MoveHexByDirUnsafe(),MoveHexAroundAway(); - обход multihex:
ForEachMultihexLines().
Безопасные помощники проверяют границы msize; небезопасные предназначены для внутренних алгоритмов, уже доказавших нахождение в границах.
Поиск пути
Source/Common/PathFinding.h экспортирует основное API поиска пути:
FindPathInputFindPathOutputTraceLineInputTraceLineOutputPathFinding::CheckHexWithMultihex()PathFinding::FindPath()PathFinding::EvaluateFreeMovementEndOffset()PathFinding::TraceLine()
FindPathInput намеренно основан на callback. Runtime предоставляет CheckHex(mpos), чтобы владение картой, блокировки, криттеры, gag-предметы и проектная политика блокирования оставались вне универсального алгоритма.
Важные поля FindPathInput:
FromHex/ToHex— запрошенные начало и конец маршрута;ToHexOffset— настоящее смещение цели внутриToHex; непрерывная позиция цели равна центруToHexплюсToHexOffset. Используется только при расчёте конечного смещенияFreeMovement;MapSize— границы всех проверок;MaxLength— максимальная длина маршрута в шагах, обычно получаемая из настроек движка. Маршрут ровно такой длины допустим; память выделяется блоками 16×16 только для затронутых гексов, а не пропорционально пределу;CritterDetour— добавочная стоимость входа в гекс, занятый живым криттером.MapManagerиMapViewберут её изGeometry.PathFindCritterDetour(по умолчанию12);Cut— остановка при достижении заданного расстояния до цели;0требует точного достижения;Multihex— радиус multihex-сущности;FreeMovement— включает оптимизацию контрольных шагов через line tracer и непрерывное конечное смещение;CheckTarget— необязательный предикат точной цели для поиска среди нескольких целей. При наличии заменяет проверку единственной парыToHex/Cut; ближайшая достижимая цель возвращается вNewToHex;CheckHex— callback, возвращающий состояние блокировки или откладывания.EnclosureProbeLimit— бюджет обратного обхода после его превышения прямым фронтом;0отключает проверку.MapManagerиMapViewиспользуютGeometry.PathFindEnclosureProbe(по умолчанию1024).
FindPathOutput возвращает результат, шаги направлений, контрольные шаги, возможно скорректированный Cut конечный NewToHex и EndHexOffset как конкретный ipos16, равный нулю при выключенном FreeMovement.
MapManager::FindPathToAny() строит индексированный набор целей и передаёт его через CheckTarget, поэтому сервер может найти ближайшую достижимую точную цель одним поиском вместо отдельного полного поиска для каждого кандидата. Скриптовые серверные перегрузки Map.FindPathToAny(...) предоставляют ту же операцию для сырого начального гекса или криттера и возвращают выбранную цель и длину маршрута через выходные аргументы.
Если нужен ответ о достижимости каждой точки, а не один выбранный маршрут,
PathFinding::FindReachable() выполняет один обход в ширину от FromHex до
обнаружения всех целей либо исчерпания MaxLength шагов. Серверный
Map.FindReachableHexes(fromHex, targetHexes, gagCallback) возвращает
достижимые цели в порядке входного списка, сохраняя повторы, и отвергает
координаты за границами карты. Проверяются препятствия карты и разрешённый
callback проход через gag-предметы, но не занятость гекса криттером и не
проходимость для конкретного multihex-актёра. Для выбора маршрута с учётом
стоимости движения используйте FindPathToAny().
Поиск A* и выбор маршрута
FindPath() использует A*: стоимость пройденного пути плюс оставшееся расстояние
в гексах до радиуса Cut цели. Для CheckTarget оценка оставшегося расстояния
равна нулю, поэтому поиск равномерно расходится к ближайшей достижимой цели.
Шаг стоит единицу, DeferGag добавляет десять, а DeferCritter добавляет
CritterDetour (отрицательное значение при расчёте считается нулём). Короткий
обход криттера предпочтительнее прохода через него, но стоящего в дверях можно
пересечь вместо обхода всего здания. Раньше криттер стоил дороже любого пути
без криттеров: окружённая ими цель заставляла поиск исчерпать большую свободную
область, прежде чем попробовать пройти через толпу.
Для одной цели ToHex/Cut поиск до первого шага проверяет весь диск целей.
Занятый криттером гекс можно пересечь за добавочную стоимость, но на нём не
останавливаются, пока хотя бы один целевой гекс имеет состояние Passable или
DeferGag. Если все цели заблокированы или заняты и хотя бы одна занята,
маршрут может закончиться на занятой цели. Поиск с несколькими целями через
CheckTarget не имеет такого диска и принимает выбранную цель как заданную.
Правило игрового действия, которому нельзя останавливаться на другом криттере,
нужно проверять на границе его собственного запроса движения.
Цель дальше MaxLength даже по прямой даёт TooFar без вызова CheckHex.
Поиск не входит и в гекс, для которого длина пройденного пути плюс оставшееся
расстояние превышает предел. Исчерпание обрезанной области даёт TooFar,
замыкание всей достижимой области — NoWay. Ответ CheckHex для одного гекса
кэшируется, но отпечаток multihex проверяется отдельно по стороне входа:
в центр можно войти с одной стороны и не суметь с другой.
После нахождения самой дешёвой цели поиск рассматривает все варианты той же
стоимости и восстанавливает самый прямой из дешёвых маршрутов. Среди равно
дешёвых целей внутри Cut предпочтительна ближайшая к прямой от начала до
ToHex. Очередь с корзинами стоимости обеспечивает одинаковый выбор маршрута
на разных платформах.
Для одной цели проверка обходит гексы от её радиуса Cut в обратном направлении через всё, кроме Blocked. Если эта широкая область замкнулась до начала пути, результат — NoWay; при исчерпании бюджета прямой поиск продолжается. FindPathToAny() не имеет одной цели для проверки. Ранний успех не оплачивает обратный обход.
MapManager::FindPath(max_length) при 0 использует Geometry.MaxPathFindLength, ограничивает положительные значения этой настройкой и отвергает отрицательные. Серверный Critter.MoveToHex(hex, cut, endHexOffset, maxPathLength, speed, gagCallback) возвращает HexTooFar сверх предела. Задавайте его, когда обход лишает движение смысла.
Обратный проход должен перечислять GameSettings::MAP_DIR_COUNT через GeometryHelper::MoveHexByDirUnsafe(), а не жёстко задавать шесть соседей гекса. Гексагональная сборка компилирует шесть направлений, квадратная — восемь; общие помощники удерживают расширение поиска и восстановление пути на одинаковых правилах геометрии.
Конечное смещение FreeMovement
При установленном FreeMovement прямые отрезки могут заменять части найденного маршрута, если в каждый гекс можно войти со стороны этого отрезка, даже когда поиск не достигал его. Поиск по-прежнему заканчивает маршрут на целом гексе, но конечная позиция уточняется до точки внутри него вместо привязки к центру NewToHex. PathFinding::EvaluateFreeMovementEndOffset() вычисляет EndHexOffset относительно центра NewToHex так, чтобы непрерывная конечная позиция находилась точно на расстоянии Cut, R = dist(центр NewToHex, центр ToHex), от настоящей позиции цели центр ToHex + ToHexOffset, со стороны NewToHex. Расстояния используют Y-проекцию камеры GeometryHelper::GetYProj(), совпадающую с метрикой расстояний сегментов MovingContext.
Поведение:
EndHexOffset— обычныйipos16. При выключенномFreeMovementили досрочном выходе до его прохода сохраняется нулевое значение, то есть движущийся останавливается в центре достигнутого гекса.- Центрированная цель, до которой остановились с зазором (
ToHexOffset == 0,Cut > 0), даётEndHexOffset == 0. Cut == 0для смещённой цели даётEndHexOffset == ToHexOffset, то есть точную остановку на цели.- Модуль смещения ограничен
|ToHexOffset|и половиной гекса. - При вырожденном направлении остановки внутренний
EvaluateFreeMovementEndOffsetвозвращаетoptional<ipos16>со значениемnullopt, когда настоящая цель практически совпадает с центром достигнутого гекса, напримерCut == 0для центрированной цели илиCut > 0, когда смещение цели точно компенсирует вектор между гексами. ТогдаFindPathподставляетFindPathInput::FromHexOffset, собственное текущее смещение движущегося, чтобы он остался в текущей точке, а не привязался к центру.MapView::FindPathиMapManager::FindPathавтоматически заполняютFromHexOffsetиз переданного криттера.
Вызывающий передаёт ToHexOffset и непосредственно использует EndHexOffset: на клиенте через MapView::FindPath и учитывающую Cut перегрузку Critter.MoveToHex(hex, cut, hexOffset, speed), на сервере через MapManager::FindPath и Critter.MoveToHex(hex, cut, endHexOffset, speed). Разрешённое смещение проходит в существующем поле end-offset сообщения SendCritterMove как конкретный ipos16, поэтому клиентское предсказание и серверная авторитетность останавливаются в одной непрерывной точке без изменения протокола.
Модель блокировки
HexBlockResult выражает приоритет поиска пути:
Passable— гекс доступен;Blocked— постоянная блокировка;DeferGag— gag-предмет, проход добавляет стоимость десяти шагов;DeferCritter— криттер, проход добавляет стоимостьCritterDetour; остановка допустима лишь при отсутствии свободной цели.
Для multihex-сущностей CheckHexWithMultihex() проверяет переднюю дугу по направлению и возвращает худший результат среди проверенных гексов.
Когда игровой код меняет семантику блокировки, обновляйте поставщика callback и тесты; не встраивайте проектные правила в универсальный алгоритм пути.
Серверные Map::IsHexMovable() и IsHexShootable() объединяют две сетки: собственное Field карты, пересчитываемое RecacheHexFlags() по динамическим предметам и ручным блокировкам, и статическое StaticMap::Field того же гекса. Статическая половина читается через Map::GetStaticField() — именно там применяется удаление статических предметов конкретного экземпляра карты.
DeferGag включается отдельно для каждого серверного или клиентского поиска.
Map::CheckGagItem() и MapView::CheckGagItem() требуют флаг поля
MovableWithGag, который устанавливается только когда все блокирующие движение
предметы на гексе являются gag-предметами, и предикат вызывающего, принимающий
каждый такой предмет. Без предиката MapManager::FindPath() и
MapView::FindPath() возвращают для того же гекса Blocked, поэтому обычное
движение не меняется. Клиентская скриптовая перегрузка Map.GetPath и
серверные перегрузки Map.GetPathLength с gagCallback предоставляют тот же
выбор: вызывающий может отличить маршрут, запечатанный стеной, от маршрута
через дверь, которую ему разрешено открыть.
Удаление статических предметов
Запечённые статические предметы живут в StaticMap (Source/Server/StaticMap.h), который MapManager ключует по ProtoMap и разделяет между всеми живыми экземплярами этой карты. Экземпляр всё равно может убрать отдельные статические предметы, не трогая эти общие данные.
Удаление одностороннее на всё время жизни экземпляра карты. RemovedStaticItemIds только растёт: попытку вынуть id обратно отклоняет setter свойства, который срабатывает до записи значения, поэтому запись не проходит, а сохранённый список и overlay остаются нетронутыми. Не рассчитывайте, что предмет вернётся на уже загруженные у игроков карты: живой клиентский путь умеет только убирать статический предмет, но не строить его заново, а отказ после записи сохранил бы укороченный список и молча отменил удаление при следующем старте сервера. MapManager::RegenerateMap() перегенерирует содержимое карты и оставляет удаления на месте; карте, которой нужен целый статический слой, нужен новый экземпляр.
Механика:
Map::RemovedStaticItemIds(Common Mutable PublicSync Persistent) — сохранённый список. Он и есть весь контракт: сохранение, синхронизация с клиентом и видимость из скриптов следуют из свойства.Mapвыводит из него три кэша вRefreshRemovedStaticItems()→RebuildStaticOverlay(): множество удалённых id,vectorуцелевших статических предметов и overrideStaticMap::Fieldдля каждого гекса, который покрывал удалённый предмет. Все три пусты, пока карта хранит все запечённые предметы, поэтому нетронутая карта читает общую сетку без лишней косвенности.Map::VerifyStaticItemRemovalsOnlyGrow()— страж append-only, вызываемый через setterServerEngine::OnSetMapRemovedStaticItems. Он ничего не меняет и только бросает исключение, поэтому отклонённая запись оставляет карту ровно такой, какой она была.Map::GetStaticField()возвращает override, когда он есть, и общую ячейку в остальных случаях. Через него проходят все статические запросы (GetStaticItem,GetStaticItemOnHex,GetStaticItems,GetStaticItemsOnHex,GetStaticItemsInRadius,GetTriggerStaticItemsOnHex,IsTriggerStaticItemOnHex) и оба запроса блокировки, поэтому удалённый предмет исчезает из движения, стрельбы, триггеров и поиска одинаково.StaticMap::ForEachItemHex()иStaticMap::ApplyItemToField()общие для загрузчика и для пересборки overlay, поэтому они не могут разойтись в том, какие гексы занимает предмет и какую блокировку он даёт.StaticMap::Field::ScrollBlockedсуществует ради этой пересборки. Проход загрузчика по scroll-блокировке пишетMoveBlockedбез владеющего предмета, поэтому override, собранный только из уцелевших предметов, молча открыл бы границу карты; пересборка сначала засеваетMoveBlockedизScrollBlocked.- Статические предметы несут
ident_t, записанный их файлом карты (MapManager::LoadFromResources): именно его ищетGetStaticItem(), его несёт клиентскийItemHexViewи его записывает список удалений.
EntityManager::CallInit(Map, bool) строит overlay один раз на карту — этот хук покрывает и только что созданную карту, и восстановленную из базы. Изменения во время работы приходят через post-setter свойства ServerEngine::OnPostSetMapRemovedStaticItems, поэтому скрипт, пишущий свойство напрямую, получает ту же пересборку, что и вызывающий RemoveStaticItem().
На клиенте есть ровно два пути, и map view не хранит для этого собственного состояния. LoadStaticData() сразу пропускает id из списка удалённых, поэтому предмет не создаётся, не попадает в поле, не рисуется и не индексируется — запись всё равно прочитывается насквозь, потому что запечённые записи переменной длины и у читателя нет индекса для перемотки. ClientEngine::OnSetMapRemovedStaticItems вызывает MapView::ApplyStaticItemRemovals(), который уничтожает ставшие лишними view обычным путём DestroyItems(). Третьего пути нет: ничто на клиенте не восстанавливает статический предмет на загруженной карте.
Трассировка линий
TraceLineInput описывает трассировку от StartHex к TargetHex:
MaxDistограничивает длину;0означает расстояние между началом и целью;Angleможет переопределять или задавать направление;CheckLastMovableтребует сообщить последний доступный для движения гекс;IsHexBlockedостанавливает трассировку;IsHexMovableнеобязательно отмечает допустимые кандидаты движения.
TraceLineOutput сообщает, была ли линия пройдена полностью, найден ли последний доступный гекс, а также гекс перед блокировкой, заблокированный и последний доступный гекс.
Трассировку используют движение и поиск пути, а также игровые системы видимости, стрельбы и прямолинейного перемещения.
Контексты движения
Source/Common/Movement.h определяет состояние и интерполяцию движения:
MovingState— причина завершения или ошибки;MovingMetrics— конечный гекс, полное время и расстояние;MovingProgress— текущий гекс, смещение, направление и флаг завершения;MovingRawProgress— внутренние данные сегмента и прогресса;MovingContext— план движения со счётчиком ссылок и runtime-вычислитель.
MovingContext хранит:
- размер карты;
- скорость;
- шаги направлений;
- контрольные шаги;
- начальное время и временное смещение;
- начальный и конечный гекс и смещения;
- вычисленные полное время и расстояние;
- состояние завершения и заблокированный и предшествующий гексы.
Основные операции:
- вычисление:
EvaluateMetrics(),EvaluateProjectedHex(),EvaluateNearestPathHex(),EvaluatePathHexes(),EvaluateProgress(); - продвижение времени:
UpdateCurrentTime(),UpdateCurrentTimeToNextHex(); - изменение runtime-состояния:
ChangeSpeed(),Complete(),SetBlockHexes(); - проверка согласованности:
ValidateRuntimeState().
Таким образом, движение — переиспользуемый зависящий от времени план, а не просто список позиций. Клиентское предсказание, серверная коррекция и видимые скриптам данные должны сохранять это различие.
_offsetTime сообщает наблюдателю, насколько уже продвинулся план: сервер включает его в сообщение клиенту, но исходный клиентский запрос движения такого поля не содержит. Поэтому сервер запускает план игрока позже клиента на время передачи запроса. При обычном завершении клиент сообщает о прибытии, а сервер согласует позицию вдоль того же пути до обработки следующего упорядоченного запроса; см. сетевой протокол.
Серверная и клиентская обработка сохраняет активный MovingContext независимо от CritterCondition. Игровые скрипты владеют разрешениями движения по состоянию и могут представлять падение в knockout, скольжение мёртвого тела или движение пользовательского состояния, используя те же путь, смещение и завершение. Присоединённые криттеры всё равно останавливаются, поскольку attachment является отношением транспорта и владения, а не состоянием криттера.
Загрузка карт
Source/Common/MapLoader.h экспортирует MapLoader::Load():
static void Load(
string_view name,
string_view file_name,
const string& buf,
const EngineMetadata& meta,
HashResolver& hash_resolver,
const CrLoadFunc& cr_load,
const ItemLoadFunc& item_load);
Загрузчик разбирает буфер карты, использует file_name в диагностике источника и вызывает предоставленные движком или runtime callback для криттеров и предметов:
CrLoadFunc(ident_t id, ptr<const ProtoCritter> proto, ptr<const map<string_view, string_view>> kv);ItemLoadFunc(ident_t id, ptr<const ProtoItem> proto, ptr<const map<string_view, string_view>> kv).
Парсинг файла остаётся универсальным, а сервер и инструменты решают, как загруженные криттеры и предметы превращаются в живые сущности или объекты редактора.
MapLoader::EnumerateMaps(file_name, buf) возвращает имена карт, объявленных исходным файлом, не материализуя их сущности. При изменении парсера сохраняйте тестовое покрытие диагностики имени источника и перечисления нескольких карт.
Производство ресурса карты соседствует с baking. О MapBaker см. Baking Pipeline.
Тесты для проверки
Подходящие тесты:
Source/Tests/Test_Geometry.cppSource/Tests/Test_PathFinding.cppSource/Tests/Test_Movement.cppSource/Tests/Test_MapLoader.cppSource/Tests/Test_MapBaker.cppSource/Tests/Test_ServerMapOperations.cpp
Маршрутизация изменений
- Типы координат и значений:
Source/Common/Geometry.*и документация сгенерированных метаданных. - Трассировка линий:
Source/Common/LineTracer.*иSource/Common/PathFinding.*. - Поиск и блокирование пути:
Source/Common/PathFinding.*плюс предоставленные вызывающим callback блокировок. - Интерполяция и состояние движения:
Source/Common/Movement.*. - Клиентское представление 2D-ходьбы и бега поверх интерполяции: Sprite Root Motion.
- Парсинг файла карты:
Source/Common/MapLoader.*. - Baking ресурса карты:
Source/Tools/MapBaker.*и Baking Pipeline. - Runtime-поведение сущности карты: серверная среда выполнения, клиентская среда выполнения и модель сущностей.
Контрольный список проверки
- Запустите относящиеся к алгоритму тесты движения, пути и загрузчика карт.
- При изменении блокировок проверьте прямое движение и multihex.
- При изменении направлений или проекции проверьте предположения
FO_GEOMETRY. - При изменении парсера проверьте загрузку настоящих baked-ресурсов карт подключающего проекта.
- Если состояние движения реплицируется, проверьте сеть и поведение клиента и сервера; при изменении потока пакетов или свойств обновите Networking.
- Если меняется видимое скриптам API движения, обновите GeneratedApiAndMetadata.md, а при изменении сигнатур или nullability также Nullability.md.