oriverse-engine 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md CHANGED
@@ -1,30 +1,30 @@
1
- # Oriverse Artifact Bundle License
2
-
3
- > PLACEHOLDER — review with legal counsel before the first `npm publish`.
4
-
5
- Copyright (c) 2026 Oriverse. All rights reserved.
6
-
7
- This package contains a compiled, proprietary build of the Oriverse engine
8
- ("the Bundle"). No source code is included or licensed.
9
-
10
- Permission is hereby granted, free of charge and royalty-free, to any person
11
- obtaining a copy of the Bundle to:
12
-
13
- 1. Use the Bundle to run and serve games and applications, commercial or
14
- non-commercial.
15
- 2. Redistribute the Bundle **unmodified** (including via CDNs such as unpkg,
16
- bundlers, or by copying the `dist/` files alongside your game), provided this
17
- license file accompanies any standalone redistribution of the Bundle.
18
-
19
- The following are NOT permitted:
20
-
21
- - Modifying, decompiling, disassembling, or reverse-engineering the Bundle,
22
- except to the extent such restriction is prohibited by applicable law.
23
- - Redistributing modified versions of the Bundle.
24
- - Representing the Bundle as your own work or removing copyright notices.
25
-
26
- THE BUNDLE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
- LIABILITY ARISING FROM THE USE OF THE BUNDLE.
1
+ # Oriverse Artifact Bundle License
2
+
3
+ > PLACEHOLDER — review with legal counsel before the first `npm publish`.
4
+
5
+ Copyright (c) 2026 Oriverse. All rights reserved.
6
+
7
+ This package contains a compiled, proprietary build of the Oriverse engine
8
+ ("the Bundle"). No source code is included or licensed.
9
+
10
+ Permission is hereby granted, free of charge and royalty-free, to any person
11
+ obtaining a copy of the Bundle to:
12
+
13
+ 1. Use the Bundle to run and serve games and applications, commercial or
14
+ non-commercial.
15
+ 2. Redistribute the Bundle **unmodified** (including via CDNs such as unpkg,
16
+ bundlers, or by copying the `dist/` files alongside your game), provided this
17
+ license file accompanies any standalone redistribution of the Bundle.
18
+
19
+ The following are NOT permitted:
20
+
21
+ - Modifying, decompiling, disassembling, or reverse-engineering the Bundle,
22
+ except to the extent such restriction is prohibited by applicable law.
23
+ - Redistributing modified versions of the Bundle.
24
+ - Representing the Bundle as your own work or removing copyright notices.
25
+
26
+ THE BUNDLE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ LIABILITY ARISING FROM THE USE OF THE BUNDLE.
package/README.md CHANGED
@@ -2,13 +2,16 @@
2
2
 
3
3
  Prebuilt **Oriverse engine** bundle for serverless single-player games: a WebGPU
4
4
  wasm engine you use three.js-style from a CDN, with your whole game authored as
5
- **one `game.html`** containing inline [Weave](https://oriverse.io) source.
5
+ **one `game.html`** containing inline [Weave](https://www.oriverse.com) source.
6
6
 
7
7
  This package is the **compiled engine bundle only** (js glue + wasm + KTX texture
8
8
  transcoder + docs). It contains no engine source code and has zero npm dependencies.
9
9
  Everything runs client-side: no server, no SharedArrayBuffer, no workers, no
10
10
  COOP/COEP headers, no network calls after the engine files load.
11
11
 
12
+ Full platform (multiplayer, marketplace, in-app AI editor): https://www.oriverse.com
13
+ — agents start at https://www.oriverse.com/llms.txt.
14
+
12
15
  ## Quickstart (script-tag flow)
13
16
 
14
17
  Create a single `game.html`:
@@ -26,11 +29,11 @@ object Box { model = "cube" position = (0, 300, 100) size = (100, 100, 100) colo
26
29
  </script>
27
30
 
28
31
  <!-- Pinned engine version: bump @0.1.2 together on new releases -->
29
- <script src="https://unpkg.com/oriverse-engine@0.1.2/dist/ktx_lib.js"></script>
30
- <script src="https://unpkg.com/oriverse-engine@0.1.2/dist/ktx_bridge.js"></script>
32
+ <script src="https://unpkg.com/oriverse-engine@0.1.9/dist/ktx_lib.js"></script>
33
+ <script src="https://unpkg.com/oriverse-engine@0.1.9/dist/ktx_bridge.js"></script>
31
34
  <script type="module">
32
- import initOriverse from "https://unpkg.com/oriverse-engine@0.1.2/dist/oriverse_wgpu.js";
33
- await initOriverse({ module_or_path: "https://unpkg.com/oriverse-engine@0.1.2/dist/oriverse_wgpu_bg.wasm" });
35
+ import initOriverse from "https://unpkg.com/oriverse-engine@0.1.9/dist/oriverse_wgpu.js";
36
+ await initOriverse({ module_or_path: "https://unpkg.com/oriverse-engine@0.1.9/dist/oriverse_wgpu_bg.wasm" });
34
37
  </script>
35
38
  </body>
36
39
  </html>
@@ -46,8 +49,8 @@ The engine auto-boots from the `#oriverse-weave` tag. To boot programmatically
46
49
  instead, omit the tag and call the typed export once after init:
47
50
 
48
51
  ```js
49
- import initOriverse, { bootOriverseArtifact } from "https://unpkg.com/oriverse-engine@0.1.2/dist/oriverse_wgpu.js";
50
- await initOriverse({ module_or_path: "https://unpkg.com/oriverse-engine@0.1.2/dist/oriverse_wgpu_bg.wasm" });
52
+ import initOriverse, { bootOriverseArtifact } from "https://unpkg.com/oriverse-engine@0.1.9/dist/oriverse_wgpu.js";
53
+ await initOriverse({ module_or_path: "https://unpkg.com/oriverse-engine@0.1.9/dist/oriverse_wgpu_bg.wasm" });
51
54
  await bootOriverseArtifact(weaveSource);
52
55
  ```
53
56
 
@@ -66,7 +69,7 @@ point an agent at it, and say "make a game".
66
69
  - Content must be fully procedural: primitive models, `#mesh`, `#texture`,
67
70
  `#particles`. Market/network assets are unavailable and hard-error.
68
71
  - Weave language + API reference: `dist/weave_documentation.txt`
69
- (also at `https://unpkg.com/oriverse-engine@0.1.2/dist/weave_documentation.txt`).
72
+ (also at `https://unpkg.com/oriverse-engine@0.1.9/dist/weave_documentation.txt`).
70
73
  - Package/runtime onboarding: `dist/AGENT_ONBOARDING.md`.
71
74
  - Copy-paste prompt for AI coding agents: `dist/CODEX_PROMPT.md`.
72
75
 
@@ -53,15 +53,15 @@ object BlueCube {
53
53
  </script>
54
54
  <!-- ==== END OF EDITABLE BLOCK ==== -->
55
55
 
56
- <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.7/dist/ktx_lib.js"></script>
57
- <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.7/dist/ktx_bridge.js"></script>
56
+ <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.9/dist/ktx_lib.js"></script>
57
+ <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.9/dist/ktx_bridge.js"></script>
58
58
  <script type="module">
59
- import initOriverse from "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.7/dist/oriverse_wgpu.js";
59
+ import initOriverse from "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.9/dist/oriverse_wgpu.js";
60
60
  const setBoot = (t) => { const b = document.getElementById('oriverse-status-banner'); if (b) b.textContent = t; };
61
61
  (async () => {
62
62
  try {
63
63
  setBoot('starting engine…');
64
- await initOriverse({ module_or_path: "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.7/dist/oriverse_wgpu_bg.wasm" });
64
+ await initOriverse({ module_or_path: "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.9/dist/oriverse_wgpu_bg.wasm" });
65
65
  } catch (e) {
66
66
  setBoot('WASM init failed:\n' + ((e && e.stack) ? e.stack : String(e)));
67
67
  console.error('[wasm_boot] initOriverse failed:', e);
@@ -90,15 +90,15 @@ Rules — the engine hard-errors if you break the first two:
90
90
  - `color = (r, g, b)` uses 0-1 floats (or `#RRGGBB`). 0-255 values clamp to WHITE.
91
91
  - The game boots straight into play mode. WASD + mouse + Space jump already work via a
92
92
  default player controller; define `#class player ... #end` only for custom movement.
93
- - Give the world a ground object, a HUD (`screenCanvas`), a win or lose condition, and
94
- `event KeyboardDown[R] { RestartGame() }` to replay.
93
+ - Give the world a ground object, a HUD (`screenCanvas`), a win or lose condition, and a
94
+ Restart `button` whose `ClickButton` handler calls `RestartGame()` to replay.
95
95
 
96
96
  Authoritative references, served next to the engine (fetch if you can — the authoring
97
97
  guide contains three complete tested example games to pattern-match from):
98
98
 
99
- - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.7/dist/GAME_AUTHORING.md <- game patterns + 3 full example games
100
- - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.7/dist/weave_documentation.txt <- full language + API reference
101
- - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.7/dist/examples/coin_collector.weave (also dodge_blocks / target_range)
99
+ - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.9/dist/GAME_AUTHORING.md <- game patterns + 3 full example games
100
+ - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.9/dist/weave_documentation.txt <- full language + API reference
101
+ - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.9/dist/examples/coin_collector.weave (also dodge_blocks / target_range)
102
102
 
103
103
  ## Step 3: test
104
104
 
@@ -72,8 +72,8 @@ event OnSpawned { // main entry; runs once when the object spawns
72
72
  }
73
73
  }
74
74
  }
75
- event KeyboardDown[R] { // any player pressed R (use ...ByThisPlayer only in #class player)
76
- RestartGame()
75
+ event ClickButton { // any player clicked any UI button
76
+ if Event.ClickedButton == ResolveObjectByNameOnce("RestartBtn") { RestartGame() }
77
77
  }
78
78
  #end
79
79
  ```
@@ -206,7 +206,7 @@ The manager pattern (used by all three examples):
206
206
  - Keep `var GameOver = false` (or `RoundOver`) on the tagged manager.
207
207
  - Losing conditions write it cross-object: `NearestObjectWithTag(Tag.Manager).GameOver = true`.
208
208
  - The manager's `forever` loop shows the banner and freezes scoring when set.
209
- - `event KeyboardDown[R] { RestartGame() }` reloads the whole world (fresh state).
209
+ - Replay: a Restart `button` shown at game over; its `ClickButton` handler calls `RestartGame()`.
210
210
 
211
211
  ## 9. Procedural textures and meshes (quickstarts)
212
212
 
@@ -269,7 +269,7 @@ Collect 10 spinning coins; HUD counter; win banner + R to restart. Demonstrates:
269
269
  objects + runtime spawning, tags, proximity pickup, default player controller.
270
270
 
271
271
  ```text
272
- // Coin Collector — walk into the 10 spinning coins to collect them all. Press R to replay.
272
+ // Coin Collector — walk into the 10 spinning coins to collect them all. Click Restart to replay.
273
273
  // Demonstrates: saved objects + runtime spawning, tags, proximity pickup, HUD score text,
274
274
  // win + restart flow. Movement uses the engine's default player controller (no #class player).
275
275
 
@@ -299,6 +299,7 @@ event OnSpawned {
299
299
  }
300
300
  var scoreLabel = ResolveObjectByNameOnce("ScoreText")
301
301
  var midLabel = ResolveObjectByNameOnce("MidText")
302
+ var restartBtn = ResolveObjectByNameOnce("RestartBtn")
302
303
  forever {
303
304
  // Spawned coins register their tag on the next tick, so wait until the census
304
305
  // has seen at least one coin before treating "0 left" as a win.
@@ -306,7 +307,8 @@ event OnSpawned {
306
307
  var collected = TotalCoins - left
307
308
  scoreLabel.SetText("Coins: " + collected + " / " + TotalCoins)
308
309
  if left == 0 and collected == TotalCoins and SeenCoins {
309
- midLabel.SetText("YOU WIN! Press R to play again")
310
+ midLabel.SetText("YOU WIN!")
311
+ restartBtn.SetVisible(true)
310
312
  } else {
311
313
  midLabel.SetText("")
312
314
  }
@@ -315,8 +317,8 @@ event OnSpawned {
315
317
  }
316
318
  }
317
319
  }
318
- event KeyboardDown[R] {
319
- RestartGame()
320
+ event ClickButton {
321
+ if Event.ClickedButton == ResolveObjectByNameOnce("RestartBtn") { RestartGame() }
320
322
  }
321
323
  #end
322
324
 
@@ -338,6 +340,7 @@ display manager : GameManager { visible = false }
338
340
  screenCanvas HUD {
339
341
  textLabel ScoreText { offset = (20, 20) text = "Coins: 0 / 10" color = (1, 1, 0.85) }
340
342
  textLabel MidText { align = (center, center) text = "" color = (1, 1, 0.3) }
343
+ button RestartBtn { anchor = (center, center) offset = (0, 70) size = (220, 52) text = "Restart" corner_radius = 8 color = #B98A20 visible = false }
341
344
  }
342
345
  ```
343
346
 
@@ -348,7 +351,7 @@ Demonstrates: timed spawner, dynamic physics objects, cross-object state writes,
348
351
  lose + restart flow.
349
352
 
350
353
  ```text
351
- // Dodge the Falling Blocks — red cubes rain down; touching one ends the run. Press R to retry.
354
+ // Dodge the Falling Blocks — red cubes rain down; touching one ends the run. Click Restart to retry.
352
355
  // Demonstrates: a hidden manager object, timed spawning with Timer, dynamic physics objects,
353
356
  // cross-object variable writes (block -> manager), survival-seconds score, lose + restart flow.
354
357
  // Movement uses the engine's default player controller (no #class player).
@@ -380,9 +383,11 @@ event OnSpawned {
380
383
  var dropTmr = Timer()
381
384
  var scoreLabel = ResolveObjectByNameOnce("ScoreText")
382
385
  var midLabel = ResolveObjectByNameOnce("MidText")
386
+ var restartBtn = ResolveObjectByNameOnce("RestartBtn")
383
387
  forever {
384
388
  if GameOver {
385
- midLabel.SetText("GAME OVER — survived " + Score + " s. Press R to retry")
389
+ midLabel.SetText("GAME OVER — survived " + Score + " s")
390
+ restartBtn.SetVisible(true)
386
391
  continue
387
392
  }
388
393
  if secondTmr.IsDone() {
@@ -397,8 +402,8 @@ event OnSpawned {
397
402
  }
398
403
  }
399
404
  }
400
- event KeyboardDown[R] {
401
- RestartGame()
405
+ event ClickButton {
406
+ if Event.ClickedButton == ResolveObjectByNameOnce("RestartBtn") { RestartGame() }
402
407
  }
403
408
  #end
404
409
 
@@ -417,6 +422,7 @@ display manager : GameManager { visible = false }
417
422
  screenCanvas HUD {
418
423
  textLabel ScoreText { offset = (20, 20) text = "Survived: 0 s" color = (1, 1, 1) }
419
424
  textLabel MidText { align = (center, center) text = "" color = (1, 0.45, 0.35) }
425
+ button RestartBtn { anchor = (center, center) offset = (0, 70) size = (220, 52) text = "Restart" corner_radius = 8 color = #B03A2E visible = false }
420
426
  }
421
427
  ```
422
428
 
@@ -427,7 +433,7 @@ Click the floating spheres through the crosshair; 30-second round. Demonstrates:
427
433
  called cross-object, TeleportTo respawn, countdown timer.
428
434
 
429
435
  ```text
430
- // Target Range — aim at the floating spheres and left-click to score. 30-second round, R restarts.
436
+ // Target Range — aim at the floating spheres and left-click to score. 30-second round, click Restart to replay.
431
437
  // Demonstrates: a custom #class player (movement + jump + click-to-shoot with the crosshair ray),
432
438
  // user-defined funcs called cross-object, TeleportTo respawn, countdown round timer, HUD score.
433
439
 
@@ -474,6 +480,7 @@ event OnSpawned {
474
480
  secondTmr.StartCountDown(1)
475
481
  var scoreLabel = ResolveObjectByNameOnce("ScoreText")
476
482
  var midLabel = ResolveObjectByNameOnce("MidText")
483
+ var restartBtn = ResolveObjectByNameOnce("RestartBtn")
477
484
  forever {
478
485
  scoreLabel.SetText("Hits: " + Hits + " Time: " + TimeLeft)
479
486
  if RoundOver {
@@ -484,13 +491,14 @@ event OnSpawned {
484
491
  TimeLeft -= 1
485
492
  if TimeLeft <= 0 {
486
493
  RoundOver = true
487
- midLabel.SetText("TIME! Final score: " + Hits + " — press R to retry")
494
+ midLabel.SetText("TIME! Final score: " + Hits)
495
+ restartBtn.SetVisible(true)
488
496
  }
489
497
  }
490
498
  }
491
499
  }
492
- event KeyboardDown[R] {
493
- RestartGame()
500
+ event ClickButton {
501
+ if Event.ClickedButton == ResolveObjectByNameOnce("RestartBtn") { RestartGame() }
494
502
  }
495
503
  #end
496
504
 
@@ -508,5 +516,6 @@ display manager : GameManager { visible = false }
508
516
  screenCanvas HUD {
509
517
  textLabel ScoreText { offset = (20, 20) text = "Hits: 0 Time: 30" color = (1, 1, 0.85) }
510
518
  textLabel MidText { align = (center, center) text = "" color = (0.4, 1, 0.5) }
519
+ button RestartBtn { anchor = (center, center) offset = (0, 70) size = (220, 52) text = "Restart" corner_radius = 8 color = #2E8B57 visible = false }
511
520
  }
512
521
  ```
@@ -1,70 +1,73 @@
1
- // Coin Collector — walk into the 10 spinning coins to collect them all. Press R to replay.
2
- // Demonstrates: saved objects + runtime spawning, tags, proximity pickup, HUD score text,
3
- // win + restart flow. Movement uses the engine's default player controller (no #class player).
4
-
5
- #class Coin
6
- event OnPreSpawn {
7
- AddTag(Tag.Coin)
8
- }
9
- event OnSpawned {
10
- forever {
11
- RotateLeftInstantly(4)
12
- var p = NearestPlayer()
13
- if p.Exists() and SurfaceGapTo(p) < 70 {
14
- p.SpawnFloatingText("+1", Vector(1, 0.9, 0.2))
15
- Destroy()
16
- }
17
- }
18
- }
19
- #end
20
-
21
- #class GameManager
22
- var TotalCoins = 10
23
- var SeenCoins = false
24
- event OnSpawned {
25
- for i in 0..TotalCoins {
26
- var pos = Position(RandomNumber(-1400, 1400), RandomNumber(-1400, 1400), 80)
27
- SpawnObject(SavedObject.coin, pos)
28
- }
29
- var scoreLabel = ResolveObjectByNameOnce("ScoreText")
30
- var midLabel = ResolveObjectByNameOnce("MidText")
31
- forever {
32
- // Spawned coins register their tag on the next tick, so wait until the census
33
- // has seen at least one coin before treating "0 left" as a win.
34
- var left = GetAllObjectsWithTag(Tag.Coin).Count()
35
- var collected = TotalCoins - left
36
- scoreLabel.SetText("Coins: " + collected + " / " + TotalCoins)
37
- if left == 0 and collected == TotalCoins and SeenCoins {
38
- midLabel.SetText("YOU WIN! Press R to play again")
39
- } else {
40
- midLabel.SetText("")
41
- }
42
- if left > 0 {
43
- SeenCoins = true
44
- }
45
- }
46
- }
47
- event KeyboardDown[R] {
48
- RestartGame()
49
- }
50
- #end
51
-
52
- saved coin {
53
- display coin : Coin { model = "cylinder" size = (60, 60, 12) rotation = (90, 0, 0) color = (1, 0.85, 0.2) }
54
- }
55
-
56
- object Ground {
57
- model = "cube"
58
- position = (0, 0, -50)
59
- size = (4000, 4000, 100)
60
- color = (0.32, 0.5, 0.34)
61
- }
62
- object PillarA { position = (400, 400, 150) size = (100, 100, 300) color = (0.62, 0.58, 0.52) }
63
- object PillarB { position = (-600, 300, 150) size = (100, 100, 300) color = (0.62, 0.58, 0.52) }
64
- object PillarC { position = (200, -700, 150) size = (100, 100, 300) color = (0.62, 0.58, 0.52) }
65
- display manager : GameManager { visible = false }
66
-
67
- screenCanvas HUD {
68
- textLabel ScoreText { offset = (20, 20) text = "Coins: 0 / 10" color = (1, 1, 0.85) }
69
- textLabel MidText { align = (center, center) text = "" color = (1, 1, 0.3) }
70
- }
1
+ // Coin Collector — walk into the 10 spinning coins to collect them all. Click Restart to replay.
2
+ // Demonstrates: saved objects + runtime spawning, tags, proximity pickup, HUD score text,
3
+ // win + restart flow. Movement uses the engine's default player controller (no #class player).
4
+
5
+ #class Coin
6
+ event OnPreSpawn {
7
+ AddTag(Tag.Coin)
8
+ }
9
+ event OnSpawned {
10
+ forever {
11
+ RotateLeftInstantly(4)
12
+ var p = NearestPlayer()
13
+ if p.Exists() and SurfaceGapTo(p) < 70 {
14
+ p.SpawnFloatingText("+1", Vector(1, 0.9, 0.2))
15
+ Destroy()
16
+ }
17
+ }
18
+ }
19
+ #end
20
+
21
+ #class GameManager
22
+ var TotalCoins = 10
23
+ var SeenCoins = false
24
+ event OnSpawned {
25
+ for i in 0..TotalCoins {
26
+ var pos = Position(RandomNumber(-1400, 1400), RandomNumber(-1400, 1400), 80)
27
+ SpawnObject(SavedObject.coin, pos)
28
+ }
29
+ var scoreLabel = ResolveObjectByNameOnce("ScoreText")
30
+ var midLabel = ResolveObjectByNameOnce("MidText")
31
+ var restartBtn = ResolveObjectByNameOnce("RestartBtn")
32
+ forever {
33
+ // Spawned coins register their tag on the next tick, so wait until the census
34
+ // has seen at least one coin before treating "0 left" as a win.
35
+ var left = GetAllObjectsWithTag(Tag.Coin).Count()
36
+ var collected = TotalCoins - left
37
+ scoreLabel.SetText("Coins: " + collected + " / " + TotalCoins)
38
+ if left == 0 and collected == TotalCoins and SeenCoins {
39
+ midLabel.SetText("YOU WIN!")
40
+ restartBtn.SetVisible(true)
41
+ } else {
42
+ midLabel.SetText("")
43
+ }
44
+ if left > 0 {
45
+ SeenCoins = true
46
+ }
47
+ }
48
+ }
49
+ event ClickButton {
50
+ if Event.ClickedButton == ResolveObjectByNameOnce("RestartBtn") { RestartGame() }
51
+ }
52
+ #end
53
+
54
+ saved coin {
55
+ display coin : Coin { model = "cylinder" size = (60, 60, 12) rotation = (90, 0, 0) color = (1, 0.85, 0.2) }
56
+ }
57
+
58
+ object Ground {
59
+ model = "cube"
60
+ position = (0, 0, -50)
61
+ size = (4000, 4000, 100)
62
+ color = (0.32, 0.5, 0.34)
63
+ }
64
+ object PillarA { position = (400, 400, 150) size = (100, 100, 300) color = (0.62, 0.58, 0.52) }
65
+ object PillarB { position = (-600, 300, 150) size = (100, 100, 300) color = (0.62, 0.58, 0.52) }
66
+ object PillarC { position = (200, -700, 150) size = (100, 100, 300) color = (0.62, 0.58, 0.52) }
67
+ display manager : GameManager { visible = false }
68
+
69
+ screenCanvas HUD {
70
+ textLabel ScoreText { offset = (20, 20) text = "Coins: 0 / 10" color = (1, 1, 0.85) }
71
+ textLabel MidText { align = (center, center) text = "" color = (1, 1, 0.3) }
72
+ button RestartBtn { anchor = (center, center) offset = (0, 70) size = (220, 52) text = "Restart" corner_radius = 8 color = #B98A20 visible = false }
73
+ }
@@ -1,70 +1,73 @@
1
- // Dodge the Falling Blocks — red cubes rain down; touching one ends the run. Press R to retry.
2
- // Demonstrates: a hidden manager object, timed spawning with Timer, dynamic physics objects,
3
- // cross-object variable writes (block -> manager), survival-seconds score, lose + restart flow.
4
- // Movement uses the engine's default player controller (no #class player).
5
-
6
- #class FallingBlock
7
- var LifeTmr : Timer
8
- event OnSpawned {
9
- LifeTmr.StartCountDown(8) // despawn landed blocks so the arena stays playable
10
- forever {
11
- if LifeTmr.IsDone() {
12
- Destroy()
13
- }
14
- var p = NearestPlayer()
15
- if p.Exists() and SurfaceGapTo(p) < 5 {
16
- NearestObjectWithTag(Tag.Manager).GameOver = true
17
- }
18
- }
19
- }
20
- #end
21
-
22
- #class GameManager
23
- var GameOver = false
24
- var Score = 0
25
- event OnPreSpawn {
26
- AddTag(Tag.Manager)
27
- }
28
- event OnSpawned {
29
- var secondTmr = Timer()
30
- var dropTmr = Timer()
31
- var scoreLabel = ResolveObjectByNameOnce("ScoreText")
32
- var midLabel = ResolveObjectByNameOnce("MidText")
33
- forever {
34
- if GameOver {
35
- midLabel.SetText("GAME OVER — survived " + Score + " s. Press R to retry")
36
- continue
37
- }
38
- if secondTmr.IsDone() {
39
- secondTmr.StartCountDown(1)
40
- Score += 1
41
- scoreLabel.SetText("Survived: " + Score + " s")
42
- }
43
- if dropTmr.IsDone() {
44
- dropTmr.StartCountDown(0.35)
45
- var pos = Position(RandomNumber(-900, 900), RandomNumber(-900, 900), 1300)
46
- SpawnObject(SavedObject.block, pos)
47
- }
48
- }
49
- }
50
- event KeyboardDown[R] {
51
- RestartGame()
52
- }
53
- #end
54
-
55
- saved block {
56
- physics block : FallingBlock { model = "cube" size = (120, 120, 120) color = (0.85, 0.25, 0.2) }
57
- }
58
-
59
- object Ground {
60
- model = "cube"
61
- position = (0, 0, -50)
62
- size = (2200, 2200, 100)
63
- color = (0.4, 0.42, 0.5)
64
- }
65
- display manager : GameManager { visible = false }
66
-
67
- screenCanvas HUD {
68
- textLabel ScoreText { offset = (20, 20) text = "Survived: 0 s" color = (1, 1, 1) }
69
- textLabel MidText { align = (center, center) text = "" color = (1, 0.45, 0.35) }
70
- }
1
+ // Dodge the Falling Blocks — red cubes rain down; touching one ends the run. Click Restart to retry.
2
+ // Demonstrates: a hidden manager object, timed spawning with Timer, dynamic physics objects,
3
+ // cross-object variable writes (block -> manager), survival-seconds score, lose + restart flow.
4
+ // Movement uses the engine's default player controller (no #class player).
5
+
6
+ #class FallingBlock
7
+ var LifeTmr : Timer
8
+ event OnSpawned {
9
+ LifeTmr.StartCountDown(8) // despawn landed blocks so the arena stays playable
10
+ forever {
11
+ if LifeTmr.IsDone() {
12
+ Destroy()
13
+ }
14
+ var p = NearestPlayer()
15
+ if p.Exists() and SurfaceGapTo(p) < 5 {
16
+ NearestObjectWithTag(Tag.Manager).GameOver = true
17
+ }
18
+ }
19
+ }
20
+ #end
21
+
22
+ #class GameManager
23
+ var GameOver = false
24
+ var Score = 0
25
+ event OnPreSpawn {
26
+ AddTag(Tag.Manager)
27
+ }
28
+ event OnSpawned {
29
+ var secondTmr = Timer()
30
+ var dropTmr = Timer()
31
+ var scoreLabel = ResolveObjectByNameOnce("ScoreText")
32
+ var midLabel = ResolveObjectByNameOnce("MidText")
33
+ var restartBtn = ResolveObjectByNameOnce("RestartBtn")
34
+ forever {
35
+ if GameOver {
36
+ midLabel.SetText("GAME OVER — survived " + Score + " s")
37
+ restartBtn.SetVisible(true)
38
+ continue
39
+ }
40
+ if secondTmr.IsDone() {
41
+ secondTmr.StartCountDown(1)
42
+ Score += 1
43
+ scoreLabel.SetText("Survived: " + Score + " s")
44
+ }
45
+ if dropTmr.IsDone() {
46
+ dropTmr.StartCountDown(0.35)
47
+ var pos = Position(RandomNumber(-900, 900), RandomNumber(-900, 900), 1300)
48
+ SpawnObject(SavedObject.block, pos)
49
+ }
50
+ }
51
+ }
52
+ event ClickButton {
53
+ if Event.ClickedButton == ResolveObjectByNameOnce("RestartBtn") { RestartGame() }
54
+ }
55
+ #end
56
+
57
+ saved block {
58
+ physics block : FallingBlock { model = "cube" size = (120, 120, 120) color = (0.85, 0.25, 0.2) }
59
+ }
60
+
61
+ object Ground {
62
+ model = "cube"
63
+ position = (0, 0, -50)
64
+ size = (2200, 2200, 100)
65
+ color = (0.4, 0.42, 0.5)
66
+ }
67
+ display manager : GameManager { visible = false }
68
+
69
+ screenCanvas HUD {
70
+ textLabel ScoreText { offset = (20, 20) text = "Survived: 0 s" color = (1, 1, 1) }
71
+ textLabel MidText { align = (center, center) text = "" color = (1, 0.45, 0.35) }
72
+ button RestartBtn { anchor = (center, center) offset = (0, 70) size = (220, 52) text = "Restart" corner_radius = 8 color = #B03A2E visible = false }
73
+ }