Physics and joints
KHIM simulates parts on the game server (and in KHIM Engine's Play). One unit is one stud; gravity is 196.2 units/s², as in Roblox.
Anchored or loose
- An anchored part stays exactly where it is. Floors, walls, buildings.
- A loose part (
anchored = false) falls, rolls, is pushed and knocked about. Crates, balls, debris.
local crate = world.Crate
crate.anchored = false
crate:applyImpulse(vec3(0, 500, -300)) -- a shove, divided by its mass
crate.velocity = vec3(0, 50, 0) -- or set its speedMaterials and mass
A part weighs its volume times its material's density, and its material sets how it grips and bounces:
| Material | Density | Friction | Bounce |
|---|---|---|---|
| Plastic, Neon | 0.7 | 0.5 | 0.1 |
| Smooth | 0.7 | 0.3 | 0.1 |
| Wood | 0.35 | 0.6 | 0.05 |
| Glass | 2.4 | 0.25 | 0.2 |
| Metal | 7.8 | 0.4 | 0.05 |
| Grass | 0.9 | 0.65 | 0.05 |
| Brick | 1.9 | 0.75 | 0.05 |
| Concrete | 2.4 | 0.7 | 0.03 |
| Sand | 1.6 | 0.85 | 0 |
| Rock | 2.7 | 0.65 | 0.05 |
| Water | 1.0 | 0.1 | 0 |
| Leaves | 0.3 | 0.6 | 0.02 |
So the same push moves a big Metal part much less than a small Wood one.
Collision groups
Every part is in one collision group (part.group, "Default" to start). Groups decide what passes through what:
local door = world.VipDoor
door.group = "VipDoor"
physics.collide("VipDoor", "Players", false) -- players walk through it; crates still bump into it
print(physics.collides("VipDoor", "Players")) -- false- Built in: Default, Players (every character) and Ragdoll. A world can name 13 of its own (up to 23 characters each).
physics.collide(a, b, false)witha == bmakes a group's parts pass through each other.touchedstill fires for parts that don't collide, so the VIP door can still tell who went through.
In KHIM Engine you can set the table without code: a part's Inspector > Behavior > CollisionGroup > Which groups collide... (or Insert > Collision groups) - a grid of ticks.
Welds
A weld holds two parts together as one. KHIM Engine keeps welds between parts built together, and a Model's clone() keeps them. Welded parts don't collide with each other.
Joints
A joint goes in the part that moves and names the part it is joined to (part1):
| Joint | Does | Like |
|---|---|---|
| Hinge | Turns about an axis | A door, a wheel, a lever |
| Slider | Slides along an axis | A lift, a drawer, a piston |
| BallJoint | Turns every way about a point | A lamp on a chain |
| Spring | Pulls toward a rest length | A car's suspension, a trampoline |
| Rope | Keeps at most a length apart | A swing, a crane's hook |
In KHIM Engine: select the part it joins to, Ctrl-click the part that moves, then insert the joint (Insert Object > Joints). The Inspector sets the other part, the anchor, the axis, limits and the motor.
Members
| Member | |
|---|---|
part1 | The part it is joined to |
anchor, axis | vec3s in its own part's space |
enabled | On or off |
limited, lower, upper | Hinge: degrees; Slider: units |
motor, speed, force | Drive it: degrees or units a second, with up to force |
angle (Hinge), offset (Slider) | How far it has turned / slid (read-only) |
length, stiffness, damping | Spring and Rope (length for both) |
-- A spinning platform on its post (the platform not anchored: joints move only loose parts)
local spinner = new("Hinge", world.Platform)
spinner.part1 = world.Post
spinner.motor = true
spinner.speed = 45 -- degrees a second
spinner.force = 100000
-- A gate made in KHIM Engine: limited 0 to 90, opened by a button
local gate = world.Gate.Hinge
world.Button.Clickable.clicked:connect(function()
gate.motor = true
gate.speed = 60 -- swings to its 90-degree stop and holds there
end)Joints work on the server. A ClientScript can read them but moves nothing with them.
Movers
Put these on a loose part to push it:
| Mover | Does | Members |
|---|---|---|
| Mover | Drives it at a speed, holding it against gravity too: a moving platform, a drone | velocity, strength, relative |
| Spinner | Spins it: a fan, a rolling wheel | spin (radians a second about each axis), strength, relative |
| Thruster | Pushes with a steady force: a rocket, wind | force, relative |
local drone = new("Mover", world.Drone)
drone.velocity = vec3(0, 0, -10) -- flies level at 10 units a secondrelative = true makes the vector turn with the part (a Thruster's default: a rocket pushes the way it points).
Moving parts with no physics
For platforms and doors that should follow an exact path, a Motion (no code) or a tween is simpler than physics:
world.Lift:tween({position = world.Lift.position + vec3(0, 20, 0)}, {time = 4, reverses = true, repeats = -1})Characters standing on a moving part - tweened, on a joint, spinning - ride along with it.
Raycasts
local hit = world:raycast(origin, direction) -- the first part along the line, or nil
if hit then print(hit.part, hit.position, hit.normal, hit.distance) enddirection's length is how far it looks.