Physics Scripting

Headers: Core/include/Core/PhysicsWorld.h, Core/include/Core/PhysicsComponents.h

Physics vs Direct Transform

ApproachWhen to Use
Direct transformUI elements, non-physical objects, simple movement
Physics velocityCharacters, projectiles, anything that collides

When an entity has a RigidBody and Collider, the physics engine (Jolt) controls its position. Modifying Transform.position directly will fight the physics simulation. Instead, use velocity:

engine.GetPhysicsWorld().SetLinearVelocity(e.id(), vx, vy, vz);

Setting Velocity

Full Velocity

Sets all three axes. Overwrites any existing velocity including gravity:

engine.GetPhysicsWorld().SetLinearVelocity(e.id(), vx, vy, vz);

Horizontal Velocity Only

Sets X and Z but preserves the Y velocity (gravity/jumping):

engine.GetPhysicsWorld().SetHorizontalVelocity(e.id(), vx, vz);
Tip

Use SetHorizontalVelocity for character controllers. It lets you control movement on the ground plane without interfering with falling or jumping.

Physics API Reference

MethodDescription
SetLinearVelocity(id, vx, vy, vz)Set full velocity vector
SetHorizontalVelocity(id, vx, vz)Set XZ velocity, preserve Y
SetGravity(x, y, z)Set world gravity (default: 0, -9.81, 0)
HasBody(id)Check if entity has a physics body
RefreshEntity(entity)Recreate the physics body after changes

RigidBody Component

Controls how the physics engine treats an entity:

struct RigidBody {
  Type type = Dynamic;       // Static, Dynamic, or Kinematic
  float mass = 1.0f;
  float linearDamping = 0.05f;
  float angularDamping = 0.05f;
  float restitution = 0.3f;  // bounciness (0 = no bounce, 1 = full bounce)
  float friction = 0.5f;
  float gravityFactor = 1.0f; // 0 = no gravity, 1 = normal, 2 = double
  bool freezePositionX/Y/Z;  // lock position on axis
  bool freezeRotationX/Y/Z;  // lock rotation on axis
};

Body Types

TypeMoves?Affected by Forces?Use For
StaticNoNoFloors, walls, platforms
DynamicYesYesPlayers, enemies, projectiles
KinematicYesNoMoving platforms, elevators

Collider Component

Defines the collision shape:

struct Collider {
  ColliderShape shape;  // Box, Sphere, or Capsule
  // Box
  float halfExtentX, halfExtentY, halfExtentZ;
  // Sphere / Capsule
  float radius;
  // Capsule only
  float halfHeight;
  // Offset from entity center
  float offsetX, offsetY, offsetZ;
  bool showOutline;  // debug wireframe
};

Example: Physics Player Controller

class PlayerBehavior : public Behavior {
public:
  void OnUpdate(flecs::entity e, Engine &engine, float dt) override {
    auto &input = engine.GetInput();
    float speed = 6.0f;
    float vx = 0.0f, vz = 0.0f;

    if (input.IsKeyDown(GLFW_KEY_W)) vz -= speed;
    if (input.IsKeyDown(GLFW_KEY_S)) vz += speed;
    if (input.IsKeyDown(GLFW_KEY_A)) vx -= speed;
    if (input.IsKeyDown(GLFW_KEY_D)) vx += speed;

    // Use horizontal velocity to preserve gravity
    engine.GetPhysicsWorld().SetHorizontalVelocity(e.id(), vx, vz);

    // Jump (set full velocity to add upward impulse)
    if (input.IsKeyPressed(GLFW_KEY_SPACE)) {
      engine.GetPhysicsWorld().SetLinearVelocity(e.id(), vx, 8.0f, vz);
    }
  }
};
Warning

Freeze rotation axes on player characters to prevent them from tipping over. In the Inspector, enable Freeze Rotation X and Freeze Rotation Z on the RigidBody component.