Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit61a33237e0

README.md

characterController

Physics-based character controller — capsule movement with ground / wall / ceiling detection. A kinematic controller using raycasts for collision; handles gravity, jumping, slope traversal, step climbing, wall sliding, coyote time, and moving platforms. The module owns a per-entity registry; attach once, call move / jump each frame, then drive a per-frame update(dt). The install / uninstall / preset surface wires up the standard set of components (CharacterController + Locomotion + MovementState + optional WASDInput and third-person camera child) in one call.

Require it to use the surface:

local CharacterController = require("@builtin::systems.characterController.characterController")

Exports

  • CC.attach(entityId: string, userConfig: Config?) — register an entity, merge user overrides over Defaults.standard.
  • CC.detach(entityId: string) — drop the registry entry.
  • CC.move(entityId: string, direction: Vec3) — set per-frame world-space movement input (y is ignored).
  • CC.jump(entityId: string) — request a jump; honours coyote time and jump buffer.
  • CC.getState(entityId: string) -> State — snapshot copy of grounded / falling / velocity / etc.
  • CC.getVelocity(entityId: string) -> (number, number, number) — current vx, vy, vz.
  • CC.setGravity(entityId: string, gravity: number) — update gravity at runtime.
  • CC.setMaxSlope(entityId: string, degrees: number) — update max walkable slope angle.
  • CC.setConfig(entityId: string, key: string, value: any) — update a single config key.
  • CC.getConfig(entityId: string) -> Config — config copy.
  • CC.update(entityId: string, dt: number) — advance one frame (resolve movement, write position, tick state machine). dt is clamped to 0.1.
  • CC.isAttached(entityId: string) -> boolean — whether the entity has a registration.
  • CC.install(entityId: string, opts: InstallOpts?) -> InstallResult — high-level wire-up (components + optional camera / mesh / WASDInput).
  • CC.uninstall(entityId: string) -> UninstallResult — remove everything install added.
  • CC.preset(entityId: string, presetName: string) -> PresetResult — apply a preset to an already-installed character in place.

Types:

  • Vec3 = { x: number, y: number, z: number }
  • Config = { height?, radius?, maxSlopeAngle?, stepHeight?, gravity?, jumpForce?, coyoteTime?, jumpBuffer?, maxJumps?, airControlFactor?, groundSnapDistance?, skinWidth?, moveSpeed? }
  • State = { grounded, falling, sliding, wallContact, wallNormal, velocity, groundNormal, slopeAngle, jumpCount, timeSinceGrounded, timeSinceWallContact, onMovingPlatform, platformEntity? }
  • BundleRef = { guid?, __ref?, identity?, path?, name? }
  • InstallOpts = { preset?, camera?, input?, mesh?, overrides? }
  • InstallResult = { entityId, cameraId?, meshHostId? }
  • UninstallResult = { entityId, despawned }
  • PresetResult = { entityId, preset, applied }

Usage

local CharacterController = require("@builtin::systems.characterController.characterController")

-- Low-level: manual attach + per-frame drive.
CharacterController.attach(entityId, { gravity = -15, jumpForce = 7 })
CharacterController.move(entityId, { x = 1, y = 0, z = 0 })
CharacterController.update(entityId, dt)

-- High-level: one-call wire-up.
CharacterController.install(entityId, {
    preset = "humanoid_default",
    camera = true,
    overrides = { jumpForce = 8 },
})

Notes

  • attach merges user config over Defaults.standard; passing unknown keys is allowed (they survive into the config table).
  • update clamps dt to 0.1s to prevent capsule tunnelling on lag spikes.
  • getState and getConfig return shallow copies — mutating the result does not affect the controller. The returned velocity / wallNormal / groundNormal are also shallow copies.
  • install is idempotent per component — re-running on an already wired entity adds nothing new. uninstall removes the components and any third-person-camera child that targets this entity.
  • preset mutates fields in place and never adds or removes components — call install first.