Skip to content

Commit 2bcb7ac

Browse files
Site changes
1 parent 0bf362c commit 2bcb7ac

431 files changed

Lines changed: 684 additions & 728 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 79 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
local BALL_START = vmath.vector3(650, 545, 0)
1+
local BALL_START_POSITION = vmath.vector3(650, 545, 0)
22
local BALL_VELOCITY = vmath.vector3(-140, 0, 0)
33
local CHAIN_COLOR = vmath.vector4(0.1, 0.8, 0.9, 1.0)
4-
local PREV_GHOST_VERTEX = vmath.vector3(715, 505, 0)
5-
local NEXT_GHOST_VERTEX = vmath.vector3(35, 235, 0)
4+
local PREV_GHOST_VERTEX = vmath.vector3(715, 505, 0) -- <1>
5+
local NEXT_GHOST_VERTEX = vmath.vector3(35, 235, 0) -- <2>
66

7-
local TERRAIN_VERTICES = {
7+
local TERRAIN_VERTICES = { -- <3>
88
vmath.vector3(650, 480, 0),
99
vmath.vector3(560, 455, 0),
1010
vmath.vector3(470, 395, 0),
@@ -14,135 +14,130 @@ local TERRAIN_VERTICES = {
1414
vmath.vector3(95, 265, 0),
1515
}
1616

17-
local function draw_line(from, to, color)
18-
msg.post("@render:", "draw_line", { start_point = from, end_point = to, color = color }) -- <1>
19-
end
20-
21-
local function get_major_version()
22-
local version = b2d.get_version()
23-
if type(version) == "table" then
24-
return version.major
25-
end
26-
return tonumber(string.match(version, "^(%d+)"))
27-
end
28-
29-
local function clear_fixtures(body)
30-
local fixtures = b2d.body.get_fixtures(body) -- <2>
31-
for i = #fixtures, 1, -1 do
32-
b2d.body.destroy_fixture(body, fixtures[i].index) -- <3>
33-
end
34-
end
35-
3617
local function create_chain(body)
37-
clear_fixtures(body)
38-
39-
return b2d.body.create_fixture(body, {
18+
return b2d.body.create_fixture(body, { -- <4>
4019
density = 0.0,
4120
friction = 0.65,
4221
restitution = 0.2,
4322
shape = {
44-
type = b2d.shape.SHAPE_TYPE_CHAIN,
23+
type = b2d.shape.SHAPE_TYPE_CHAIN, -- <5>
4524
vertices = TERRAIN_VERTICES,
4625
prev_vertex = PREV_GHOST_VERTEX,
4726
next_vertex = NEXT_GHOST_VERTEX,
4827
},
49-
}) -- <4>
28+
})
5029
end
5130

5231
local function delete_ball(self)
53-
if self.ball_id then
32+
if self.ball_id then -- <6>
5433
go.delete(self.ball_id)
34+
self.ball_id = nil
5535
end
5636
end
5737

5838
local function spawn_ball(self)
59-
delete_ball(self)
60-
self.ball_id = factory.create("#ball_factory", BALL_START) -- <5>
39+
delete_ball(self) -- <7>
6140

62-
local ball_body = b2d.get_body(msg.url(nil, self.ball_id, "collisionobject"))
63-
if b2d.body.set_active then
64-
b2d.body.set_active(ball_body, true) -- <6>
65-
end
66-
b2d.body.set_linear_velocity(ball_body, BALL_VELOCITY) -- <7>
67-
b2d.body.set_angular_velocity(ball_body, -4.0)
68-
end
41+
self.ball_id = factory.create("#ball_factory", BALL_START_POSITION) -- <8>
6942

70-
local function draw_chain()
71-
for i = 1, #TERRAIN_VERTICES - 1 do
72-
draw_line(TERRAIN_VERTICES[i], TERRAIN_VERTICES[i + 1], CHAIN_COLOR) -- <8>
43+
local ball_body = b2d.get_body(msg.url(nil, self.ball_id, "collisionobject")) -- <9>
44+
if b2d.body.set_active then -- <10>
45+
b2d.body.set_active(ball_body, true) -- <11>
7346
end
47+
b2d.body.set_linear_velocity(ball_body, BALL_VELOCITY) -- <12>
48+
b2d.body.set_angular_velocity(ball_body, -4.0) -- <13>
7449
end
7550

7651
local function start_reset_timer(self)
77-
self.reset_timer = timer.delay(2.5, true, function() spawn_ball(self) end) -- <9>
78-
end
79-
80-
local function cancel_reset_timer(self)
81-
if self.reset_timer then
52+
if self.reset_timer then -- <14>
8253
timer.cancel(self.reset_timer)
83-
self.reset_timer = nil
8454
end
55+
56+
self.reset_timer = timer.delay(2.5, true, function() -- <15>
57+
spawn_ball(self)
58+
end)
8559
end
8660

8761
function init(self)
88-
self.active = get_major_version() == 2 -- <10>
62+
local b2d_version = b2d.get_version() -- <16>
63+
self.active = b2d_version.major == 2 -- <17>
8964

90-
if not self.active then -- <11>
65+
if not self.active then -- <18>
9166
return
9267
end
9368

94-
msg.post(".", "acquire_input_focus")
95-
local terrain_body = b2d.get_body(msg.url(nil, "terrain", "collisionobject")) -- <12>
96-
self.chain = create_chain(terrain_body) -- <13>
97-
label.set_text("#label", "Box2D V2 chain\nClick or touch to reset")
69+
msg.post(".", "acquire_input_focus") -- <19>
70+
label.set_text("#label", "Box2D V2 chain terrain\nClick or touch to reset") -- <20>
71+
72+
local terrain_body = b2d.get_body(msg.url(nil, "terrain", "collisionobject")) -- <21>
73+
self.chain = create_chain(terrain_body) -- <22>
74+
9875
spawn_ball(self)
9976
start_reset_timer(self)
10077
end
10178

102-
function update(self, dt)
103-
if self.active then
104-
draw_chain()
105-
end
106-
end
79+
-------------------
80+
-- Input handling:
10781

10882
local TOUCH = hash("touch")
10983

11084
function on_input(self, action_id, action)
111-
if not self.active then
85+
if not self.active then -- <23>
11286
return
11387
end
11488

115-
if action_id == TOUCH and action.pressed then -- <14>
89+
if action_id == TOUCH and action.pressed then -- <24>
11690
spawn_ball(self)
117-
cancel_reset_timer(self) -- <15>
118-
start_reset_timer(self)
91+
start_reset_timer(self) -- <25>
11992
end
12093
end
12194

122-
function final(self)
123-
if not self.active then
124-
return
95+
-------------------
96+
-- Debug draw only:
97+
98+
local function draw_line(from, to, color)
99+
msg.post("@render:", "draw_line", { start_point = from, end_point = to, color = color }) -- <26>
100+
end
101+
102+
local function draw_chain()
103+
for i = 1, #TERRAIN_VERTICES - 1 do -- <27>
104+
draw_line(TERRAIN_VERTICES[i], TERRAIN_VERTICES[i + 1], CHAIN_COLOR)
125105
end
106+
end
126107

127-
delete_ball(self)
128-
cancel_reset_timer(self)
129-
msg.post(".", "release_input_focus")
108+
function update(self, dt)
109+
if self.active then
110+
draw_chain() -- <28>
111+
end
130112
end
131113

132114
--[[
133-
1. Draw each terrain segment through the render socket. The lines are transient, so the chain is redrawn every frame.
134-
2. Read the existing Box2D V2 fixtures from the placeholder body.
135-
3. Remove the placeholder fixture so only the runtime chain remains.
136-
4. Attach a V2 chain fixture to the terrain body with `b2d.body.create_fixture()` and `b2d.shape.SHAPE_TYPE_CHAIN`.
137-
5. Spawn one dynamic ball from the local factory at the start of the chain.
138-
6. Explicitly activate the spawned body on backends that expose body activation.
139-
7. Give the ball an initial velocity so it rolls across the terrain immediately.
140-
8. Draw the runtime terrain again each frame because the render line messages do not persist.
141-
9. Replay the ball automatically so the example stays active without input.
142-
10. Detect whether the running engine uses the Box2D V2 backend. The helper accepts both table and string version formats.
143-
11. Leave this script as a no-op when the project uses another backend.
144-
12. Get the Box2D body owned by the hidden `terrain` collision object in the collection.
145-
13. Build the V2 chain terrain.
146-
14. Clicks and taps reset the ball manually.
147-
15. Reset the repeating timer after manual input so the next automatic reset waits for a full interval.
148-
]]
115+
1. Defines the ghost vertex before the first chain vertex. Box2D uses it to calculate smoother contacts at the open start of the chain.
116+
2. Defines the ghost vertex after the last chain vertex. It helps avoid unwanted edge catching at the open end of the chain.
117+
3. Defines the terrain path. The same points are used for the Box2D chain shape and the debug line visualization.
118+
4. Creates a Box2D V2 fixture on the terrain body. In Box2D V2, a fixture attaches a collision shape and material properties to a body.
119+
5. Selects a chain shape. A chain is an open sequence of connected line segments, useful for terrain collision.
120+
6. Checks whether a previously spawned ball exists before deleting it.
121+
7. Removes the previous ball before spawning a new one, so the example only has one active ball at a time.
122+
8. Spawns a new ball from the factory at the configured start position.
123+
9. Gets the Box2D body from the spawned ball’s `collisionobject` component.
124+
10. Checks whether this Box2D V2 build exposes explicit body activation.
125+
11. Activates the spawned body through the Box2D V2 API when `set_active()` is available.
126+
12. Sets the ball’s linear velocity through the Box2D V2 body API.
127+
13. Sets the ball’s angular velocity through the Box2D V2 body API so it starts spinning.
128+
14. Cancels the previous reset timer before creating a new one. This prevents multiple repeating timers from running at the same time.
129+
15. Starts a repeating timer that respawns the ball, so the chain interaction keeps replaying without input.
130+
16. Reads the active Box2D backend version.
131+
17. Enables this example only when the project is running the Box2D V2 backend.
132+
18. Stops the script early when Box2D V2 is not active, because the example uses V2-specific API calls.
133+
19. Acquires input focus so this script can receive click or touch input.
134+
20. Updates the label with a short description and reset instruction.
135+
21. Gets the Box2D body from the `terrain` collision object placed in the collection.
136+
22. Adds the runtime chain fixture to the terrain body.
137+
23. Skips input handling if this script is inactive.
138+
24. Handles a click or touch press and uses it as a manual reset for the ball.
139+
25. Restarts the repeating timer after manual input, so the next automatic reset waits for a full interval.
140+
26. Draws one debug line through the render socket. These lines are temporary and must be sent every frame.
141+
27. Draws each segment of the terrain path as a debug line so the invisible chain shape is visible.
142+
28. If this script is active, draws the chain each frame.
143+
]]

0 commit comments

Comments
 (0)