reckfellENGINE

Большие координаты: точность и перенос начала мира

Что ограничивает float, как проверить установленный SDK и сохранить логическую позицию при переносе.

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

Сначала проверьте масштаб

Речь о величине координат и требуемой точности, а не только о площади карты или числе объектов. При больших значениях соседние float отстоят дальше друг от друга; маленькое перемещение может исчезнуть при округлении. В проверенном SDK Vector3.X имеет тип Single, размер Vector3 — 12 байт. Тип можно вывести PhysicsBackendProbe.

Порядок координатыШаг между соседними float около этой величины
10 0000.0009765625
100 0000.0078125
1 000 0000.0625
10 000 0001
100 000 0008

При обычных метровых единицах шаг 8 — восемь метров. Это оценка представления числа, не готовый предел размера игры. В тесте Vector3 с X = 100 000 000 теряет прибавку 0.1. Увеличение масштаба модели или дальности камеры не возвращает потерянные разряды. Сначала держите разумные размеры объектов и проверяйте минимально нужную точность контактов/рисунка.

Разделить логический мир и локальную симуляцию

Храните глобальную позицию как логические координаты с нужной точностью — например double или целые индексы участков плюс локальное смещение. Физику/рендер активной области держите около начала координат. При переносе начала сохраните логический offset и согласованно сдвиньте активные сущности. Сетевые сообщения, сохранения, навигация и фоновые задания должны знать, в какой системе находится каждая точка.

Перенос не загружает участки мира и не удаляет далёкие. Потоковая загрузка, владение участками и политика активации — отдельные системы проекта. Очень большая плоскость сама по себе не даёт точную бесконечную симуляцию.

Рабочий пример переноса

Прикрепите OriginShiftLab3D.cs к корню Entity3D отдельного C#-уровня, соберите и запустите F6. Физика 60 шагов/с, гравитация 9.8 вниз; корень статичный с единичным масштабом. Персонаж автоматически идёт по X. При расстоянии 32 от нуля код сдвигает прямых пространственных детей WorldRoot, сохраняет offset в double и пишет OriginShift в Output. Локальная позиция возвращается близко к нулю, логическая продолжает расти.

Локальный персонаж и физический пол примера переноса

OriginShiftLab3D.cs
C#
using Reckfell;

public partial class OriginShiftLab3D : Entity3D
{
    public Entity3D WorldRoot { get; private set; } = null!;
    public CharacterBody3D Player { get; private set; } = null!;
    public Camera3D Camera { get; private set; } = null!;
    public Vector2 MoveInput { get; set; } = Vector2.Right;
    public float Speed { get; set; } = 12;
    public float Threshold { get; set; } = 32;
    public double OriginX { get; private set; }
    public double OriginY { get; private set; }
    public double OriginZ { get; private set; }
    public int Shifts { get; private set; }
    public (double X,double Y,double Z) LogicalPosition => (OriginX + Player.GlobalPosition.X,OriginY + Player.GlobalPosition.Y,OriginZ + Player.GlobalPosition.Z);
    public override void _Ready()
    {
        WorldRoot = new Entity3D { Name = "World" }; AddChild(WorldRoot);
        var floor = new StaticBody3D { Position = new Vector3(0,-.25f,0), CollisionLayer = 1, CollisionMask = 0 };
        floor.AddChild(new CollisionShape3D { Shape = new BoxShape3D { Size = new Vector3(512,.5f,32) } });
        floor.AddChild(new MeshInstance3D { Mesh = new BoxMesh { Size = new Vector3(512,.5f,32) }, MaterialOverride = Material(new Color(.55f,.55f,.6f)) }); WorldRoot.AddChild(floor);
        Player = new CharacterBody3D { Position = new Vector3(0,.9f,0), CollisionLayer = 2, CollisionMask = 1, FloorSnapLength = .2f };
        Player.AddChild(new CollisionShape3D { Shape = new CapsuleShape3D { Radius = .35f, Height = 1.8f } });
        Player.AddChild(new MeshInstance3D { Mesh = new CapsuleMesh { Radius = .35f, Height = 1.8f }, MaterialOverride = Material(new Color(.25f,.45f,1)) }); WorldRoot.AddChild(Player);
        Camera = new Camera3D { Current = true }; WorldRoot.AddChild(Camera); FollowCamera(); ResetPhysicsInterpolation();
    }
    private static StandardMaterial3D Material(Color color) => new() { AlbedoColor = color, ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded };
    private void FollowCamera() { Camera.GlobalPosition = Player.GlobalPosition + new Vector3(7,5,8); Camera.LookAt(Player.GlobalPosition); }
    public override void _PhysicsProcess(double delta)
    {
        Vector2 input = MoveInput.IsFinite() ? MoveInput.LimitLength() : Vector2.Zero;
        Vector3 velocity = Player.Velocity;
        velocity.X = input.X * Speed; velocity.Z = input.Y * Speed;
        if (!Player.IsOnFloor()) velocity += Player.GetGravity() * (float)delta;
        Player.Velocity = velocity; Player.MoveAndSlide(); FollowCamera(); RebaseIfNeeded();
    }
    public bool RebaseIfNeeded()
    {
        if (!float.IsFinite(Threshold) || Threshold <= 0) return false;
        Vector3 shift = Player.GlobalPosition;
        if (!shift.IsFinite() || shift.Length() < Threshold) return false;
        foreach (Entity child in WorldRoot.GetChildren())
            if (child is Entity3D spatial) { spatial.GlobalPosition -= shift; spatial.ResetPhysicsInterpolation(); }
        OriginX += shift.X; OriginY += shift.Y; OriginZ += shift.Z; Shifts++;
        ReckfellApi.Print($"OriginShift {Shifts}: ({OriginX:F2}, {OriginY:F2}, {OriginZ:F2})"); return true;
    }
}

Это ограниченный уровень с полом длиной 512, без загрузки новых участков. Камера, StaticBody и CharacterBody — прямые дети статичного WorldRoot. Сам WorldRoot не накапливает огромный transform. Вложенные обычные сущности следуют за своим пространственным родителем; отдельные TopLevel-ветви, другие viewport/world, сохранённые мировые точки и внешние системы этим циклом автоматически не исправляются.

Для RigidBody, физических костей, SoftBody, частиц, мировых шейдерных координат, trail и сетевых буферов нужны собственные согласованные действия. Не переносите весь произвольный проект таким foreach без аудита систем. Физические перемещения здесь выполняются после MoveAndSlide в том же _PhysicsProcess; после скачка сбрасывается визуальная интерполяция. Она не заменяет изменение логической системы координат.

Двойная точность сборки

В исходной системе сборки есть precision=double; управляемые математические типы также зависят от ReckfellFloat64. Нельзя переключить одну C#-константу или взять SDK другой точности к имеющемуся native runtime. Согласованно пересобираются runtime, управляемые bindings/SDK, расширения и шаблоны экспорта; такой набор нужно отдельно проверить и установить. Этот материал проверен на текущей single-сборке, double-сборка здесь не выполнялась.

Double не делает все GPU-вычисления, материалы, частицы или внешние библиотеки автоматически 64-битными. Сохраняйте понятные единицы и проверяйте свой рендер/экспорт. Не меняйте физическую частоту или интерполяцию ради исправления арифметической потери точности: они решают другие задачи.

Проверить свой перенос

Сравнивайте логическую позицию до/после сдвига, относительные расстояния, контакт с полом, камеры и сохранённые пути/цели. Тест выполняет шесть переносов и проверяет сохранение позиции и контакта на обоих backend. Он не подтверждает произвольный стриминг, все системы шейдеров/анимации или сетевую согласованность. Начните с минимального уровня и добавляйте системы по одной.

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

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

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

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