reckfellENGINE

Vectors: direction, distance and bouncing

A complete 2D aiming example, angle sign, Dot/Cross and Bounce versus Reflect.

On this page

Aim toward a target

Create a 1.0.0 C# level with an Entity2D root, add VectorAim2D.cs and attach it to the root. Use a 640×420 window/viewport to match the image, build and press F6. Move the mouse: the blue arrow runs from (100, 270) to the target, with length and angle in the caption. GetLocalMousePosition supplies the same local space used by drawing.

The origin-to-target vector and its X-axis projection

VectorAim2D.cs
C#
using System;
using Reckfell;

public partial class VectorAim2D : Entity2D
{
    [Export] public bool UseMouse { get; set; } = true;
    public Vector2 OriginPoint => new(100, 270);
    public Vector2 Target { get; private set; } = new(490, 150);
    public Vector2 Offset => Target - OriginPoint;
    public Vector2 Direction => Offset.Normalized();
    private Label _status = null!;
    private bool _ru;
    private readonly Color _blue = new(0.25f, 0.46f, 1);

    public override void _Ready()
    {
        _ru = TranslationServer.GetLocale().StartsWith("ru");
        var title = new Label { Position = new Vector2(24, 20), Text = _ru
            ? "Векторы 2D: начало, цель и направление" : "2D vectors: origin, target and direction" };
        title.AddThemeFontSizeOverride("font_size", 22);
        AddChild(title);
        _status = new Label { Position = new Vector2(24, 58) };
        AddChild(_status);
        UpdateCaption();
    }

    public Error SetTarget(Vector2 target)
    {
        if (!target.IsFinite()) return Error.InvalidParameter;
        Target = target;
        if (_status is not null) UpdateCaption();
        QueueRedraw();
        return Error.Ok;
    }

    public override void _Process(double delta)
    {
        if (!UseMouse) return;
        Vector2 mouse = GetLocalMousePosition().Clamp(new Vector2(40, 115), new Vector2(600, 370));
        if (!mouse.IsEqualApprox(Target)) SetTarget(mouse);
    }

    private void UpdateCaption()
    {
        string angle = Offset.IsZeroApprox() ? "--" :
            FormattableString.Invariant($"{Mathf.RadToDeg(Vector2.Right.AngleTo(Direction)):F1}");
        _status.Text = _ru
            ? FormattableString.Invariant($"X вправо, Y вниз. Длина {Offset.Length():F1}; угол {angle}°")
            : FormattableString.Invariant($"X right, Y down. Length {Offset.Length():F1}; angle {angle}°");
    }

    private void Arrow(Vector2 from, Vector2 to, Color color)
    {
        Vector2 offset = to - from;
        if (offset.IsZeroApprox()) return;
        Vector2 direction = offset.Normalized();
        DrawLine(from, to, color, 3, true);
        DrawLine(to, to - direction.Rotated(0.45f) * 12, color, 3, true);
        DrawLine(to, to - direction.Rotated(-0.45f) * 12, color, 3, true);
    }

    public override void _Draw()
    {
        DrawRect(new Rect2(0, 0, 640, 420), new Color(0.035f, 0.04f, 0.05f));
        Arrow(OriginPoint, OriginPoint + Vector2.Right * 160, new Color(0.5f, 0.5f, 0.5f));
        Arrow(OriginPoint, OriginPoint + Vector2.Down * 90, new Color(0.3f, 0.3f, 0.3f));
        Vector2 projection = OriginPoint + Vector2.Right * Offset.Dot(Vector2.Right);
        DrawLine(Target, projection, new Color(0.3f, 0.35f, 0.45f), 1, true);
        Arrow(OriginPoint, Target, _blue);
        DrawCircle(OriginPoint, 5, Colors.White);
        DrawCircle(Target, 7, Colors.White);
    }
}

Offset = Target − OriginPoint. A target at (400, 270) gives (300, 0), with normalized direction (1, 0). Coincident points normalize to zero; choosing an aim direction/angle for them is meaningless. The example displays “--” for that angle.

Choose the quantity

ExpressionPurpose
offset.Length()Distance
offset.LengthSquared()Compare with radius × radius without a square root
offset.Normalized() × speedVelocity with a chosen magnitude, for nonzero offset
position + velocity × deltaNext geometric position, without collision handling

(3, 4) has length 5, squared length 25 and normalized value (0.6, 0.8). Normalization removes distance. Do not use it when displacement magnitude matters. For physics bodies, use the relevant collision-motion API instead of directly assigning Position from this formula.

Dot, Cross and angle

Dot of two unit directions is their angle's cosine: 1 aligned, 0 perpendicular, −1 opposite. For a view cone, compare Dot against cos(half-angle), normalizing directions and checking for nonzero displacement first.

In 2D, Cross returns the scalar x₁y₂ − y₁x₂. Right.Cross(Down) = 1 and Right.AngleTo(Down) = π/2. Because screen Y points down, positive angles appear clockwise. Right.Orthogonal() gives Up. In 3D, Cross returns Vector3: Right.Cross(Up) = Back, or +Z; Forward is −Z.

Bounce off a surface

Use Bounce with a unit surface normal to bounce velocity. In this SDK, Reflect computes 2 × dot(v,n) × n − v, while Bounce is its negative. For v = (1, 1) and n = Up, Bounce gives (1, −1), but Reflect gives (−1, 1). Do not transfer another API's Reflect name without checking behavior.

Slide removes the component along a unit normal. It is a geometric operation: it does not find a surface or move a physics body. Large coordinates and almost-zero directions need suitable tolerances; exact float equality is not a universal proximity check.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.