Vectors: direction, distance and bouncing
A complete 2D aiming example, angle sign, Dot/Cross and Bounce versus Reflect.
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.
VectorAim2D.cs
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
| Expression | Purpose |
|---|---|
| offset.Length() | Distance |
| offset.LengthSquared() | Compare with radius × radius without a square root |
| offset.Normalized() × speed | Velocity with a chosen magnitude, for nonzero offset |
| position + velocity × delta | Next 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.