reckfellENGINE

Entity3D: позиция, поворот и масштаб родителя

Практика локального/мирового пространства, направления −Z и корректного переноса нормалей.

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

Запустить иерархию

Создайте отдельный C#-уровень 1.0.0 с корнем Entity3D. Добавьте TransformRig3D.cs и прикрепите к корню. Для вида ниже задайте окно/viewport 640×420, соберите и нажмите F6. Скрипт создаёт камеру, пол и родителя с позицией (1, 0, 0), поворотом Y на π/4 и масштабом (1,5; 1; 0,6). Синий куб находится у него в локальной точке (1, 1, 0).

Entity3D с локальными осями, дочерним кубом и мировой проекцией

TransformRig3D.cs
C#
using System;
using Reckfell;

public partial class TransformRig3D : Entity3D
{
    public Entity3D Pivot { get; private set; } = null!;
    public MeshInstance3D Marker { get; private set; } = null!;
    public Plane GroundPlane => new(Vector3.Up, 0);
    public Vector3 ProjectedPoint => GroundPlane.Project(Marker.GlobalPosition);

    private static MeshInstance3D Box(Vector3 size, Color color, Vector3 position)
    {
        return new MeshInstance3D
        {
            Mesh = new BoxMesh { Size = size }, Position = position,
            MaterialOverride = new StandardMaterial3D
            {
                AlbedoColor = color, ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded
            }
        };
    }

    public override void _Ready()
    {
        AddChild(new WorldEnvironment { Environment = new Reckfell.Environment
        {
            BackgroundMode = Reckfell.Environment.BGMode.Color,
            BackgroundColor = new Color(0.035f, 0.04f, 0.05f)
        }});
        AddChild(Box(new Vector3(8, 0.05f, 6), new Color(0.10f, 0.11f, 0.13f), new Vector3(0, -0.025f, 0)));
        Pivot = new Entity3D
        {
            Position = new Vector3(1, 0, 0), Rotation = new Vector3(0, Mathf.Pi / 4, 0),
            Scale = new Vector3(1.5f, 1, 0.6f)
        };
        AddChild(Pivot);
        Pivot.AddChild(Box(new Vector3(1.6f, 0.045f, 0.045f), new Color(0.25f, 0.46f, 1), new Vector3(0.8f, 0, 0)));
        Pivot.AddChild(Box(new Vector3(0.045f, 1.5f, 0.045f), Colors.White, new Vector3(0, 0.75f, 0)));
        Pivot.AddChild(Box(new Vector3(0.045f, 0.045f, 1.5f), new Color(0.45f, 0.48f, 0.55f), new Vector3(0, 0, 0.75f)));
        Marker = Box(new Vector3(0.2f, 0.2f, 0.2f), new Color(0.25f, 0.46f, 1), new Vector3(1, 1, 0));
        Pivot.AddChild(Marker);
        AddChild(Box(new Vector3(0.25f, 0.035f, 0.25f), Colors.White, ProjectedPoint + Vector3.Up * 0.025f));
        var camera = new Camera3D { Position = new Vector3(5, 4.5f, 7), Current = true, Fov = 48 };
        AddChild(camera); camera.LookAt(new Vector3(0.7f, 0.4f, 0), Vector3.Up);
        var canvas = new CanvasLayer(); AddChild(canvas);
        bool ru = TranslationServer.GetLocale().StartsWith("ru");
        var title = new Label { Position = new Vector2(24, 20), Text = ru
            ? "Локальная точка и мировая плоскость" : "A local point and a world-space plane" };
        title.AddThemeFontSizeOverride("font_size", 22); canvas.AddChild(title);
        canvas.AddChild(new Label { Position = new Vector2(24, 58), Text = ru
            ? "Синий куб: local (1,1,0). Белая метка: проекция на Y = 0."
            : "Blue cube: local (1,1,0). White marker: projection onto Y = 0." });
        ReckfellApi.Print("TRANSFORM local=" + Pivot.ToLocal(Marker.GlobalPosition));
    }

    public static bool TryTransformNormal(Basis basis, Vector3 normal, out Vector3 result)
    {
        result = Vector3.Zero;
        if (!basis.IsFinite() || !normal.IsFinite() || normal.LengthSquared() < 0.000001f ||
            MathF.Abs(basis.Determinant()) < 0.000001f) return false;
        Vector3 transformed = basis.Inverse().Transposed() * normal;
        if (!transformed.IsFinite() || transformed.LengthSquared() < 0.000001f) return false;
        result = transformed.Normalized();
        return true;
    }
}

В консоли TRANSFORM local должен быть около (1, 1, 0): ToLocal возвращает мировую позицию куба обратно в пространство Pivot. Изменение масштаба/поворота родителя меняет GlobalPosition ребёнка без изменения его Position.

Выбрать пространство

Свойство/операцияПространство
Position, Rotation, Scale, TransformОтносительно родительской Entity3D
GlobalPosition, GlobalTransformМировое
ToGlobal(localPoint)Из локального в мировое для этой Entity3D
ToLocal(worldPoint)Из мирового в локальное для этой Entity3D

Для прицеливания используйте target.GlobalPosition − actor.GlobalPosition либо сначала переведите обе точки в одно локальное пространство. Vector3.Forward — (0, 0, −1). Для обычного объекта, ориентированного по −Z, мировой вектор вперёд получают из −GlobalBasis.Column2; масштаб влияет на его длину, поэтому для направления его нормализуют. Импортированная модель может иметь другую переднюю ось: это настройка модели/ориентации, а не смена мирового соглашения.

Поворот без путаницы

Rotation хранит углы Euler в радианах; RotationDegrees — в градусах. Порядок Euler влияет на результат, а повороты вокруг разных осей не коммутируют. Не складывайте три угла как универсальную замену композиции 3D-поворотов. Для плавного перехода произвольной ориентации используйте единичные Quaternion и Slerp; для обычного наведения проверяйте, что цель отличается от позиции и up не параллелен направлению.

Методы структуры Transform3D и методы Entity3D — не всегда одинаковые операции. Transform3D.Rotated поворачивает всё преобразование, включая Origin вокруг внешнего начала; RotatedLocal применяет вращение в локальном базисе и сохраняет Origin. Не подменяйте ими изменение ориентации объекта без проверки желаемого центра вращения. Подробнее о порядке умножения — в руководстве по матрицам.

Неравномерный масштаб

В примере оси масштабированы неодинаково. Для обратного перевода точек нужен AffineInverse; для нормали поверхности — обратный транспонированный Basis с последующей нормализацией. Helper TryTransformNormal проверяет выбранные границы и демонстрирует этот случай; порог близости к вырождению подбирается по масштабу вашей задачи.

Масштаб 0 делает соответствующее направление необратимым. Orthonormalized удаляет масштаб/искажение осей, а не исправляет модель бесплатно. Под иерархией с неравномерным масштабом и поворотами может появиться shear; позиция, Euler и Scale не всегда передают всю такую матрицу без потерь. Для сложной анимации отделяйте чистый поворот от масштабирования и проверяйте итоговую иерархию.

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

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

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

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

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