Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

GravityField

Gravity fields: planet gravity, a pull toward a point, an axis or a surface, or a zone with its own direction. Every dynamic body a field reaches (rigid bodies, ragdoll bones, a vehicle's chassis, soft body particles) takes it as its gravity inside every physics substep, from where the body stands then, so a moon keeps its orbit, a slingshot past a planet bends the way it should, and a crate dropped anywhere around a small planet falls onto its surface. Where no field reaches, a body takes the world's gravity vector (Physics.setGravity). Fields are scene data, synced like any component, so every peer pulls the same way.

Physics.gravityAt(point) reads the gravity at any point (exactly what the step applies there), Physics.upAt(point) the unit up against it (the way a character stands on a planet and a camera keeps level), and Physics.gravityFieldsAt(point) which fields act there and what each gives. Physics.bodyState(id).effectiveGravity is the gravity a body takes, after its gravityScale. The built-in CharacterController stands and walks against Physics.upAt, so a player walks all the way around a small planet.

Public fields:

  • shape (default "point"): what the field pulls toward, from where the entity stands and turned with it. "point": the entity's position (a planet, a star, a black hole). "axis": the entity's Y axis, or a segment of it length long (a cylinder, a capsule; with a negative strength, a rotating station's spin gravity). "direction": along direction inside the box halfExtents (an anti-gravity zone, a wall to walk on, a current). "surface": the nearest point of the entity's own colliders, whatever their shape (a box, a capsule, a convex hull, a mesh, a terrain). "path": the nearest point of the line through points (a path, a spline sampled into points, a track a body rides along; repeat the first point at the end for a loop).
  • strength (default 9.81): the pull at radius, in m/s². Negative pushes away: a "point" field with a negative strength and range the shell's radius is an inverted sphere, dropping what is inside onto the shell.
  • falloff (default "constant"): how the pull changes past radius. "constant"; "linear", to nothing at range; "inverseSquare", strength * (radius / distance)^2, a body of mass with radius its surface; "curve", strength times the scale curve states at the distance.
  • radius (default 1) and range (default 0: everywhere): where the falloff starts, and how far the field reaches, in world units from what it pulls toward (for a "direction" field: the height above the plane through the entity, against its pull).
  • halfExtents (default zero: everywhere) and direction (default { 0, -1, 0 }): a "direction" field's box and pull, in the entity's own frame.
  • length (default 0: the whole line): an "axis" field's segment length.
  • priority (default 0) and combine (default "priority"): where several fields reach a place, the "priority" fields of the highest priority are its gravity, summed when they share it, in place of the world's vector; "add" fields add on top of whatever acts there (a wind, a current, a tractor beam).
  • groups (default 0xFFFFFFFF): the collision groups the field acts on, against a body's CollisionGroup membership (a body with none belongs to every group).
  • curve (default empty): a "curve" falloff's { distance, scale } points, in order of distance.
  • points (default empty): a "path" field's { x, y, z } points, in the entity's own frame.

A body's gravityScale scales the gravity it takes, field or world. A field leaves its own entity's body alone, so a moving planet carries its gravity with it. Disabling the component (.enabled = false) switches the field off from the next step.

The step evaluates fields at every substep for every dynamic body while any field stands: about bodies times fields vector work each substep.

-- a small planet of radius 10 that walkers and crates fall onto, all the way round
local planet = entity.spawn("planet")
planet.component.add("SphereCollider", { radius = 10 })
planet.component.add("GravityField", { strength = 9.81, falloff = "inverseSquare", radius = 10, range = 80 })

-- a moon on a circular orbit 30 m out, set going on the first step: the field
-- and the moon's body stand in the physics world from the next step
local moon = entity.spawn("moon")
moon.localPosition = { 30, 0, 0 }
moon.component.add("Physics", { kind = "dynamic" })
moon.component.add("SphereCollider", { radius = 1 })
local off
off = Physics.onBeforeStep(function()
    off()
    Physics.setVelocity(moon.id, Physics.orbitVelocity(moon.position, planet.position))
end)

-- an anti-gravity zone that wins over the planet inside its box
zone.component.add("GravityField", { shape = "direction", direction = { 0, 1, 0 }, strength = 2, halfExtents = { 5, 5, 5 }, priority = 1 })

-- a ring station spinning gravity outward from its axis
station.component.add("GravityField", { shape = "axis", strength = -9.81, range = 50 })

-- which way is down here
local up = Physics.upAt(entity(player).position)