1. Empty project and physics world
Start a blank GML project, import the runtime resources, and verify that one managed world starts and stops.
Stage 4 built an isolated project from local resources; it does not prove a manual .yymps import through the editor. For a first attempt, use a separate copy of the complete development project: create an empty rm_tutorial, make it the start room and place only o_tutorial. Do not place the demo controllers in this room. Keep the original project as the dependency source.
Where each file belongs#
Open o_tutorial and use Add Event for each row. Choose GML Code if asked. Paste the file contents into that event editor; do not create separate Script assets from these files. Keep Visible enabled and Persistent disabled; no sprite is needed.
| File | Add Event |
|---|---|
| Create_0.gml | Create |
| Step_0.gml | Step → Step |
| Step_2.gml | Step → End Step |
| Draw_0.gml | Draw → Draw |
| Draw_64.gml | Draw → Draw GUI |
| CleanUp_0.gml | Clean Up |
Steps#
- Create a blank GML project targeting Windows x64. This guide is checked against runtime 2023.11.1.160; it does not certify other targets.
- Import the JoltPhysics extension (including its supplied DLL), its current jolt_* scripts, and sh_jolt_debug_3d / sh_jolt_debug_primitive_batch shaders. The complete script bundle also references BBMOD types: import its matching BBMOD scripts/shaders/resources too. This guide needs no model files or BBMOD renderer instance. Do not copy demo controller objects or rooms.
- Continue with a blank project only if you already have a matching complete resource package from the maintainer. Installation describes the planned local-package workflow; that editor import has not been verified by the automated build. Without a package, use a separate copy of the full development project and an empty tutorial room as explained above. Copying only the DLL is insufficient.
- Create room rm_tutorial, size 1280×720, dark background, built-in room physics disabled, viewport cameras disabled. Create object o_tutorial with no sprite and put exactly one instance in the room. Make this the first room; leave o_tutorial non-persistent.
- Add the six events listed below to o_tutorial. Paste one complete block into each matching event. The code owns the only Jolt world; do not add another jolt_update controller.
Event order#
Create allocates resources. Step reads input and prepares the camera. End Step updates Grabber, calls the single jolt_update and refreshes the camera after simulation. Draw renders 3D debug; Draw GUI renders text. Clean Up releases helpers, Character, bodies and finally the world.
o_tutorial — Create#
game_set_speed(60, gamespeed_fps);
floor_body = undefined;
crate = undefined;
player = undefined;
carry = undefined;
frame_seconds = 1 / 60;
mouse_locked = false;
carry_mouse_input = false;
yaw = 90;
pitch = -17;
sensitivity = 0.12;
if (!jolt_start()) {
show_error(jolt_get_error_message(), true);
exit;
}
jolt_debug_enable(true);
jolt_debug_set(0);
jolt_debug_set(JOLT_DEBUG_SHAPES, true);
camera = {
Position: {X:8, Y:-10, Z:7},
Target: {X:0, Y:0, Z:1},
Up: {X:0, Y:0, Z:1}
};
refresh_camera = function() {
if (is_undefined(player)) return;
var _feet = player.get_position();
camera.Position = {X:_feet.x, Y:_feet.y, Z:_feet.z + 1.6};
camera.Target = {
X:camera.Position.X + dcos(yaw) * dcos(pitch),
Y:camera.Position.Y + dsin(yaw) * dcos(pitch),
Z:camera.Position.Z + dsin(pitch)
};
};
refresh_camera();
o_tutorial — Step#
frame_seconds = clamp(delta_time / 1000000, 0, 0.05);
o_tutorial — Step → End Step#
if (!is_undefined(carry)) carry.update_controls();
if (jolt_update(frame_seconds) < 0) {
show_error(jolt_get_error_message(), true);
exit;
}
refresh_camera();
o_tutorial — Draw#
var _world = matrix_get(matrix_world);
var _view = matrix_get(matrix_view);
var _projection = matrix_get(matrix_projection);
var _p = camera.Position;
var _t = camera.Target;
matrix_set(matrix_world, matrix_build_identity());
matrix_set(matrix_view, matrix_build_lookat(
_p.X, _p.Y, _p.Z, _t.X, _t.Y, _t.Z, 0, 0, 1));
matrix_set(matrix_projection, matrix_build_projection_perspective_fov(
70, room_width / room_height, 0.05, 100));
jolt_debug_draw();
matrix_set(matrix_world, _world);
matrix_set(matrix_view, _view);
matrix_set(matrix_projection, _projection);
o_tutorial — Draw → Draw GUI#
draw_set_colour(c_white);
draw_text(16, 16, "World: " + string(jolt_is_started()));
if (!is_undefined(crate)) {
draw_text(16, 40, "Crate Z: " + string_format(crate.get_position().z, 1, 2));
}
if (!is_undefined(player)) {
draw_text(16, 64, "Tab: mouse | WASD: move | Space: jump");
var _cx = display_get_gui_width() * 0.5;
var _cy = display_get_gui_height() * 0.5;
draw_line(_cx - 5, _cy, _cx + 5, _cy);
draw_line(_cx, _cy - 5, _cx, _cy + 5);
}
if (!is_undefined(carry)) {
draw_text(16, 88, "LMB: grab/release | RMB: rotate | R: realign | F: throw");
draw_text(16, 112, "Holding: " + string(carry.is_holding()));
draw_text(16, 136, "Grab reason: " + carry.get_last_reason());
}
o_tutorial — Clean Up#
window_mouse_set_locked(false);
window_set_cursor(cr_default);
if (is_struct(carry)) carry.destroy();
if (is_struct(player)) player.destroy();
if (is_struct(crate)) crate.destroy();
if (is_struct(floor_body)) floor_body.destroy();
jolt_stop();
Try it step by step#
| Action in the game window | Expected result |
|---|---|
| Run the project with Run (F5). | The game window shows World: 1. An empty dark scene is correct in lesson 1; no body has been created yet. |
| Close the game window and run again. | The world starts again without an error dialog. End the previous run before editing code. |
This is a reader checklist, not a claim that every manual step has been successfully tested. See Example Verification for the actual verification scope.
Check the result#
GUI must show World: 1. An empty world has no geometry to draw yet. Exiting must not produce an invalid-handle error.
Troubleshooting#
- Unknown jolt_* function or BBMOD type: an imported resource is missing; this is not another object setting.
- World: 0 or a DLL error: check the Windows x64 target and matching DLL using Installation.
- World: 1 but nothing visible: check the Draw event, both debug shaders, object visibility and disabled viewport cameras. Physics bodies do not create visual game models.
- Movement too fast: do not run a second jolt_update controller; convert delta_time from microseconds to seconds.
- Error when leaving the room: release Grabber before Character and the world last. Do not use a persistent controller here.
Example files#
Verification scope#
The isolated test project uses these exact files. Compilation and runtime check results are listed on Practical Example Verification. Automated tests do not replace manual assessment of mouse feel and scene appearance.