diff --git a/docs/GAMEPLAY_PROGRAMMING/external-process-languages/native-callback-setup.md b/docs/GAMEPLAY_PROGRAMMING/external-process-languages/native-callback-setup.md index e415f45..672f165 100644 --- a/docs/GAMEPLAY_PROGRAMMING/external-process-languages/native-callback-setup.md +++ b/docs/GAMEPLAY_PROGRAMMING/external-process-languages/native-callback-setup.md @@ -15,7 +15,7 @@ Each SDK handles omitted callbacks automatically. Supported names are `on_create`, `on_start`, `on_enable`, `fixed_update`, `update`, `on_disable`, `on_destroy`, and `on_stop`. Only frame callbacks take dt. Luau also accepts the capitalized Lua -aliases. External collision callbacks remain unavailable. API 1.1 adds shared +aliases. Collision subscriptions are available through `Physics.onCollisionEnter/Stay/Exit`; see [Contacts and slopes](/docs/api/physics-actions/contacts). API 1.1 adds shared `Events` to every supported script type, backed by the same scene services. See [Shared gameplay actions](Scripting/gameplayActions.md) for coverage and setup. Use `rustic.log` for game diagnostics and run scripts through Play. diff --git a/docs/GAMEPLAY_PROGRAMMING/lifecycle-execution-order-and-api-1-0.md b/docs/GAMEPLAY_PROGRAMMING/lifecycle-execution-order-and-api-1-0.md index cabadac..079cf7a 100644 --- a/docs/GAMEPLAY_PROGRAMMING/lifecycle-execution-order-and-api-1-0.md +++ b/docs/GAMEPLAY_PROGRAMMING/lifecycle-execution-order-and-api-1-0.md @@ -6,14 +6,12 @@ Global scripts are instantiated first when the game starts. Scene scripts are ne when their scene loads. Component scripts are instantiated with their object and are disabled/unregistered with it. Only callbacks actually supplied by a script are invoked. The canonical callbacks are `Start`, `Update`, `FixedUpdate`, -`OnCollisionEnter`, `OnCollisionStay`, `OnCollisionExit`, `OnEnable`, `OnDisable`, and -`OnDestroy`. Original snake-case callback names continue to work. +`OnEnable`, `OnDisable`, and `OnDestroy`. Original snake-case callback names continue to work. Collision notifications use explicit Physics event subscriptions. The basic Play simulator runs gravity and primitive box collision response after `FixedUpdate`, including in scenes with no scripts. `Anchored` prevents physics movement; `CanCollide` must be enabled on both objects for solid response. Collision -callback names are reserved: the simulator does not dispatch collision or touch -events. See `PHYSICS.md` for floor setup, falling objects, and current limitations. +subscriptions use `Physics.onCollisionEnter/Stay/Exit`, delivered during Update after resolution. Merely declaring an OnCollisionEnter lifecycle function does not subscribe. See [Contacts and slopes](/docs/api/physics-actions/contacts). Execution is deterministic: scope (global, scene, component), explicit execution order, entity Asset ID, script Asset ID, then attachment order. Scripts communicate @@ -40,7 +38,7 @@ The global `rustic` table exposes `entity_id`, `get_translation`, `set_translati `find_entity`, `get_attribute`/`GetAttribute`, `edit_attribute`/`EditAttribute`, `input`, `log`, `delta_time`, `fixed_delta_time`, `get_property`, `set_property`, and `set_enabled`. The built-in attributes are `Name`, `Position`, `Size`, `Color`, -`CanTouch`, `CanCollide`, `Anchored`, and `Parent`. +`CanTouch`, `CanCollide`, `Anchored`, `Friction`, and `Parent`. Lua also provides `print(...)` for an info Console entry and `warn(...)` for a warning Console entry. Use `rustic.log(level, message)` to choose a different level. diff --git a/docs/PHYSICS.md b/docs/PHYSICS.md index d70d40e..eaae960 100644 --- a/docs/PHYSICS.md +++ b/docs/PHYSICS.md @@ -1,4 +1,4 @@ -# Basic physics +# Physics: forces, friction, contacts and slopes Built-in primitives simulate automatically in Play, New Window, and Standalone. No script or physics component is required. Edit mode never moves objects. Gravity @@ -29,7 +29,8 @@ Rectangle2D or Circle2D above it. Gravity still acts along world Y. | `Anchored = false` | The primitive falls, including when collision is disabled. Unanchoring starts with zero velocity after an anchored tick. | | `CanCollide = true` | Solid response occurs only when both primitives have this flag enabled. Anchored primitives can be floors or walls. | | `CanCollide = false` | The object passes through other objects; it still falls if unanchored. | -| `CanTouch` | Required for object clicks and touchscreen taps; it does not change solid response or enable collision callbacks. See [Touchscreen input and object clicks](/docs/api/touch-input). | +| `CanTouch` | Enables collision subscriptions when both bodies allow touch, plus object clicks and taps. Does not change solid response. | +| `Friction` | Finite coefficient from 0 to 10; default 0.5. Zero is frictionless. Set it in the Inspector or through `EditAttribute`. The contact coefficient is the geometric mean of both values. | | `Size` | Local transform scale, multiplied by primitive dimensions for collision bounds. | These flags are read every fixed tick, after all `FixedUpdate` script callbacks. @@ -69,17 +70,52 @@ globalThis.behavior = { Attach this version as a JavaScript Object Component instead of the Lua script. Scene paths can also target other objects; see the **Edit scene objects** guide. +## Forces, friction and slopes + +Use the falling-box scene above. Create a JavaScript **Object Component** in +Programming, attach it to `Box` with **+ Add Component**, and replace its source: + +```javascript +globalThis.behavior = { + Start() { + rustic.EditAttribute("Friction", 0.6); + Physics.onCollisionEnter(rustic.entity_id(), (other, normal) => { + print("Contact", other, "normal", normal); + }); + Physics.onCollisionExit(rustic.entity_id(), other => print("Left", other)); + Physics.impulse(rustic.entity_id(), [2, 0, 0]); + }, + FixedUpdate(dt) { + // Unit mass: an upward force of 9.81 cancels gravity over this interval. + // Physics.force(rustic.entity_id(), [0, 9.81, 0], dt); + const support = Physics.ground(rustic.entity_id(), 45); + if (support && support.walkable) { + // normal is world-space; angle is degrees from world up. + // Use support.normal to project controller movement along the surface. + } + } +}; +``` + +Press Play. The box falls, emits one enter callback, and slows on the floor. +Rotate the anchored floor about Z to make a ramp. With zero friction on either +body the box slides down the slope; sufficiently high friction holds it. There +is no automatic character stair stepping or slope-angle movement clamp. +`Physics.ground` reads contacts from the most recent completed fixed step, so it +is null before the first physics tick and may lag a scripted teleport by a tick. + +`Physics.force` adds `force * scaledDelta` to velocity. Call it once per +`FixedUpdate`, passing `dt` in languages requiring an explicit interval. Impulse +adds velocity immediately; launch replaces it. Applying force from every rendered +frame with a fixed interval produces frame-rate-dependent acceleration. + ## Current limits - Only built-in primitives participate. Imported mesh assets, cameras, lights, folders, and empty nodes have no physics bodies. Runtime-created primitives join the next fixed tick; removed primitives lose their velocity state. -- Collision uses world-aligned boxes enclosing primitive dimensions, transformed - by rotation, scale, and the hierarchy. Spheres and circles behave as boxes, torus - holes are solid, and rotated slopes do not produce true slope sliding. Thin planes - and 2D shapes have a minimum collision half-thickness of 0.025 units. -- Bodies have equal mass and translate without rotating, bouncing, friction, - joints or configurable gravity. API 1.1 adds live primitive raycasts, sphere casts, overlaps, forces, impulses and velocity changes; see [Physics queries and forces](/docs/api/physics-actions). These APIs do not replace the box collision model. +- Collision uses oriented boxes enclosing primitive dimensions, transformed by rotation, scale, and the hierarchy. Rotated boxes and planes provide slope normals. Spheres and circles still behave as boxes and torus holes are solid. Sheared parent transforms fall back to world-aligned boxes. Planes and 2D shapes have a minimum collision half-thickness of 0.025 units. +- Bodies have equal unit mass and translate without rotating, bouncing, joints or configurable gravity. Coulomb friction reduces relative tangential motion and can hold bodies on shallow slopes. API 1.1 adds live primitive raycasts, sphere casts, overlaps, forces, impulses and velocity changes; see [Physics queries and forces](/docs/api/physics-actions). These APIs do not replace the box collision model. - Swept box checks reduce tunnelling during simulated falls. Script teleports and script-moved anchored platforms are not swept. Fast multi-body impacts and large stacks are approximate; use separated starting positions and modest scene sizes. @@ -90,8 +126,7 @@ Scene paths can also target other objects; see the **Edit scene objects** guide. - Child bodies receive world-space gravity and are converted back to local positions. Anchored children still follow their parent's transform. For predictable independent rigid bodies, place them at scene root or under an organizational folder. -- No `OnCollisionEnter`, `OnCollisionStay`, or `OnCollisionExit` events are dispatched - by this simulator, even if a language adapter accepts those callback names. +- Collision callbacks use explicit `Physics.onCollisionEnter`, `Physics.onCollisionStay`, and `Physics.onCollisionExit` subscriptions. Merely declaring lifecycle functions named `OnCollisionEnter` does not register a subscription. See [Contacts and slopes](/docs/api/physics-actions/contacts). ## Diagnose problems @@ -102,7 +137,7 @@ Scene paths can also target other objects; see the **Edit scene objects** guide. - **The box falls through:** enable Can collide on both the box and floor. Imported models need a separate built-in primitive as a collision proxy for now. - **Objects hover or collide too early:** check primitive dimensions and transform - scale. Rotated or curved shapes use enclosing boxes rather than their surface. + scale. Curved primitives use enclosing boxes; sheared transforms use world-aligned enclosing boxes. - **The object vanishes:** check the camera view and floor placement. Without a collidable floor, a body continues falling beyond the camera. - **A script release fails:** verify attachment and language, and read Console diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index edf1e44..f4cfd79 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -178,8 +178,7 @@ loop is timed out/restarted without editor failure; no project-language setting - Implement initial Rapier rigid/static/kinematic bodies, colliders, triggers, layers, raycasts, fixed-step synchronization, and debug draw behind `physics-api`. Basic Play gravity and enclosing-box primitive collision are already implemented - in `engine-play`; see `PHYSICS.md`. Full rigid-body rotation, triggers, queries, - collision callbacks, and imported-mesh physics remain future work. + in `engine-play`, along with forces, friction, raycasts, contact subscriptions and slope support queries; see `PHYSICS.md`. Full rigid-body rotation, triggers and imported-mesh physics remain future work. - Implement WAV/MP3/AAC/FLAC/OGG 2D/3D playback, source/listener, attenuation, loop, bus, volume, pitch, and streaming music behind `audio-api`. - Implement native runtime labels, buttons, images, panels, text input, basic flex/grid diff --git a/docs/Scripting/API/physics-actions.md b/docs/Scripting/API/physics-actions.md index d78edb5..600a8b8 100644 --- a/docs/Scripting/API/physics-actions.md +++ b/docs/Scripting/API/physics-actions.md @@ -4,3 +4,5 @@ Choose the physics operation you need. - [Physics queries](/docs/api/physics-actions/queries) — Find colliders with casts and overlaps. - [Physics forces](/docs/api/physics-actions/forces) — Apply forces, impulses and velocity changes. + +See [Collision callbacks and slope queries](/docs/api/physics-actions/contacts) for contact subscriptions, friction and ground support. diff --git a/docs/Scripting/API/physics-actions/contacts.md b/docs/Scripting/API/physics-actions/contacts.md new file mode 100644 index 0000000..a93109e --- /dev/null +++ b/docs/Scripting/API/physics-actions/contacts.md @@ -0,0 +1,100 @@ +# Collision callbacks and slope queries + +[Back to Physics APIs](/docs/api/physics-actions). + +## Setup + +Create an anchored, collidable Rectangular Prism floor and an unanchored Cube at +`[0, 5, 0]`. Enable **Can touch** on both. In Programming create an Object Component, +then select the cube and attach it using **+ Add Component**. Physics runs in Play, +New Window and Standalone; edit mode does not simulate. + +## Calls + +- `Physics.onCollisionEnter(entity, callback)` +- `Physics.onCollisionStay(entity, callback)` +- `Physics.onCollisionExit(entity, callback)` +- `Physics.ground(entity, maxAngle = 45)` +- `rustic.EditAttribute("Friction", coefficient)` and `rustic.GetAttribute("Friction")` + +Subscriptions return an Events connection; call `disconnect()` or +`Events.disconnect(connection)` to remove it. Owner disable, destruction, scene +unload and successful reload clean up subscriptions using the existing event rules. +Callbacks run during the next `Update` event dispatch, after fixed-step collision +resolution. Multiple fixed ticks can queue multiple stay notifications before a +rendered frame. Exceptions disable the behavior using the normal script error path. + +Each callback receives `(otherEntityId, normal)`. The ID is a stable string. The +normal is a three-component world vector pointing from the other body toward the +subscribed body. Exit reports the last contact normal; the other ID may refer to +an object that was removed. There is one enter/stay notification per touching pair +per fixed tick, mirrored to both bodies; substeps and solver iterations do not +duplicate notifications. Leaving contact, removing a collider, anchoring both bodies, +or disabling collision/touch produces exit on the next fixed tick. At least one +body must be dynamic, both must enable CanCollide, and both must enable CanTouch. +These are solid-contact callbacks; noncollidable triggers are not implemented. +Collision event names are reserved and cannot be emitted by scripts. + +`ground` returns null without an upward support contact, otherwise +`{entity, normal, angle, walkable}`. `angle` is degrees from world up; `walkable` +compares it with `maxAngle`, a finite number from 0 through 90. It chooses the +most upward-facing contact from the latest completed fixed step, independently +of CanTouch. It does not cast a ray, predict the next step, stop a body on a steep +slope, or implement stairs. Query after a fixed step has completed; startup has no +contacts yet. Friction and the solver produce sliding along rotated surfaces. + +## JavaScript example + +```javascript +globalThis.behavior = { + Start() { + rustic.EditAttribute("Friction", 0.5); + Physics.onCollisionEnter(rustic.entity_id(), (other, normal) => { + print("Landed on", other, normal); + }); + }, + Update() { + const support = Physics.ground(rustic.entity_id(), 45); + if (support && support.walkable) { + // Surface projection: tangent = movement - normal * dot(movement, normal). + } + } +}; +``` + +Press Play: one landing message appears, followed by stay events if subscribed. +Rotate the floor about Z to test ramps. Set friction to zero to observe sliding. +If no callback arrives, check attachment, Console errors, both flags on both +bodies, and that Update is running. If ground is null in Start, wait for a physics +tick. Merely defining `OnCollisionEnter` lifecycle functions does not subscribe. + +## Language bindings + +Lua 5.4 and Luau use `Physics.onCollisionEnter(id, function(other, normal) ... end)` +and `Physics.ground(id, 45)`; vectors are one-indexed arrays. JavaScript and HTML +inline scripts use the example above. Python uses the same method names and +`lambda other, normal: ...`. PHP uses `Physics::onCollisionEnter($id, $fn)` and +`Physics::ground($id, 45)`; a callback receives the two positional arguments. + +C++ uses `Physics::onCollisionEnter(id, [](const RusticValue::Array& args){ ... })` +and `Physics::ground(id, 45)`, which returns a RusticValue object or null. C# uses +`Physics.onCollisionEnter(id, args => { ... })` and `Physics.ground(id, 45)`, which +returns a JsonElement (test ValueKind for Null). Java uses the same calls with a +Consumer callback and returns an Object (null or Map). Native callback +arrays contain the other ID at index 0 and the normal at index 1. + +C uses `Physics.onCollisionEnter(id, fn)` where +`void fn(const RusticValue *args, size_t count)` receives those same two values. +`Physics.ground(id, 45)` returns `RusticGround` with `found`, `entity`, `normal`, +`angle` and `walkable`; check `found` before reading the other fields. C and Java +require an explicit maximum angle. The matching Stay/Exit methods use the same +callback signature in every language. + +The shared query protocol is +`{"op":"physics_ground","entity":"","max_angle":45}`. +The event subscription protocol is the existing `connect` gameplay request with +signal `{name:"CollisionEnter",source:""}` (or CollisionStay/Exit). +Friction accepts numeric values from 0 to 10 and rejects NaN/infinity. Old scenes +without a coefficient load with 0.5. These features use oriented box proxies; +curved primitives and meshes do not gain exact shape collision. Sheared transforms +fall back to enclosing AABBs. See [Basic physics](/docs/guides/physics). diff --git a/docs/Scripting/API/physics-actions/queries.md b/docs/Scripting/API/physics-actions/queries.md index 9bbc1a4..4a58fd4 100644 --- a/docs/Scripting/API/physics-actions/queries.md +++ b/docs/Scripting/API/physics-actions/queries.md @@ -39,7 +39,7 @@ globalThis.behavior = { ## Behavior and limitations - Attach to a Part above an anchored collidable floor. The query reports the nearest hit. -- Queries use primitive world AABBs including parent transforms. SphereCast uses rounded sphere/box contacts; model meshes do not automatically acquire primitive colliders. +- Raycasts and sphere casts use oriented primitive bounds including parent transforms; sphere casts retain rounded sphere/box contacts. Overlap and explosion candidate searches use conservative world AABBs. Sheared transforms use enclosing AABBs. Imported models require primitive collision proxies. - See [Basic physics](/docs/guides/physics) for collider setup, gravity and collision limits. - Check script attachment, enabled state and the Console when an operation fails. diff --git a/docs/Scripting/apiLanguages.md b/docs/Scripting/apiLanguages.md index 907323a..b620b9a 100644 --- a/docs/Scripting/apiLanguages.md +++ b/docs/Scripting/apiLanguages.md @@ -222,6 +222,4 @@ Find/List retain their existing snapshot semantics in external adapters. List re Keyboard input remains limited to held WASD, arrows and Shift in embedded Play. [Touchscreen input and object clicks](/docs/api/touch-input) provide multi-touch -input and CanTouch-gated click subscriptions in the embedded game view. Physics -collision contact callbacks are not dispatched. These are shared runtime limits -across languages. +input and CanTouch-gated click subscriptions in the embedded game view. Physics contact subscriptions and ground queries are available across languages; see [Contacts and slopes](/docs/api/physics-actions/contacts). diff --git a/docs/Scripting/gameplayActions/physics-queries.md b/docs/Scripting/gameplayActions/physics-queries.md index c9ae152..8cd0425 100644 --- a/docs/Scripting/gameplayActions/physics-queries.md +++ b/docs/Scripting/gameplayActions/physics-queries.md @@ -15,4 +15,4 @@ end } `sphereCast` adds a radius and uses rounded sphere/box contacts; `overlap(center, radius, ignore?)` returns entity IDs. Hits contain entity, point, normal and distance. Direction is normalized by the engine. Queries use the simulation's authored -primitive colliders represented by world AABBs, including parent transforms. +primitive colliders represented by oriented boxes, including parent transforms. Overlaps and explosion candidate searches use conservative world AABBs. diff --git a/docs/Scripting/sceneObjects.md b/docs/Scripting/sceneObjects.md index 667bbb5..8315795 100644 --- a/docs/Scripting/sceneObjects.md +++ b/docs/Scripting/sceneObjects.md @@ -116,8 +116,9 @@ See [APIs in every language](/docs/guides/api-languages) for native signatures. | `Position` | three finite numbers | Local translation relative to parent | | `Size` | three finite numbers | Local transform scale | | `Color` | three finite numbers | RGB; preserves existing alpha | -| `CanTouch` | boolean | Enables object clicks/taps; does not affect solid response | +| `CanTouch` | boolean | Enables clicks/taps and collision subscriptions when both bodies allow touch; does not affect solid response | | `CanCollide` | boolean | Enables solid box collisions with other collidable primitives in Play | +| `Friction` | number, 0..10 | Contact friction coefficient; defaults to 0.5 | | `Anchored` | boolean | Prevents physics movement; unchecked primitives fall under gravity | | `Parent` | stable entity ID or null | Reparents the target; changes future path | @@ -127,8 +128,7 @@ path string. Material and Enabled are not supported attributes; `set_enabled` still affects the script owner. Physics runs after fixed-update callbacks. Imported meshes do not yet simulate; -curved and rotated primitives use enclosing boxes. Physics does not dispatch -collision callbacks. See the **Basic physics** guide for setup and limitations. +curved primitives use oriented enclosing boxes. Rotated planes and boxes support slopes. Register collision callbacks through `Physics.onCollisionEnter/Stay/Exit`; see [Contacts and slopes](/docs/api/physics-actions/contacts). See the **Basic physics** guide for setup and limitations. ## Diagnose failures diff --git a/docs/Scripting/scriptingC/built-in-functions.md b/docs/Scripting/scriptingC/built-in-functions.md index 9291178..4d3f67d 100644 --- a/docs/Scripting/scriptingC/built-in-functions.md +++ b/docs/Scripting/scriptingC/built-in-functions.md @@ -24,6 +24,6 @@ instance creation, and camera selection. Use native member syntax for your langu Mutations apply after the callback, in call order. Getters read the callback's snapshot, so a getter after a setter still reads the original state. Properties and attributes must retain their engine types; numbers must be finite. Attribute names -are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, and +are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, `Friction`, and `Parent`. Color is three RGB numbers. Instance parents must be stable entity IDs; resolve a path with `Game.scene.Find` first. Scene List returns IDs, not path strings. diff --git a/docs/Scripting/scriptingCSharp/built-in-functions.md b/docs/Scripting/scriptingCSharp/built-in-functions.md index a9a12fe..a43be48 100644 --- a/docs/Scripting/scriptingCSharp/built-in-functions.md +++ b/docs/Scripting/scriptingCSharp/built-in-functions.md @@ -24,6 +24,6 @@ instance creation, and camera selection. Use native member syntax for your langu Mutations apply after the callback, in call order. Getters read the callback's snapshot, so a getter after a setter still reads the original state. Properties and attributes must retain their engine types; numbers must be finite. Attribute names -are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, and +are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, `Friction`, and `Parent`. Color is three RGB numbers. Instance parents must be stable entity IDs; resolve a path with `Game.scene.Find` first. Scene List returns IDs, not path strings. diff --git a/docs/Scripting/scriptingCpp/scene-and-object-operations.md b/docs/Scripting/scriptingCpp/scene-and-object-operations.md index 89d6d75..147fd8d 100644 --- a/docs/Scripting/scriptingCpp/scene-and-object-operations.md +++ b/docs/Scripting/scriptingCpp/scene-and-object-operations.md @@ -18,5 +18,5 @@ object names. The optional parent is an ID. These mutations are queued and appli after the callback, in call order, so creation does not return a new ID. Built-in attributes are `Name`, `Position`, `Size`, `Color`, `CanTouch`, -`CanCollide`, `Anchored`, and `Parent`. Property/attribute writes must retain the +`CanCollide`, `Anchored`, `Friction`, and `Parent`. Property/attribute writes must retain the existing engine type. A rejected command fails and disables only this behavior. diff --git a/docs/Scripting/scriptingJava/built-in-functions.md b/docs/Scripting/scriptingJava/built-in-functions.md index 2e59adb..f906417 100644 --- a/docs/Scripting/scriptingJava/built-in-functions.md +++ b/docs/Scripting/scriptingJava/built-in-functions.md @@ -24,6 +24,6 @@ instance creation, and camera selection. Use native member syntax for your langu Mutations apply after the callback, in call order. Getters read the callback's snapshot, so a getter after a setter still reads the original state. Properties and attributes must retain their engine types; numbers must be finite. Attribute names -are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, and +are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, `Friction`, and `Parent`. Color is three RGB numbers. Instance parents must be stable entity IDs; resolve a path with `Game.scene.Find` first. Scene List returns IDs, not path strings. diff --git a/docs/Scripting/scriptingJavaScript/api-reference.md b/docs/Scripting/scriptingJavaScript/api-reference.md index c93169f..48ff9a2 100644 --- a/docs/Scripting/scriptingJavaScript/api-reference.md +++ b/docs/Scripting/scriptingJavaScript/api-reference.md @@ -26,6 +26,6 @@ to select a level dynamically. Lower-case `get_attribute`/`edit_attribute` are equivalent. Set `Color`, `Position`, and `Size` with three-number arrays, and `Parent` with an entity ID string or `null`. Supported built-ins are -`Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, and +`Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, `Friction`, and `Parent`. Properties must already be declared and writes must preserve their type. Mutations are queued and applied in issue order after the callback. diff --git a/docs/Scripting/scriptingLua/script-api.md b/docs/Scripting/scriptingLua/script-api.md index b59d26d..56f4afc 100644 --- a/docs/Scripting/scriptingLua/script-api.md +++ b/docs/Scripting/scriptingLua/script-api.md @@ -28,7 +28,7 @@ needed. `get_attribute`/`GetAttribute` and `edit_attribute`/`EditAttribute` are aliases. Built-in attributes are `Name`, `Position`, `Size`, `Color`, `CanTouch`, -`CanCollide`, `Anchored`, and `Parent`. Attribute and property writes must match the +`CanCollide`, `Anchored`, `Friction`, and `Parent`. Attribute and property writes must match the existing value's type. Attribute setters accept scalar values, exactly three finite numbers in a table for `Position`, `Size`, and RGB `Color`, and a stable entity ID string or `nil` for `Parent`. Public-property setters retain their existing scalar diff --git a/docs/Scripting/scriptingLuau/built-in-functions.md b/docs/Scripting/scriptingLuau/built-in-functions.md index 954658c..cce25be 100644 --- a/docs/Scripting/scriptingLuau/built-in-functions.md +++ b/docs/Scripting/scriptingLuau/built-in-functions.md @@ -24,7 +24,7 @@ instance creation, and camera selection. Use native member syntax for your langu Mutations apply after the callback, in call order. Getters read the callback's snapshot, so a getter after a setter still reads the original state. Properties and attributes must retain their engine types; numbers must be finite. Attribute names -are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, and +are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, `Friction`, and `Parent`. Color is three RGB numbers. Instance parents must be stable entity IDs; resolve a path with `Game.scene.Find` first. Scene List returns IDs, not path strings. diff --git a/docs/Scripting/scriptingPHP/built-in-functions.md b/docs/Scripting/scriptingPHP/built-in-functions.md index feaeccc..538138f 100644 --- a/docs/Scripting/scriptingPHP/built-in-functions.md +++ b/docs/Scripting/scriptingPHP/built-in-functions.md @@ -24,6 +24,6 @@ instance creation, and camera selection. Use native member syntax for your langu Mutations apply after the callback, in call order. Getters read the callback's snapshot, so a getter after a setter still reads the original state. Properties and attributes must retain their engine types; numbers must be finite. Attribute names -are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, and +are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, `Friction`, and `Parent`. Color is three RGB numbers. Instance parents must be stable entity IDs; resolve a path with `Game.scene.Find` first. Scene List returns IDs, not path strings. diff --git a/docs/Scripting/scriptingPython/built-in-functions.md b/docs/Scripting/scriptingPython/built-in-functions.md index 8de6957..acfb3b3 100644 --- a/docs/Scripting/scriptingPython/built-in-functions.md +++ b/docs/Scripting/scriptingPython/built-in-functions.md @@ -24,6 +24,6 @@ instance creation, and camera selection. Use native member syntax for your langu Mutations apply after the callback, in call order. Getters read the callback's snapshot, so a getter after a setter still reads the original state. Properties and attributes must retain their engine types; numbers must be finite. Attribute names -are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, and +are `Name`, `Position`, `Size`, `Color`, `CanTouch`, `CanCollide`, `Anchored`, `Friction`, and `Parent`. Color is three RGB numbers. Instance parents must be stable entity IDs; resolve a path with `Game.scene.Find` first. Scene List returns IDs, not path strings. diff --git a/lib/api-docs.ts b/lib/api-docs.ts index de65a21..3f95629 100644 --- a/lib/api-docs.ts +++ b/lib/api-docs.ts @@ -24,7 +24,7 @@ const docs: Record = { PHP: "// Inside an on_start function with global $rustic:\nTween::move($rustic->entity_id(),[10,0,0],2,Ease::OutCubic);", "HTML / inline JS": "globalThis.behavior={Start(){\n Tween.move(rustic.entity_id(),[10,0,0],2,Ease.OutCubic);\n}};", }, - notes: ["Use the [canonical API paths](/docs/guides/api-paths) in all ten script types: scene receivers supply the target object, global calls use game.rustic, and file receivers supply the Explorer source. Existing table APIs remain compatible.", "Targets use local Position, Scale/Size, quaternion Rotation, Color, Opacity, perspective Fov in radians, or voice Volume/Pitch. Value callbacks animate script or UI state.", "Imported glTF/GLB/FBX clips retain source timing, inverse binds and skin weights. Physics uses primitive world AABBs and unit mass. Windows has device audio output; other platforms mix headlessly. See the guide for full signatures and backend limits.", "Operations and subscriptions clean up with their owner, scene, or reload. Failed reload retains previous operations. Callback exceptions disable the owner."] + notes: ["Use the [canonical API paths](/docs/guides/api-paths) in all ten script types: scene receivers supply the target object, global calls use game.rustic, and file receivers supply the Explorer source. Existing table APIs remain compatible.", "Targets use local Position, Scale/Size, quaternion Rotation, Color, Opacity, perspective Fov in radians, or voice Volume/Pitch. Value callbacks animate script or UI state.", "Imported glTF/GLB/FBX clips retain source timing, inverse binds and skin weights. Physics uses oriented primitive boxes, Coulomb friction and unit mass; overlap searches use conservative world AABBs. Windows has device audio output; other platforms mix headlessly. See the guide for full signatures and backend limits.", "Operations and subscriptions clean up with their owner, scene, or reload. Failed reload retains previous operations. Callback exceptions disable the owner."] }, entity: { title: "Entity & time API", summary: "Identify the behavior owner and read engine-supplied frame intervals.", @@ -86,7 +86,7 @@ const docs: Record = { title: "Built-in attribute API", summary: "Read or edit engine-owned attributes on the behavior entity or a named scene object.", when: "Use attributes for built-in entity state. Prefer `set_translation` for Position and `set_enabled` for Enabled when those dedicated calls better express intent.", calls: ["game.scene.Scene.Root.Child:EditAttribute(name, value) — native member syntax outside Lua/Luau", "rustic.get_attribute(name)", "rustic.edit_attribute(name, value)", "rustic.game.Scene.Root.Child.EditAttribute(name, value)", "rustic.edit_object_attribute(source, name, value)", "rustic.game.Scene.Root.Child:GetAttribute(name)", "rustic.get_object_attribute(source, name)"], - parameters: [p("source", "string", true, "Stable entity ID or full rustic.game.Scene.Root.Child path for object helpers."), p("name", "string", true, "Name, Position, Size, Color (RGB), CanTouch, CanCollide, Anchored, or Parent."), p("value", "attribute-specific", true, "Value matching the attribute type.")], + parameters: [p("source", "string", true, "Stable entity ID or full rustic.game.Scene.Root.Child path for object helpers."), p("name", "string", true, "Name, Position, Size, Color (RGB), CanTouch, CanCollide, Anchored, Friction (number, 0..10), or Parent."), p("value", "attribute-specific", true, "Value matching the attribute type.")], returns: "Get returns the current value. Edit validates a typed mutation; [write timing depends on the runtime](/docs/guides/scripting#when-mutations-take-effect).", examples: { "Lua 5.4": "local name = rustic.get_attribute('Name')\nrustic.edit_attribute('Color', {1, .5, 0})", @@ -99,7 +99,7 @@ const docs: Record = { Java: "String name = (String)rustic.get_attribute(\"Name\");\nrustic.edit_attribute(\"Color\",new double[]{1,.5,0});", PHP: "$name = $rustic->get_attribute(\"Name\");\n$rustic->edit_attribute(\"Color\",[1,.5,0]);", "HTML / inline JS": "const name = rustic.get_attribute('Name');\nrustic.edit_attribute('Color', [1, .5, 0]);", - }, notes: ["In Play, unanchored built-in primitives fall under gravity; CanCollide enables solid box response only when both objects enable it. Anchored prevents physics movement and clears velocity on the next fixed tick. CanTouch enables object clicks and touchscreen taps; it does not affect solids or enable physics collision callbacks. See [Touchscreen input and object clicks](/docs/api/touch-input). Imported meshes do not simulate yet. See the Basic physics guide for setup and limits. Unknown attributes and type changes are rejected. Parent accepts a stable entity ID or null. Target another object with rustic.game.Scene.Root.Child:EditAttribute in Lua, native member calls in JavaScript/Python/C#/PHP, C++ subscripts, or an edit_attribute protocol command containing source. See [Edit scene objects](/docs/guides/scene-objects) for setup, native calls in every language, and runtime limits.", protocolNote], + }, notes: ["In Play, unanchored built-in primitives fall under gravity; CanCollide enables solid box response only when both objects enable it. Anchored prevents physics movement and clears velocity on the next fixed tick. CanTouch enables object clicks, touchscreen taps and collision subscriptions when both bodies allow touch; it does not affect solid response. Friction defaults to 0.5 and accepts finite coefficients from 0 through 10. See [Touchscreen input and object clicks](/docs/api/touch-input). Imported meshes do not simulate yet. See the Basic physics guide for setup and limits. Unknown attributes and type changes are rejected. Parent accepts a stable entity ID or null. Target another object with rustic.game.Scene.Root.Child:EditAttribute in Lua, native member calls in JavaScript/Python/C#/PHP, C++ subscripts, or an edit_attribute protocol command containing source. See [Edit scene objects](/docs/guides/scene-objects) for setup, native calls in every language, and runtime limits.", protocolNote], }, input: { title: "Input API", summary: "Read held keys, touchscreen contacts and object clicks in the embedded Play viewport.", @@ -309,7 +309,7 @@ function callbacks() { const java = `class RusticBehavior extends Rustic {\n public static void main(String[] args) throws Exception {\n run((callback, dt) -> {\n switch (callback) {\n${nativeNames.map((name) => ` case "${name}" -> rustic.log("info", "${name}");`).join("\n")}\n }\n });\n }\n}`; const php = ` `function ${name}(${name.includes("update") ? "float $dt" : ""}): void {\n global $rustic;\n $rustic->log("info", "${name}");\n}`).join("\n")}\nrustic_run([\n${nativeNames.map((name) => ` "${name}" => "${name}",`).join("\n")}\n]);`; const examples: Record = {"Lua 5.4": lua, JavaScript: js, Python: python, "C++": cpp, "C#": csharp, Luau: lua, C: c, Java: java, PHP: php, "HTML / inline JS": js}; - return `# Lifecycle callbacks\n\nCallbacks are entry points invoked by Rustic. Define only those a behavior needs.\n\n## When each callback runs\n\n| Callback | When to use it | Variables |\n| --- | --- | --- |\n| OnCreate | One-time construction before startup. | None |\n| Start | Resolve references and initialize gameplay state. | None |\n| OnEnable | Resume when the owner becomes enabled. | None |\n| FixedUpdate | Physics and simulation. | dt: fixed-step seconds |\n| Update | Input and presentation. | dt: frame seconds |\n| OnDisable | Pause when the owner becomes disabled. | None |\n| OnDestroy | Release instance resources. | None |\n| OnStop | Final play-session cleanup. | None |\n\n## Examples in every language\n\nThese registration examples include every callback. Replace each body with the logic you need. Follow your language guide to generate the workspace, install its toolchain, attach the script and press Play. External dispatchers receive dt; it is meaningful for update and fixed_update. Physics runs after FixedUpdate, but physics collision contact callbacks are not dispatched. Touchscreen callbacks and object clicks use [Input subscriptions](/docs/api/touch-input). HTML examples go inside an inline script in a ui/ HTML asset.\n\n${languages.map((language) => `### ${language}\n\n\`\`\`${codeLanguage(language)}\n${examples[language]}\n\`\`\``).join("\n\n")}`; + return `# Lifecycle callbacks\n\nCallbacks are entry points invoked by Rustic. Define only those a behavior needs.\n\n## When each callback runs\n\n| Callback | When to use it | Variables |\n| --- | --- | --- |\n| OnCreate | One-time construction before startup. | None |\n| Start | Resolve references and initialize gameplay state. | None |\n| OnEnable | Resume when the owner becomes enabled. | None |\n| FixedUpdate | Physics and simulation. | dt: fixed-step seconds |\n| Update | Input and presentation. | dt: frame seconds |\n| OnDisable | Pause when the owner becomes disabled. | None |\n| OnDestroy | Release instance resources. | None |\n| OnStop | Final play-session cleanup. | None |\n\n## Examples in every language\n\nThese registration examples include every callback. Replace each body with the logic you need. Follow your language guide to generate the workspace, install its toolchain, attach the script and press Play. External dispatchers receive dt; it is meaningful for update and fixed_update. Physics runs after FixedUpdate, but collision callbacks use explicit [Physics subscriptions](/docs/api/physics-actions/contacts), dispatched during Update after fixed-step resolution. Touchscreen callbacks and object clicks use [Input subscriptions](/docs/api/touch-input). HTML examples go inside an inline script in a ui/ HTML asset.\n\n${languages.map((language) => `### ${language}\n\n\`\`\`${codeLanguage(language)}\n${examples[language]}\n\`\`\``).join("\n\n")}`; } for (const language of ["Python", "C", "C++", "C#", "Java"]) docs.logging.examples[language] += diff --git a/lib/docs-catalog.ts b/lib/docs-catalog.ts index a8913f2..554fb84 100644 --- a/lib/docs-catalog.ts +++ b/lib/docs-catalog.ts @@ -79,6 +79,7 @@ const categoryGroups: DocGroup[] = [ { slug: "api/camera-actions", title: "Camera actions", description: "Move, aim, zoom, follow and shake a game camera using shared actions.", source: "docs/Scripting/API/camera-actions.md" }, { slug: "api/physics-actions", title: "Physics queries and forces", description: "Query live primitive colliders and change simulated body velocity.", source: "docs/Scripting/API/physics-actions.md" }, { slug: "api/physics-actions/queries", title: "Physics queries", description: "Find primitive colliders with raycasts, sphere casts and overlaps.", source: "docs/Scripting/API/physics-actions/queries.md", parent: "api/physics-actions" }, + { slug: "api/physics-actions/contacts", title: "Contacts, friction and slopes", description: "Subscribe to collisions, configure friction and query ground support.", source: "docs/Scripting/API/physics-actions/contacts.md", parent: "api/physics-actions" }, { slug: "api/physics-actions/forces", title: "Physics forces", description: "Apply forces, impulses and velocity changes to simulated bodies.", source: "docs/Scripting/API/physics-actions/forces.md", parent: "api/physics-actions" }, { slug: "api/effects", title: "Effects", description: "Fade, flash, shake and pulse entity properties with shared easing.", source: "docs/Scripting/API/effects.md" }, { slug: "api/audio", title: "Audio", description: "Record audio, read microphone PCM and load reusable audio in every gameplay language; select Windows audio devices and play and fade WAV, MP3, AAC, FLAC, and OGG voices.", source: "docs/Scripting/API/audio.md" },