reckfellENGINE

The 3D character controller

Complete SquashPlayer.cs: movement, gravity, jump, contact checks and resetting state.

On this page

Attach Scripts/SquashPlayer.cs to the player root. Add SquashMob.cs, SquashGame.cs, SquashHud.cs and LandingPulse.cs before the full build; these classes reference one another.

Complete SquashPlayer.cs
C#
using Reckfell;

public partial class SquashPlayer : CharacterBody3D
{
    [Signal] public delegate void HitEventHandler();
    [Export] public float Speed { get; set; } = 5;
    [Export] public float Gravity { get; set; } = 24;
    [Export] public float JumpSpeed { get; set; } = 8;
    [Export] public float BounceSpeed { get; set; } = 6;
    public bool Active { get; set; } = true;
    private Area3D _detector = null!;
    private LandingPulse _visual = null!;

    public override void _Ready()
    {
        _detector = GetEntity<Area3D>("MobDetector");
        _visual = GetEntity<LandingPulse>("Visual");
    }

    public override void _PhysicsProcess(double delta)
    {
        if (!Active)
        {
            Velocity = Vector3.Zero;
            return;
        }

        Vector3 velocity = Velocity;
        if (!IsOnFloor()) velocity.Y -= Gravity * (float)delta;
        if (IsOnFloor() && Input.IsActionJustPressed("jump")) velocity.Y = JumpSpeed;
        Vector2 input = Input.GetVector("move_left", "move_right", "move_forward", "move_back");
        velocity.X = input.X * Speed;
        velocity.Z = input.Y * Speed;
        bool falling = velocity.Y < 0;
        Velocity = velocity;
        MoveAndSlide();
        Position = new Vector3(Mathf.Clamp(Position.X, -6, 6), Position.Y, Mathf.Clamp(Position.Z, -6, 6));

        if (falling && IsOnFloor()) _visual.Pulse();
        for (int i = 0; i < GetSlideCollisionCount(); i++)
        {
            KinematicCollision3D collision = GetSlideCollision(i);
            if (falling && collision.GetNormal().Y > 0.5f &&
                collision.GetCollider() is SquashMob mob && mob.TrySquash())
            {
                velocity = Velocity;
                velocity.Y = BounceSpeed;
                Velocity = velocity;
                return;
            }
        }

        if (IsOnFloor())
        {
            foreach (Entity3D body in _detector.GetOverlappingBodies())
            {
                if (body is SquashMob mob && !mob.IsQueuedForDeletion())
                {
                    TakeHit();
                    break;
                }
            }
        }
    }

    public void StartAt(Vector3 position)
    {
        Position = position;
        Velocity = Vector3.Zero;
        ResetPhysicsInterpolation();
        _visual.ResetVisual();
        Active = true;
    }

    public void TakeHit()
    {
        if (!Active) return;
        Active = false;
        Velocity = Vector3.Zero;
        EmitSignal(SignalName.Hit);
    }
}

Speed and physics step

_PhysicsProcess() updates Velocity in world units per second. MoveAndSlide() applies the physics interval, so do not multiply movement velocity by delta again. Gravity changes vertical velocity over time, which is why Gravity * delta belongs there.

Input.GetVector() limits direction length. Input X becomes world X; input Y becomes world Z. W means negative Z, and controls do not rotate with the camera. No input clears X/Z velocity.

Position X/Z is clamped to −6…6 to keep the character on the 14 × 14 floor. This is a prototype rule, not invisible physics walls.

State

Active = false explicitly disables controls and clears velocity. StartAt() resets position, velocity, interpolation and visual animation for another round. Call it on a ready player; _Ready() obtains MobDetector and Visual references.

After MoveAndSlide(), contacts describe the current movement. See stomps and side contacts. They explain parts of this complete class, not additional scripts.

Result

Build all five classes, run Arena.rscene and press Start. The character moves at 5 units/second without faster diagonals, and Space jumps only from the floor. For a missing character, check camera and mesh; for falling through, check both shapes and player mask 5.

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.