Grip: character anchoring
Two virtual hands attach a Character to a Static or Kinematic surface.
First use#
Grip moves the character relative to hand anchors; Grabber / Carry instead holds an object in front of the character. One Character supports one Grip controller. Dynamic anchor bodies are not supported.
wall.set_grip_enabled(true);
grip = jolt_grip(player);
grip.set_camera(o_control_bbmod_camera);
grip.set_distances(0.65, 1.5, 2);
// End Step, after the camera update:
grip.update_controls();
// Clean Up:
// grip.destroy();player must be a live JoltCharacter. The camera exposes camera.Position, camera.Target and mouse_locked. Left/right mouse buttons control the corresponding hands by default. Use acquire or update_hand for custom input; do not process automatic and manual input twice.
Distances and movement#
| Setting | Meaning and range |
|---|---|
| set_distances(working, reach, break, hand=JOLT_GRIP_BOTH) | Metres: desired surface distance, acquisition reach, release threshold. 0.05 ≤ working ≤ reach ≤ break ≤ 100. Defaults 0.65, 1.5, 2. |
| set_motion(max_speed, response) | Speed (0,30] m/s and response (0,60] 1/s. Defaults 3 and 8. |
| set_body_movement_enabled(enabled) | Enables body offset movement. Default false. |
| set_body_offset_limit(distance) | 0–100 m; default 0.75. |
| set_body_move_speed(speed) / set_body_move_response(response) | (0,30] m/s and (0,60] 1/s; defaults 1 and 10. |
| set_body_return_enabled(enabled) / set_body_return_speed(speed) | Returns the offset after releasing movement keys. Defaults false and 1 m/s. With false, the offset persists. |
| set_move_input(x,y) | Abstract axes from −1 to 1, integrated at the fixed physics step. |
| set_view_angles(yaw,pitch) | Degrees: yaw 0 = +X, 90 = +Y; positive pitch looks up. |
| set_view_basis(fx,fy,fz,rx,ry,rz) | World forward/right vectors; must form a nonzero independent basis. |
Hands and prospective target#
JOLT_GRIP_LEFT = 0, JOLT_GRIP_RIGHT = 1; BOTH = −1 is only accepted by operations supporting both hands. get_attached(hand) returns Boolean. get_position, get_rotation, get_world_normal and other attached values return undefined while detached. Positions are world {x,y,z}, rotations are quaternions {x,y,z,w}. Rotation +Z follows the surface normal; +X follows camera right at acquisition.
var hand = grip.get_hand(JOLT_GRIP_LEFT);
if (is_struct(hand) && hand.attached) {
hand_model.Matrix = model_matrix_from_transform(
hand.world_position, hand.world_rotation);
}
var target = grip.get_target();
if (!is_undefined(target)) {
preview.Matrix = model_matrix_from_transform(target.position, target.rotation);
}get_target(hand=JOLT_GRIP_LEFT) performs one current camera raycast, returning {position,world_normal,rotation} or undefined. can_grip only tests eligibility; it does not acquire. Read get_hand or get_target once for several values; separate getters take separate snapshots/raycasts. Toggle model visibility in your renderer.
Moving anchors and limitations#
A Static anchor must not move after acquisition. Kinematic anchors update their local position, normal and rotation and supply point velocity. Teleporting, shape/motion changes, disabling grip_enabled, destroyed bodies, collision filters or exceeding break can release a hand. Dynamic Grip, hand reaction forces, stamina and IK are not implemented.
Diagnostics and lifetime#
get_status returns both hands and reasons; get_reason(hand) explains rejection or release. Following an update_hand rejection, release the input before retrying. release() releases both hands; destroy() ends the controller. Character destruction releases it. set_debug, set_debug_xray, set_debug_limits and set_debug_labels control diagnostics; draw_debug_gui is not public API.
Verified operation contracts#
These contracts distinguish units, return values, errors and lifetime according to the current implementation. Check results: false, 0 and undefined can mean different things for different methods.
| Symbol / owner | Signature | Operation contract |
|---|---|---|
| JoltBody.get_grip_enabled | JoltBody.get_grip_enabled() | Independent opt-in flags, default false. Setter Boolean/0/1 → Boolean; getter Boolean, also false on error. Grip now accepts Static and Kinematic, not Dynamic; grab_enabled does not bypass other Carry conditions. |
| JoltBody.set_grip_enabled | JoltBody.set_grip_enabled(enabled) | Independent opt-in flags, default false. Setter Boolean/0/1 → Boolean; getter Boolean, also false on error. Grip now accepts Static and Kinematic, not Dynamic; grab_enabled does not bypass other Carry conditions. |
| JoltGrip.acquire | JoltGrip.acquire(hand,ox,oy,oz,dx,dy,dz) | LEFT/RIGHT + world origin and direction. Boolean attempt; supports grip_enabled Static/Kinematic, not Dynamic. An attached hand retains its anchor. |
| JoltGrip.can_grip | JoltGrip.can_grip(hand=JOLT_GRIP_LEFT) | Boolean current raycast-target eligibility, default LEFT; LEFT/RIGHT only, not BOTH. Uses configured camera; the button need not be pressed. |
| JoltGrip.draw_debug | JoltGrip.draw_debug() | Draw with an active 3D camera; Boolean. Draws only when debug is enabled and controller valid; restores graphics state. Not Draw GUI. |
| JoltGrip.get_anchor_velocity | JoltGrip.get_anchor_velocity(hand) | World anchor velocity {x, y, z} m/s, undefined when detached. |
| JoltGrip.get_body | JoltGrip.get_body(hand) | Attached hand managed host, otherwise undefined; borrowed. |
| JoltGrip.get_distance | JoltGrip.get_distance(hand) | Current hand distance from the reference point in m, undefined when detached. |
| JoltGrip.get_hand | JoltGrip.get_hand(hand) | Independent current diagnostic snapshot (whole system or LEFT/RIGHT). get_hand retains reason after release; returned struct does not modify the live solver. undefined on error. |
| JoltGrip.get_local_normal | JoltGrip.get_local_normal(hand) | Host-local anchor {x, y, z}: position in m / unit normal. undefined when detached. |
| JoltGrip.get_local_position | JoltGrip.get_local_position(hand) | Host-local anchor {x, y, z}: position in m / unit normal. undefined when detached. |
| JoltGrip.get_motion_type | JoltGrip.get_motion_type(hand) | Host type static/kinematic, undefined when detached; moving_anchor Boolean, false when detached. |
| JoltGrip.get_moving_anchor | JoltGrip.get_moving_anchor(hand) | Host type static/kinematic, undefined when detached; moving_anchor Boolean, false when detached. |
| JoltGrip.get_reason | JoltGrip.get_reason(hand) | Hand diagnostic reason from snapshot, available after release; invalid hand returns undefined. |
| JoltGrip.get_status | JoltGrip.get_status() | Independent current diagnostic snapshot (whole system or LEFT/RIGHT). get_hand retains reason after release; returned struct does not modify the live solver. undefined on error. |
| JoltGrip.is_valid | JoltGrip.is_valid() | Boolean: controller is not destroyed and has a live Character; pending without a holder is false. |
| JoltGrip.set_camera | JoltGrip.set_camera(source) | Camera object / live instance / undefined; Boolean, replacement releases hands. Expects camera.Position/Target with X/Y/Z and mouse_locked. |
| JoltGrip.set_debug | JoltGrip.set_debug(enabled) | Stores a drawing flag and returns true; changes neither physics nor body grip_enabled. |
| JoltGrip.set_debug_body | JoltGrip.set_debug_body(body) | Live box JoltBody or undefined; Boolean. Optional diagnostic outline only, does not own the body. |
| JoltGrip.set_debug_labels | JoltGrip.set_debug_labels(enabled) | Stores a drawing flag and returns true; changes neither physics nor body grip_enabled. |
| JoltGrip.set_debug_limits | JoltGrip.set_debug_limits(enabled) | Stores a drawing flag and returns true; changes neither physics nor body grip_enabled. |
| JoltGrip.set_debug_xray | JoltGrip.set_debug_xray(enabled) | Stores a drawing flag and returns true; changes neither physics nor body grip_enabled. |
| JoltGrip.set_holder | JoltGrip.set_holder(holder) | Live JoltCharacter; Boolean. One controller per Character; replacement releases old hands and applies stored configuration. |
| JoltGrip.set_left_button | JoltGrip.set_left_button(button) | Integer 0..5, 0 disables the hand; Boolean. Changing the binding releases that hand. |
| JoltGrip.set_move_input | JoltGrip.set_move_input(x,y) | Finite XY -1..1, Boolean, requires live holder; camera-relative body offset with body movement enabled. |
| JoltGrip.set_right_button | JoltGrip.set_right_button(button) | Integer 0..5, 0 disables the hand; Boolean. Changing the binding releases that hand. |
| JoltGrip.set_view_angles | JoltGrip.set_view_angles(yaw,pitch) | Finite yaw/pitch degrees, Boolean. Yaw 0 is +X, 90 +Y, positive pitch up; frame for movement and acquisition orientation. |
| JoltGrip.set_view_basis | JoltGrip.set_view_basis(fx,fy,fz,rx,ry,rz) | Finite forward/right, nonzero independent vectors; Gram–Schmidt orthonormalization, Boolean. |
| JoltGrip.update_controls | JoltGrip.update_controls() | Once in End Step after camera; Boolean. Unlocked mouse or unfocused window releases hands and returns true; missing/invalid camera releases and returns false. |
| JoltGrip.update_hand | JoltGrip.update_hand(hand,down,ox,oy,oz,dx,dy,dz) | LEFT/RIGHT, Boolean down and ray. Attempts only on the press edge; holding after failure does not retry acquire. Boolean does not mean attached: check get_attached. |
Complete API declarations#
Declarations match the production GML sources. A value after = marks an optional argument. This is a signature inventory; the guide sections describe meaning, units and limitations. The type before a dot identifies the method owner, not a global function.
| Symbol / owner | Signature / value |
|---|---|
| jolt_grip | jolt_grip(character=undefined) |
| JoltGrip.destroy | JoltGrip.destroy(reason="grip_destroyed") |
| JoltGrip.get_attached | JoltGrip.get_attached(hand) |
| JoltGrip.get_position | JoltGrip.get_position(hand) |
| JoltGrip.get_rotation | JoltGrip.get_rotation(hand) |
| JoltGrip.get_target | JoltGrip.get_target(hand=JOLT_GRIP_LEFT) |
| JoltGrip.get_target_position | JoltGrip.get_target_position(hand=JOLT_GRIP_LEFT) |
| JoltGrip.get_target_rotation | JoltGrip.get_target_rotation(hand=JOLT_GRIP_LEFT) |
| JoltGrip.get_target_world_normal | JoltGrip.get_target_world_normal(hand=JOLT_GRIP_LEFT) |
| JoltGrip.get_world_normal | JoltGrip.get_world_normal(hand) |
| JoltGrip.release | JoltGrip.release(hand=JOLT_GRIP_BOTH) |