reckfellENGINE

Room scale: коллайдер, origin и поворот

Проверенная синхронизация тела с головой без двойного движения и ограничения у стен.

На этой странице

Room scale добавляет физическое перемещение пользователя к игровому движению. Это два разных источника изменения положения. Если камера — потомок origin внутри CharacterBody3D, физическая pose сдвигает камеру относительно тела. Само тело при этом может оставаться в центре комнаты и проверять столкновения не там, где находится голова.

В этом руководстве реализована одна схема: коллайдер под горизонтальной проекцией головы, origin компенсирует перенос тела. Это приближение положения корпуса, а не body tracking.

Иерархия и условия

Текст
Player (CharacterBody3D)
├── CollisionShape3D (CapsuleShape3D)
└── XROrigin3D
    ├── XRCamera3D
    ├── LeftHand (XRController3D)
    └── RightHand (XRController3D)

Используйте вертикальное тело без наклона, единичный масштаб рига и мировую Y как верх. Пример не реализует произвольную гравитацию, масштабирование, высоту коллайдера по росту, ступени, движущиеся платформы или полный контроллер перемещения.

Коллайдер размещается относительно корня Player: для капсулы высотой 1.8 её центр можно поднять на 0.9, если корень обозначает ноги. Это параметры проверочного примера, не обязательные размеры каждого игрока. Для приседания и разного роста отдельно подбирайте высоту и проверяйте, что изменение формы не помещает её внутрь препятствия.

До вызова должны быть доступны актуальные данные tracking. Числовой transform, HasFocus или видимая модель контроллера сами по себе не подтверждают валидность позы. Пример принимает уже полученные трансформации; он не запускает XR и не определяет наличие tracking. Подготовка описана в настройке XR и терминах.

Синхронизация без двойного движения

Для физического шага:

  1. Вычислите разницу мировых позиций головы и тела только по XZ.
  2. Переместите CharacterBody3D к голове через MoveAndCollide, чтобы учитывать его коллайдер.
  3. Восстановите прежний мировой transform origin. Иначе перенос родителя повторно сдвинет камеру и руки.
  4. Проверьте остаток между головой и телом: препятствие могло остановить коллайдер раньше цели.

Сохраните вспомогательный класс как RoomScaleBody.cs. Это статический помощник, его не нужно прикреплять к Entity.

C#
using System;
using Reckfell;

public static class RoomScaleBody
{
    // Upright, unscaled rig with world Y as up. Does not change the tracked pose.
    // true means a horizontal gap remains after trying to move the body.
    public static bool FollowHead(CharacterBody3D body, XROrigin3D origin, Entity3D head)
    {
        ValidateRig(body, origin, head);
        Vector3 requested = head.GlobalPosition - body.GlobalPosition;
        requested.Y = 0;
        if (!requested.IsFinite()) throw new InvalidOperationException("Invalid tracked position.");
        if (requested.LengthSquared() < 0.00000001f) return false;

        Transform3D fixedOrigin = origin.GlobalTransform;
        try
        {
            using var collision = body.MoveAndCollide(requested);
        }
        finally
        {
            origin.GlobalTransform = fixedOrigin;
        }
        Vector3 remaining = head.GlobalPosition - body.GlobalPosition;
        remaining.Y = 0;
        return remaining.LengthSquared() > 0.000001f;
    }

    // Keeps head position while changing game yaw. No rotational collision sweep.
    public static void TurnAroundHead(
        CharacterBody3D body, XROrigin3D origin, Entity3D head, float yawRadians)
    {
        ValidateRig(body, origin, head);
        if (!float.IsFinite(yawRadians)) throw new ArgumentOutOfRangeException(nameof(yawRadians));
        Vector3 before = head.GlobalPosition;
        if (!before.IsFinite()) throw new InvalidOperationException("Invalid tracked position.");
        body.RotateY(yawRadians);
        origin.GlobalPosition += before - head.GlobalPosition;
    }

    private static void ValidateRig(CharacterBody3D body, XROrigin3D origin, Entity3D head)
    {
        ArgumentNullException.ThrowIfNull(body);
        ArgumentNullException.ThrowIfNull(origin);
        ArgumentNullException.ThrowIfNull(head);
        if (!body.IsInsideTree() || !origin.IsInsideTree() || !head.IsInsideTree())
            throw new InvalidOperationException("The rig must be inside the tree.");
        if (!body.IsAncestorOf(origin) || !origin.IsAncestorOf(head))
            throw new ArgumentException("Expected body -> origin -> tracked head hierarchy.");
    }
}

Из своего контроллера вызывайте RoomScaleBody.FollowHead(player, origin, camera) в согласованном физическом шаге после получения актуальной позы. Motion внутри MoveAndCollide здесь уже является смещением, а не скоростью: дополнительно умножать его на delta нельзя.

Возвращаемое true означает, что после попытки перемещения по XZ осталось больше 0.001 игровой единицы. Это не признак любого касания: маленький остаток ниже допуска не считается блокировкой, а неверная иерархия отклоняется исключением. Готовую синхронизацию не смешивайте с другим кодом, который одновременно переносит тот же body/origin или перезаписывает pose.

Игровое движение и поворот

После компенсации физического движения обычное игровое перемещение тела должно переносить риг вместе с ним. Для этого шага прежний origin сохранять нельзя: компенсация предназначена только для уже выполненного физического движения головы.

Если пользователь отошёл от origin, обычный поворот вокруг его центра переместит голову по дуге. TurnAroundHead поворачивает тело по Y, затем компенсирует позицию origin, сохраняя мировую позицию головы. Входной угол — радианы: для 30° используйте Mathf.DegToRad(30). Ориентация вида в игровом мире изменяется, локальная отслеживаемая pose не перезаписывается.

Это не вращательный collision sweep. Пример рассчитан на вертикальную симметричную капсулу; для произвольного тела отдельно проверяйте столкновения при вращении. Комфортный угол, плавность и частоту поворота нужно выбирать и тестировать на устройстве, а не выводить из компиляции помощника.

В альтернативной origin-центричной схеме тело может быть независимым от иерархии origin. Тогда переносы нужно применять явно. Не смешивайте две схемы: здесь тело является предком origin, и помощник проверяет именно это условие.

Когда тело остановилось, а голова движется

Физическое перемещение пользователя не подчиняется игровому коллайдеру. В проверке капсула остановилась перед стеной, а синтетическая позиция головы оказалась за ней. Помощник сохранил эту позицию и сообщил об остатке. Он не толкает отслеживаемую камеру назад и не останавливает человека.

Обработайте такое состояние в игре: прекратите нежелательные взаимодействия, ограничьте игровое движение и реализуйте подходящее визуальное уведомление или затемнение, сохраняя tracking. Эти реакции не реализованы данным помощником и требуют проверки на устройстве. Границы игровой геометрии не заменяют информацию о реальном помещении.

Диагностика

СимптомЧто проверить
Физический шаг перемещает изображение дваждыOrigin восстановлен после переноса body; другой скрипт не добавляет то же смещение
Камера движется по дуге при поворотеВращение компенсирует позицию головы, а не только origin
Коллайдер остаётся далеко от головыFollowHead вызывается с актуальной позой; слои/маски и препятствия не блокируют перенос
Голова за стеной, тело перед нейЭто возможный результат физического движения; нужна отдельная реакция игры
Приседание не меняет коллайдерЭтот пример меняет только горизонтальную позицию; высота — отдельная задача
Ошибка иерархииBody должен быть предком origin, origin — предком head; все объекты должны находиться в дереве

Проверены свободное перемещение, повторный вызов, обычный перенос тела, поворот с сохранением позиции головы, реальное столкновение капсулы со статической стеной и ошибочная иерархия. Движок запускался с XR выключенным; голова была обычной Entity3D с синтетическим transform. Реальные pose updates, room boundaries, comfort, стереовывод, высота коллайдера и поведение при движении платформ не проверялись.

Документация Reckfell EngineНаверх ↑
БЫСТРЫЙ ПЕРЕХОДНачало работыEntity и иерархияC# & .NET 10Сцены и префабы
Руководства и полный API Reckfell K
reckfellENGINE
RECKFELL ACCOUNT

Вход в аккаунт

Войдите в аккаунт Reckfell.

Один аккаунт для всей платформы Reckfell.