mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 17:06:04 +00:00
109 lines
3.2 KiB
Text
109 lines
3.2 KiB
Text
|
|
// Loop / Break / Continue — demonstrates `loop`, `break`, and
|
||
|
|
// `continue` via a linear scan that finds the first "hazard" the
|
||
|
|
// player is overlapping.
|
||
|
|
//
|
||
|
|
// NEScript's sprite allocator assigns a static OAM slot per `draw`
|
||
|
|
// statement at compile time, so a `draw` inside a loop body would
|
||
|
|
// reuse the same slot on every iteration. We instead use the loop
|
||
|
|
// to COMPUTE state — "is the player touching a hazard, and which
|
||
|
|
// one?" — and then fold that result into how (and whether) the
|
||
|
|
// single player sprite is drawn. The hazards themselves get one
|
||
|
|
// unrolled `draw` per slot so each hazard gets its own OAM entry.
|
||
|
|
//
|
||
|
|
// Features shown:
|
||
|
|
// - `loop { ... }` as an infinite loop
|
||
|
|
// - `break` to exit on first match
|
||
|
|
// - `continue` to skip inactive slots
|
||
|
|
// - array indexing with a loop-carried variable
|
||
|
|
//
|
||
|
|
// Build: cargo run -- build examples/loop_break_continue.ne
|
||
|
|
|
||
|
|
game "Loop Demo" {
|
||
|
|
mapper: NROM
|
||
|
|
}
|
||
|
|
|
||
|
|
const NUM_HAZARDS: u8 = 4
|
||
|
|
const HIT_RADIUS: u8 = 12
|
||
|
|
|
||
|
|
// Four hazard slots. `active` is 1 if the slot is live.
|
||
|
|
var active: u8[4] = [1, 1, 0, 1]
|
||
|
|
var hazard_x: u8[4] = [40, 96, 160, 200]
|
||
|
|
var hazard_y: u8[4] = [60, 140, 100, 180]
|
||
|
|
|
||
|
|
var px: u8 = 120
|
||
|
|
var py: u8 = 120
|
||
|
|
|
||
|
|
// Search results, written by the loop each frame.
|
||
|
|
var hit: u8 = 0 // 1 if the player is touching any hazard
|
||
|
|
var hit_idx: u8 = 0 // which slot was hit (only meaningful if hit == 1)
|
||
|
|
|
||
|
|
// Small absolute-difference helper. Saves lines at each call site.
|
||
|
|
inline fun abs_diff(a: u8, b: u8) -> u8 {
|
||
|
|
if a > b {
|
||
|
|
return a - b
|
||
|
|
}
|
||
|
|
return b - a
|
||
|
|
}
|
||
|
|
|
||
|
|
on frame {
|
||
|
|
// Player movement.
|
||
|
|
if button.right { px += 1 }
|
||
|
|
if button.left { px -= 1 }
|
||
|
|
if button.down { py += 1 }
|
||
|
|
if button.up { py -= 1 }
|
||
|
|
|
||
|
|
// Linear scan for the first active hazard the player is
|
||
|
|
// touching. The loop exits via `break` on the first hit, or
|
||
|
|
// by running i past the end.
|
||
|
|
hit = 0
|
||
|
|
var i: u8 = 0
|
||
|
|
loop {
|
||
|
|
if i >= NUM_HAZARDS {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
if active[i] == 0 {
|
||
|
|
// Skip inactive slots — this is where `continue`
|
||
|
|
// shines: the increment and re-test happen at the top.
|
||
|
|
i += 1
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
var dx: u8 = abs_diff(px, hazard_x[i])
|
||
|
|
var dy: u8 = abs_diff(py, hazard_y[i])
|
||
|
|
if dx < HIT_RADIUS {
|
||
|
|
if dy < HIT_RADIUS {
|
||
|
|
hit = 1
|
||
|
|
hit_idx = i
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
i += 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// One draw per hazard slot — unrolled so each sprite gets its
|
||
|
|
// own static OAM entry. Inactive slots draw off-screen ($FE)
|
||
|
|
// so they don't flicker the visible ones.
|
||
|
|
if active[0] == 1 {
|
||
|
|
draw Hazard at: (hazard_x[0], hazard_y[0])
|
||
|
|
}
|
||
|
|
if active[1] == 1 {
|
||
|
|
draw Hazard at: (hazard_x[1], hazard_y[1])
|
||
|
|
}
|
||
|
|
if active[2] == 1 {
|
||
|
|
draw Hazard at: (hazard_x[2], hazard_y[2])
|
||
|
|
}
|
||
|
|
if active[3] == 1 {
|
||
|
|
draw Hazard at: (hazard_x[3], hazard_y[3])
|
||
|
|
}
|
||
|
|
|
||
|
|
// The player sprite reflects the collision state: when hit,
|
||
|
|
// draw a "bang" marker above the player head. The `hit_idx`
|
||
|
|
// is unused visually but proves the loop ran to completion.
|
||
|
|
draw Player at: (px, py)
|
||
|
|
if hit == 1 {
|
||
|
|
draw Spark at: (px, py - 8)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
start Main
|