‹›
JoltPhysics

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.

GML
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#

SettingMeaning 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.

GML
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 / ownerSignatureOperation contract
JoltBody.get_grip_enabledJoltBody.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_enabledJoltBody.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.acquireJoltGrip.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_gripJoltGrip.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_debugJoltGrip.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_velocityJoltGrip.get_anchor_velocity(hand)World anchor velocity {x, y, z} m/s, undefined when detached.
JoltGrip.get_bodyJoltGrip.get_body(hand)Attached hand managed host, otherwise undefined; borrowed.
JoltGrip.get_distanceJoltGrip.get_distance(hand)Current hand distance from the reference point in m, undefined when detached.
JoltGrip.get_handJoltGrip.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_normalJoltGrip.get_local_normal(hand)Host-local anchor {x, y, z}: position in m / unit normal. undefined when detached.
JoltGrip.get_local_positionJoltGrip.get_local_position(hand)Host-local anchor {x, y, z}: position in m / unit normal. undefined when detached.
JoltGrip.get_motion_typeJoltGrip.get_motion_type(hand)Host type static/kinematic, undefined when detached; moving_anchor Boolean, false when detached.
JoltGrip.get_moving_anchorJoltGrip.get_moving_anchor(hand)Host type static/kinematic, undefined when detached; moving_anchor Boolean, false when detached.
JoltGrip.get_reasonJoltGrip.get_reason(hand)Hand diagnostic reason from snapshot, available after release; invalid hand returns undefined.
JoltGrip.get_statusJoltGrip.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_validJoltGrip.is_valid()Boolean: controller is not destroyed and has a live Character; pending without a holder is false.
JoltGrip.set_cameraJoltGrip.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_debugJoltGrip.set_debug(enabled)Stores a drawing flag and returns true; changes neither physics nor body grip_enabled.
JoltGrip.set_debug_bodyJoltGrip.set_debug_body(body)Live box JoltBody or undefined; Boolean. Optional diagnostic outline only, does not own the body.
JoltGrip.set_debug_labelsJoltGrip.set_debug_labels(enabled)Stores a drawing flag and returns true; changes neither physics nor body grip_enabled.
JoltGrip.set_debug_limitsJoltGrip.set_debug_limits(enabled)Stores a drawing flag and returns true; changes neither physics nor body grip_enabled.
JoltGrip.set_debug_xrayJoltGrip.set_debug_xray(enabled)Stores a drawing flag and returns true; changes neither physics nor body grip_enabled.
JoltGrip.set_holderJoltGrip.set_holder(holder)Live JoltCharacter; Boolean. One controller per Character; replacement releases old hands and applies stored configuration.
JoltGrip.set_left_buttonJoltGrip.set_left_button(button)Integer 0..5, 0 disables the hand; Boolean. Changing the binding releases that hand.
JoltGrip.set_move_inputJoltGrip.set_move_input(x,y)Finite XY -1..1, Boolean, requires live holder; camera-relative body offset with body movement enabled.
JoltGrip.set_right_buttonJoltGrip.set_right_button(button)Integer 0..5, 0 disables the hand; Boolean. Changing the binding releases that hand.
JoltGrip.set_view_anglesJoltGrip.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_basisJoltGrip.set_view_basis(fx,fy,fz,rx,ry,rz)Finite forward/right, nonzero independent vectors; Gram–Schmidt orthonormalization, Boolean.
JoltGrip.update_controlsJoltGrip.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_handJoltGrip.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.