summaryrefslogtreecommitdiff
path: root/chapter31/docs
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-11-14 16:23:39 -0800
committerCliff Click <cliffc@acm.org>2026-11-14 16:23:39 -0800
commit7e66b7ae6b00a2e1652db32f01f0bf480508ef05 (patch)
treef09689493fd820544b4cd94d4b1e941d84a24b4b /chapter31/docs
parent3d0f3f38d9d8345b6508b3289dc94b0500c53d9e (diff)
downloadSimple-7e66b7ae6b00a2e1652db32f01f0bf480508ef05.tar.gz
Simple-7e66b7ae6b00a2e1652db32f01f0bf480508ef05.zip
CLeanup Breakoutmain
Fix a half-dozen compiler bugs; various kinds of forward refs and readable types. Allow read-only self-types; and read-only methods. Anonymous inner functions are treated as methods. Better error messages in many places.
Diffstat (limited to 'chapter31/docs')
-rw-r--r--chapter31/docs/examples/Breakout.smp375
1 files changed, 223 insertions, 152 deletions
diff --git a/chapter31/docs/examples/Breakout.smp b/chapter31/docs/examples/Breakout.smp
index e5757b45..b1d0b443 100644
--- a/chapter31/docs/examples/Breakout.smp
+++ b/chapter31/docs/examples/Breakout.smp
@@ -1,6 +1,6 @@
// Clear 24 x 8 bricks. Arrows or mouse move; Space/click serves; P pauses;
// R restarts; Q/Escape exits. Any argument runs ten frames as a smoke test.
-int WIDTH = 1008, HEIGHT = 760, COLS = 24, ROWS = 8;
+u32 WIDTH = 1008, HEIGHT = 760, COLS = 24, ROWS = 8;
int LEFT = 24, RIGHT = 984, TOP = 84, BOTTOM = 718;
int BRICK_TOP = 152, BRICK_DX = 40, BRICK_DY = 22, BRICK_W = 36, BRICK_H = 18;
int RADIUS = 5, PADDLE_HALF = 56, PADDLE_Y = 674;
@@ -17,18 +17,76 @@ struct Game {
int score = 0, remaining = COLS*ROWS, lives = 3, depth = 1, phase = READY;
bool boosted = false, backWall = false, paused = false;
f64 x = 504.0, y = 663.0, dx = 0.4, dy = -0.916515138991168;
- int soundDepth = 1;
- // Serve the ball; give it a nominal direction and speed
- val _serve = { ->
- if( phase == READY && !paused ) {
- phase = PLAY;
- dx = 0.4;
- dy = -_sqrt(1.0-dx*dx);
+ // Fixed 8 ms simulation, split further to travel at most two pixels per step.
+ // Even at maximum boosted speed a ball cannot tunnel through an 18 px brick.
+ val _tick = { int direction ->
+ if( paused || phase == WON || phase == LOST ) return 0;
+ _paddle(paddle+direction*6.4);
+ if( phase == READY ) { x = paddle; y = PADDLE_Y-RADIUS-6; return 0; }
+ f64 seconds = 0.008; // 8ms
+ while( seconds > 0.000000001 && phase == PLAY ) {
+ f64 dt = 2.0/speed;
+ if( dt > seconds ) dt = seconds;
+ seconds -= dt;
+ f64 oldX = x, oldY = y;
+ x += dx*speed*dt;
+ y += dy*speed*dt;
+ if( x < LEFT+RADIUS && dx < 0.0 ) {
+ x = 2.0*(LEFT+RADIUS)-x; dx = -dx; _cue(1);
+ }
+ if( x > RIGHT-RADIUS && dx > 0.0 ) {
+ x = 2.0*(RIGHT-RADIUS)-x; dx = -dx; _cue(1);
+ }
+ if( y < TOP+RADIUS && dy < 0.0 ) {
+ y = 2.0*(TOP+RADIUS)-y; dy = -dy;
+ backWall = true; _cue(1);
+ }
+ if( dy > 0.0 && oldY <= PADDLE_Y-RADIUS && y >= PADDLE_Y-RADIUS &&
+ paddle-PADDLE_HALF-RADIUS <= x <= paddle+PADDLE_HALF+RADIUS ) _rebound();
+ if( y > BOTTOM+RADIUS ) {
+ _lostBall();
+ break;
+ }
+ // Map the center into the grid of radius-expanded brick boxes.
+ // Their overlap is smaller than one cell: at most 2 x 2 candidates.
+ if( x <= LEFT+2 -RADIUS || x >= LEFT+2 +(COLS-1)*BRICK_DX+BRICK_W+RADIUS ||
+ y <= BRICK_TOP-RADIUS || y >= BRICK_TOP+(ROWS-1)*BRICK_DY+BRICK_H+RADIUS )
+ continue;
+ int col = (x-(LEFT+2 -RADIUS))/BRICK_DX;
+ int row = (y-(BRICK_TOP-RADIUS))/BRICK_DY;
+ bool hit = false;
+ for( int r = row > 0 ? row-1 : 0; r <= row && r < ROWS && !hit; r++ )
+ for( int c = col > 0 ? col-1 : 0; c <= col && c < COLS; c++ ) {
+ int i = r*COLS+c;
+ if( bricks[i] == 0 ) continue;
+ f64 left = LEFT+2 +c*BRICK_DX-RADIUS;
+ f64 top = BRICK_TOP+r*BRICK_DY-RADIUS;
+ f64 right = left+BRICK_W+2*RADIUS;
+ f64 bottom= top +BRICK_H+2*RADIUS;
+ if( left < x < right && top < y < bottom ) {
+ bool back = false;
+ // Resolve the face crossed this step, using the previous center.
+ // Vertical faces win a simultaneous corner contact.
+ if( oldY <= top && dy > 0.0 ) {
+ y = top; dy = -dy; back = true;
+ } else if( oldY >= bottom && dy < 0.0 ) {
+ y = bottom; dy = -dy;
+ } else if( oldX <= left && dx > 0.0 ) {
+ x = left; dx = -dx;
+ } else if( oldX >= right && dx < 0.0 ) {
+ x = right; dx = -dx;
+ } else continue; // Missed all bricks
+ _brick(i,back);
+ hit = true;
+ break;
+ }
+ }
}
return 0;
};
+
// Several sound things might happen in this event, pick one
int sound = 0; // Increasing priority: wall, paddle, brick, boost, miss, win.
val _cue = { int snd -> if( snd > sound ) sound = snd; };
@@ -41,11 +99,10 @@ struct Game {
f64 paddle;
val _paddle = { f64 x ->
paddle = x < LEFT +PADDLE_HALF ? LEFT +PADDLE_HALF :
- x > RIGHT-PADDLE_HALF ? RIGHT+PADDLE_HALF :
+ x > RIGHT-PADDLE_HALF ? RIGHT-PADDLE_HALF :
x;
};
-
// Hit the paddle
val _rebound = { ->
// Where along the paddle did you hit?
@@ -62,6 +119,7 @@ struct Game {
};
// Hit a brick?
+ int soundDepth; // Which brick depth for the boop
val _brick = { int index, bool back ->
if( bricks[index] == 0 ) return 0; // No brick to hit!
bricks[index] = 0; // Clear brick
@@ -70,13 +128,25 @@ struct Game {
remaining--; // Fewer bricks to iwn
if( row > depth ) depth = row; // Max depth seen
// Brick-hit sound
- if( sound <= 3 ) { soundDepth = row; _cue(3); }
- if( back && backWall && !boosted ) { boosted = true; _cue(4); }
- if( remaining == 0 ) { phase = WON; _cue(6); }
+ if( sound <= 3 ) { _cue(3); soundDepth = row; }
+ if( back && backWall && !boosted ) { _cue(3); boosted = true; }
+ if( remaining == 0 ) { _cue(6); phase = WON; }
_speed();
return 0;
};
+ // Missed ball with paddle
+ val _lostBall = { ->
+ lives--;
+ phase = lives == 0 ? LOST : READY;
+ backWall = false;
+ depth = 1;
+ boosted = false;
+ _speed();
+ x = paddle; y = PADDLE_Y-RADIUS-6;
+ _cue(5);
+ };
+
// Reset for another round
val _reset = { ->
for( int i = 0; i < bricks#; i++ ) bricks[i] = 1;
@@ -89,74 +159,133 @@ struct Game {
backWall = false;
paused = false;
sound = 0;
+ soundDepth = 1;
paddle = 504.0;
x = paddle;
y = PADDLE_Y-RADIUS-6;
_speed();
};
-};
-// Fixed 8 ms simulation, split further to travel at most two pixels per step.
-// Even at maximum boosted speed a ball cannot tunnel through an 18 px brick.
-val _tick = { !Game g, int direction ->
- if( g.paused || g.phase == WON || g.phase == LOST ) return 0;
- g._paddle(g.paddle+direction*6.4);
- if( g.phase == READY ) { g.x = g.paddle; g.y = PADDLE_Y-RADIUS-6; return 0; }
- f64 seconds = 0.008;
- while( seconds > 0.000000001 && g.phase == PLAY ) {
- f64 dt = 2.0/g.speed;
- if( dt > seconds ) dt = seconds;
- seconds -= dt;
- f64 oldX = g.x, oldY = g.y;
- g.x += g.dx*g.speed*dt;
- g.y += g.dy*g.speed*dt;
- if( g.x < LEFT+RADIUS && g.dx < 0.0 ) {
- g.x = 2.0*(LEFT+RADIUS)-g.x; g.dx = -g.dx; g._cue(1);
- }
- if( g.x > RIGHT-RADIUS && g.dx > 0.0 ) {
- g.x = 2.0*(RIGHT-RADIUS)-g.x; g.dx = -g.dx; g._cue(1);
- }
- if( g.y < TOP+RADIUS && g.dy < 0.0 ) {
- g.y = 2.0*(TOP+RADIUS)-g.y; g.dy = -g.dy;
- g.backWall = true; g._cue(1);
+ // --------------------------------
+ // Drawing code
+ val _hud = { !sys.draw.Canvas c ->
+ c.text(24,22,"BREAKOUT",5,WHITE);
+ c.text(264,20,"SCORE",2,MUTED);
+ _number(c,264,40,score,3,4,WHITE);
+ c.text(424,20,"BRICKS",2,MUTED);
+ _number(c,424,40,remaining,3,4,WHITE);
+ c.text(584,20,"BALLS",2,MUTED);
+ for( int i = 0; i < lives; i++ ) c.fillCircle(590+23*i,50,5,CYAN);
+ c.text(714,20,"SPEED",2,MUTED);
+ int ispeed = speed;
+ _number(c,714,40,ispeed,3,4,boosted ? COLORS[0] : WHITE);
+ c.text(838,40,boosted ? "BOOST!" : "NORMAL",3,boosted ? COLORS[0] : MUTED);
+ };
+
+
+ val _bricks = { !sys.draw.Canvas c ->
+ c.fillRect(LEFT-4,TOP-4,RIGHT-LEFT+8,4,MUTED);
+ c.fillRect(LEFT-4,TOP,4,BOTTOM-TOP,0x29435a);
+ c.fillRect(RIGHT,TOP,4,BOTTOM-TOP,0x29435a);
+ c.text(376,108,boosted ? "BACK WALL BOOST ACTIVE" : "BREAK THROUGH FOR A BOOST",2,boosted ? COLORS[0] : MUTED);
+ for( int i = 0; i < bricks#; i++ ) {
+ if( bricks[i] == 0 ) continue;
+ int row = i/COLS;
+ i64 x = LEFT+2+(i-row*COLS)*BRICK_DX, y = BRICK_TOP+row*BRICK_DY;
+ c.fillRect(x,y,BRICK_W,BRICK_H,COLORS[row]);
+ c.fillRect(x,y,BRICK_W,2,WHITE);
}
- if( g.dy > 0.0 && oldY <= PADDLE_Y-RADIUS && g.y >= PADDLE_Y-RADIUS &&
- g.paddle-PADDLE_HALF-RADIUS <= g.x <= g.paddle+PADDLE_HALF+RADIUS ) g._rebound();
- if( g.y > BOTTOM+RADIUS ) {
- g.lives--;
- g.phase = g.lives == 0 ? LOST : READY;
- g.backWall = false;
- g.depth = 1;
- g.boosted = false;
- g._speed();
- g.x = g.paddle; g.y = PADDLE_Y-RADIUS-6;
- g._cue(5);
- break;
+ };
+
+ val _overlay = { !sys.draw.Canvas c ->
+ if( phase != PLAY || paused ) {
+ c.fillRect(246,422,516,108,0x17263a).rect(246,422,516,108,2,0x29435a);
+ u8[~] title = paused ? "PAUSED" : phase == WON ? "WALL CLEARED!" : phase == LOST ? "GAME OVER" : "READY?";
+ c.text(504-(title#*4-1)*4/2,442,title,4,WHITE);
+ u8[~] help = paused ? "P TO CONTINUE" : phase == WON || phase == LOST ? "R TO PLAY AGAIN" : "SPACE OR CLICK TO SERVE";
+ c.text(504-(help#*4-1)*2/2,490,help,2,CYAN);
}
- for( int i = 0; i < g.bricks#; i++ ) {
- if( g.bricks[i] == 0 ) continue;
- f64 left = LEFT+2+(i-i/COLS*COLS)*BRICK_DX-RADIUS;
- f64 top = BRICK_TOP+(i/COLS)*BRICK_DY-RADIUS;
- f64 right = left+BRICK_W+2*RADIUS, bottom = top+BRICK_H+2*RADIUS;
- if( left < g.x < right && top < g.y < bottom ) {
- bool back = false;
- // Resolve the face crossed this step, using the previous center.
- // Vertical faces win a simultaneous corner contact.
- if( oldY <= top && g.dy > 0.0 ) {
- g.y = top; g.dy = -g.dy; back = true;
- } else if( oldY >= bottom && g.dy < 0.0 ) {
- g.y = bottom; g.dy = -g.dy;
- } else if( oldX <= left && g.dx > 0.0 ) {
- g.x = left; g.dx = -g.dx;
- } else if( oldX >= right && g.dx < 0.0 ) {
- g.x = right; g.dx = -g.dx;
- } else continue;
- g._brick(i,back);
- break;
+ return 0;
+ };
+
+ // With this Canvas, we draw into the blank canvas, then "present". The
+ // underlying graphics lib copies the bits, and we are then free to clear
+ // and draw again while the old bits continue to be presented.
+ val _draw = { !sys.draw.Canvas c ->
+ c.clear(BACKGROUND);
+ _hud(c);
+ _bricks(c);
+ i64 paddle = paddle, ballX = x, ballY = y;
+ int glowRadius = RADIUS+3;
+ c.fillRect(paddle-PADDLE_HALF,PADDLE_Y,2*PADDLE_HALF,12,CYAN);
+ c.fillRect(paddle-PADDLE_HALF+4,PADDLE_Y+2,2*PADDLE_HALF-8,2,WHITE);
+ c.fillCircle(ballX,ballY,glowRadius,boosted ? 0x672a42 : 0x224b61);
+ c.fillCircle(ballX,ballY,RADIUS,boosted ? COLORS[0] : WHITE);
+ _overlay(c);
+ c.text(24,738,"ARROWS / MOUSE: MOVE SPACE / CLICK: SERVE P: PAUSE R: RESTART Q / ESC: QUIT",2,MUTED);
+ };
+
+ // Served and still holding serve key
+ bool serveHeld;
+ // Paused and still holding pause key
+ bool pauseHeld;
+ // Restart and still holding restart key
+ bool restartHeld;
+ // Last mouse position; track moving mouse
+ int lastMouse;
+
+ // Read and act on the controls
+ val _input = { ->
+ val gfx = sys.gfx;
+ bool serve = gfx.key(' ') || gfx.button(gfx.LEFT);
+ bool pause = gfx.key('P');
+ bool restart= gfx.key('R');
+
+ // Keyboard direction and mouse position
+ int direction = (gfx.key(KEY_RIGHT) ? 1 : 0)-(gfx.key(KEY_LEFT) ? 1 : 0);
+ int mouse = gfx.mouseX();
+
+ // Restart requested
+ restartHeld = debounce( restart, restartHeld, { -> _reset(); sys.audio.stop(); } );
+
+ // Playing or ready to play game?
+ if( phase == READY || phase == PLAY ) {
+ // Flip pause state
+ pauseHeld = debounce( pause, pauseHeld, { -> paused = !paused; } );
+
+ // If no key and mouse moved, set paddle to mouse
+ if( mouse != lastMouse && direction == 0 && !paused ) {
+ _paddle(mouse*1.0); // Set paddle to mouse
+ // If READY and not PLAY, ball tracks paddle directly
+ if( phase == READY ) x = paddle;
}
+ lastMouse = mouse;
+
}
- }
- return 0;
+
+ // Serve requested
+ serveHeld = debounce( serve, serveHeld, _serve );
+
+ // Return key direction, so the tick tracking can slide paddle smoothly
+ return direction;
+ };
+
+ val debounce = { bool b, bool bHeld, { int } action ->
+ if( b && !bHeld ) // Action requested and repeating
+ action(); // Do action
+ b; // Dont action again, until 'b' is released
+ };
+
+
+ // Serve the ball; give it a nominal direction and speed
+ val _serve = { ->
+ if( phase == READY && !paused ) {
+ phase = PLAY;
+ dx = 0.4;
+ dy = -_sqrt(1.0-dx*dx);
+ }
+ return 0;
+ };
};
// Fixed-width decimal labels avoid allocating new strings on every frame.
@@ -166,55 +295,6 @@ val _number = { !sys.draw.Canvas c, i64 x, i64 y, int value, int digits, int sca
value /= 10;
}
};
-val _hud = { !sys.draw.Canvas c, Game g ->
- c.text(24,22,"BREAKOUT",5,WHITE);
- c.text(264,20,"SCORE",2,MUTED);
- _number(c,264,40,g.score,3,4,WHITE);
- c.text(424,20,"BRICKS",2,MUTED);
- _number(c,424,40,g.remaining,3,4,WHITE);
- c.text(584,20,"BALLS",2,MUTED);
- for( int i = 0; i < g.lives; i++ ) c.fillCircle(590+23*i,50,5,CYAN);
- c.text(714,20,"SPEED",2,MUTED);
- int speed = g.speed;
- _number(c,714,40,speed,3,4,g.boosted ? COLORS[0] : WHITE);
- c.text(838,40,g.boosted ? "BOOST!" : "NORMAL",3,g.boosted ? COLORS[0] : MUTED);
-};
-val _bricks = { !sys.draw.Canvas c, Game g ->
- c.fillRect(LEFT-4,TOP-4,RIGHT-LEFT+8,4,MUTED);
- c.fillRect(LEFT-4,TOP,4,BOTTOM-TOP,0x29435a);
- c.fillRect(RIGHT,TOP,4,BOTTOM-TOP,0x29435a);
- c.text(376,108,g.boosted ? "BACK WALL BOOST ACTIVE" : "BREAK THROUGH FOR A BOOST",2,g.boosted ? COLORS[0] : MUTED);
- for( int i = 0; i < g.bricks#; i++ ) {
- if( g.bricks[i] == 0 ) continue;
- int row = i/COLS;
- i64 x = LEFT+2+(i-row*COLS)*BRICK_DX, y = BRICK_TOP+row*BRICK_DY;
- c.fillRect(x,y,BRICK_W,BRICK_H,COLORS[row]);
- c.fillRect(x,y,BRICK_W,2,WHITE);
- }
-};
-val _overlay = { !sys.draw.Canvas c, Game g ->
- if( g.phase != PLAY || g.paused ) {
- c.fillRect(246,422,516,108,0x17263a).rect(246,422,516,108,2,0x29435a);
- u8[~] title = g.paused ? "PAUSED" : g.phase == WON ? "WALL CLEARED!" : g.phase == LOST ? "GAME OVER" : "READY?";
- c.text(504-(title#*4-1)*4/2,442,title,4,WHITE);
- u8[~] help = g.paused ? "P TO CONTINUE" : g.phase == WON || g.phase == LOST ? "R TO PLAY AGAIN" : "SPACE OR CLICK TO SERVE";
- c.text(504-(help#*4-1)*2/2,490,help,2,CYAN);
- }
- return 0;
-};
-val _draw = { !sys.draw.Canvas c, Game g ->
- c.clear(BACKGROUND);
- _hud(c,g);
- _bricks(c,g);
- i64 paddle = g.paddle, ballX = g.x, ballY = g.y;
- int glowRadius = RADIUS+3;
- c.fillRect(paddle-PADDLE_HALF,PADDLE_Y,2*PADDLE_HALF,12,CYAN);
- c.fillRect(paddle-PADDLE_HALF+4,PADDLE_Y+2,2*PADDLE_HALF-8,2,WHITE);
- c.fillCircle(ballX,ballY,glowRadius,g.boosted ? 0x672a42 : 0x224b61);
- c.fillCircle(ballX,ballY,RADIUS,g.boosted ? COLORS[0] : WHITE);
- _overlay(c,g);
- c.text(24,738,"ARROWS / MOUSE: MOVE SPACE / CLICK: SERVE P: PAUSE R: RESTART Q / ESC: QUIT",2,MUTED);
-};
// Short, fading triangle waves; the array initializer builds PCM once at startup.
val _tone = { int period ->
@@ -226,27 +306,7 @@ val _tone = { int period ->
int handle = sys.audio.load(samples);
return handle < 0 ? sys.audio.BOOP : handle;
};
-struct Controls {
- bool serveHeld = false, pauseHeld = false, restartHeld = false;
- int lastMouse = 504;
-};
-val _input = { !Game g, !Controls keys ->
- val gfx = sys.gfx;
- bool serve = gfx.key(' ') || gfx.button(gfx.LEFT);
- bool pause = gfx.key('P'), restart = gfx.key('R');
- if( restart && !keys.restartHeld ) { g._reset(); sys.audio.stop(); }
- if( pause && !keys.pauseHeld && (g.phase == READY || g.phase == PLAY) ) g.paused = !g.paused;
- int direction = (gfx.key(KEY_RIGHT) ? 1 : 0)-(gfx.key(KEY_LEFT) ? 1 : 0);
- int mouse = gfx.mouseX();
- if( mouse != keys.lastMouse && direction == 0 && !g.paused && (g.phase == READY || g.phase == PLAY) ) {
- g._paddle(mouse*1.0);
- if( g.phase == READY ) g.x = g.paddle;
- }
- keys.lastMouse = mouse;
- if( serve && !keys.serveHeld ) g._serve();
- keys.serveHeld = serve; keys.pauseHeld = pause; keys.restartHeld = restart;
- return direction;
-};
+
// Fast low accuracy sqrt
val _sqrt = { f64 n ->
@@ -256,32 +316,43 @@ val _sqrt = { f64 n ->
};
val main = { u8[~][] args ->
- val gfx = sys.gfx, audio = sys.audio, time = sys.time;
- val c = new sys.draw.Canvas(WIDTH,HEIGHT);
- val g = new Game;
- if( gfx.open("Simple Breakout",c.width,c.height) != gfx.OK ) return 1;
+ // Shortcuts
+ val gfx = sys.gfx, audio = sys.audio;
+
+ // Open a window
+ if( gfx.open("Simple Breakout",WIDTH,HEIGHT) != gfx.OK ) return 1;
+
+ // Audio setup; pre-specify a series of sounds
audio.open(); // Optional: a missing audio backend leaves play silent.
val sounds = new int[~]{0,_tone(120),_tone(80),audio.BOOP,audio.WIN,audio.LOSE,audio.WIN};
// Bottom to top: roughly 333..800 Hz, below the old 1000 Hz brick tone.
val brickSounds = new int[~ROWS]{int i -> int period = 144-i*12; _tone(period);};
- val keys = new Controls;
- keys.lastMouse = gfx.mouseX();
+
+ // Setup drawing board
+ val c = new sys.draw.Canvas(WIDTH,HEIGHT);
+
+ // Setup game contents
+ val g = new Game;
+ g._reset();
+
+ //
int frames = 0, status = gfx.OK;
- i64 last_ms = time.monotonic(), pending_ms = 0, frame_ms = last_ms;
+ i64 last_ms = sys.time.monotonic(), pending_ms = 0, frame_ms = last_ms;
+
while( status == gfx.OK ) {
if( gfx.key('Q') || gfx.key(gfx.ESCAPE) ) break;
- int direction = _input(g,keys);
- i64 now_ms = time.monotonic(), elapsed_ms = now_ms-last_ms;
+ int direction = g._input();
+ i64 now_ms = sys.time.monotonic(), elapsed_ms = now_ms-last_ms;
last_ms = now_ms;
// Drop long stalls instead of teleporting the ball after a window drag.
pending_ms += elapsed_ms < 0 ? 0 : elapsed_ms > 64 ? 64 : elapsed_ms;
- while( pending_ms >= STEP_ms ) { _tick(g,direction); pending_ms -= STEP_ms; }
+ while( pending_ms >= STEP_ms ) { g._tick(direction); pending_ms -= STEP_ms; }
if( g.sound != 0 ) {
audio.play(g.sound == 3 ? brickSounds[g.soundDepth-1] : sounds[g.sound]);
g.sound = 0;
}
if( now_ms >= frame_ms ) {
- _draw(c,g);
+ g._draw(c);
status = gfx.present(c.pixels);
frame_ms = now_ms+FRAME_ms;
if( args# > 1 && ++frames == 10 ) break;