reckfellENGINE

2D parallax: camera, layers and background repetition

Two complete depth layers with a correct repeat period and coverage that adapts to window size.

On this page

Result

Two repeating background layers move at different speeds relative to Camera2D. White dots belong to the distant layer and blue rectangles to the nearer one. Arrow keys move the camera. Code draws the pattern; no external textures are needed.

This is a visual background. Repetition does not create entity copies, physics bodies or additional levels. Parallax2D organizes decorative layers; it does not stream a gameplay world.

Run the complete example

  1. Create a C# project with Reckfell.NET.Sdk 1.0.0 and net10.0.
  2. Save the two files below and run dotnet build.
  3. Create a level with an Entity2D root, attach ParallaxExample.cs and save parallax.rscene.
  4. Run that level, focus the game window and hold the right arrow. Blue rectangles move left faster than white dots.

The sample uses the standard ui_left/ui_right/ui_up/ui_down actions. Existing project remappings apply. It creates its Camera2D and both layers; do not add a second camera/background setup.

ParallaxPattern.cs
C#
using Reckfell;

public partial class ParallaxPattern : Entity2D
{
    public bool Distant { get; set; }

    public override void _Draw()
    {
        if (Distant)
        {
            DrawRect(new Rect2(0, 0, 128, 96), new Color(0.1f, 0.11f, 0.13f));
            DrawCircle(new Vector2(32, 24), 6, Colors.White);
            DrawCircle(new Vector2(96, 56), 3, Colors.White);
        }
        else
        {
            DrawRect(new Rect2(88, 64, 20, 32), new Color(0.25f, 0.45f, 1));
        }
    }
}
ParallaxExample.cs
C#
using Reckfell;

public partial class ParallaxExample : Entity2D
{
    public Parallax2D Far { get; private set; } = null!;
    public Parallax2D Near { get; private set; } = null!;
    public Camera2D Camera { get; private set; } = null!;
    private Vector2 _visibleSize;

    public override void _Ready()
    {
        Camera = new Camera2D { Position = GetViewportRect().Size * 0.5f };
        AddChild(Camera);
        Camera.MakeCurrent();
        Far = MakeLayer(0.25f, true, -20);
        Near = MakeLayer(0.75f, false, -10);
        FitRepeats();
    }

    private Parallax2D MakeLayer(float speed, bool distant, int order)
    {
        var layer = new Parallax2D
        {
            ScrollScale = new Vector2(speed, speed),
            RepeatSize = new Vector2(128, 96),
            ZIndex = order
        };
        layer.AddChild(new ParallaxPattern { Distant = distant });
        AddChild(layer);
        return layer;
    }

    public void FitRepeats()
    {
        Vector2 zoom = Camera.Zoom.Abs();
        if (zoom.X <= 0 || zoom.Y <= 0 || !zoom.IsFinite()) return;
        Vector2 visible = GetViewportRect().Size / zoom;
        if (visible == _visibleSize) return;
        _visibleSize = visible;
        int repeats = Mathf.CeilToInt(Mathf.Max(visible.X / 128, visible.Y / 96)) * 2 + 2;
        Far.RepeatTimes = repeats;
        Near.RepeatTimes = repeats;
    }

    public override void _Process(double delta)
    {
        Camera.Position += Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down") * 200 * (float)delta;
        FitRepeats();
    }
}

Choose layer speed

With the default FollowViewport=true, IgnoreCameraScroll=false, unit scale and Zoom=(1,1), moving the camera right by 40 units produces these pattern displacements on screen:

ScrollScale.XPattern movement leftUse
00A distant background fixed relative to the camera
0.2510The sample distant layer
0.7530The sample nearer layer
140Ordinary world speed

X and Y are independent. A smaller value usually suggests more distance but does not change actual geometry or perspective. Repetition can wrap the layer coordinate by a period while preserving the visible pattern. Compare phase and rendered output rather than raw Position alone.

Repeat without gaps

RepeatSize is the pattern period in local units, not screen dimensions. Our drawing occupies (0,0)—(128,96), so RepeatSize=(128,96). Zero disables repetition on that axis. Use (128,0) for horizontal repetition only.

RepeatTimes sets the budget of rendered repeats. It does not resize the artwork or create child entities. FitRepeats updates that budget for the visible Viewport area and the positive magnitude of Camera.Zoom. Zooming out exposes more world, requiring more repeats. This is a conservative calculation for an unrotated camera, unit layer scale and this pattern period; it does not promise minimal rendering cost.

When substituting a texture, place its origin at (0,0). A Sprite2D covering one period usually needs Centered disabled. Atlas regions, sprite offsets and child drawing scale must agree with the period. RepeatSize neither clips artwork to a rectangle nor makes mismatched edges seamless.

Doubling artwork scale without changing the period overlaps neighboring repeats. An oversized period leaves gaps. Provide vertical coverage separately for decorations that do not repeat vertically: (128,0) does not automatically fill the screen above and below.

Add motion without moving the camera

Set Far.Autoscroll=(60,0), for example. The engine advances ScrollOffset at 60 local units per second. This is additional pattern motion, not camera velocity. Offset wraps by RepeatSize on a repeated axis.

Built-in Autoscroll requires at least one nonzero RepeatSize component. RepeatSize=(0,0) disables its internal processing. ProcessMode=Disabled also stops it. This internal autoscroll does not run as gameplay processing in the editor.

Use ScrollOffset to move the pattern phase manually. Do not assign Position every frame: Parallax2D calculates it. The camera normally updates ScreenOffset; manual control requires deliberate IgnoreCameraScroll and FollowViewport settings. Keep the sample defaults for ordinary backgrounds and use CanvasLayer for HUD elements.

Diagnose defects

SymptomCause and action
No visible depth differenceCheck distinct ScrollScale values and an active Camera2D in the same Viewport
Gaps after widening the window or zooming outIncrease RepeatTimes; do not alter the period only to cover the screen
Seams or overlapsMatch RepeatSize to actual dimensions, offsets and artwork edges
Autoscroll does nothingCheck nonzero RepeatSize, ProcessMode and that the game is running rather than expecting editor motion
Position changes after manual assignmentUse ScrollOffset; the parallax implementation controls Position
Repeated decorations do not collide with the playerThese repeats are visual only; create gameplay geometry separately

Verified in Compatibility/OpenGL: drawing repeats on both axes, 40-unit and 512-unit camera motion, ScrollOffset periodicity, autoscroll and its stop conditions, Viewport resize and Zoom changes. Camera rotation, nonuniform scale, LimitBegin/LimitEnd, other renderers and exports were not exercised by this example.

2D coordinates · Camera-independent HUD · Actor movement

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.