# The play screen # # --- # Did some profiling with # cargo instruments --release --open -- libs -m a.ggez.demo.tetris # It seems roughly 50% of the time is spent in the 'draw' # callback of play. # # The relevant lines in the profiler looked something like this: # 41.08 s 99.0% 28.00 ms ggez::event::run::h07c034247191fc79 # 37.82 s 91.2% 16.00 ms ...EventHandler$GT$::draw::h31b66df87e5fe65c # 21.74 s 52.4% 0 s mtots::mds::ggez::wctx::with_ctx::hdbc1287be2d5e83a # 21.73 s 52.4% 0 s mtots_core::base::eval::Eval::call::ha0b27def1bc539fa # Roughly # # This suggests ~91% of the total time was spent in the 'draw' callback overall, and # ~52% of the total time was spent in the actual script code (i.e. the 'draw' callback # writtten in mtots). # This disparity might be because 'yield_now' is called after the draw callback finishes # to allow the CPU to breathe # # The drop in performance due to design (i.e. calling a callback in script-land for # every frame) was more or less expected, though part of this exercise was to see # if this performance drop was tenable. Doing things this way definitely simplifies # the API quite a bit, since all the retain logic can be implemented in the script # itself # # For the mini tetris demo, this seems ok, but this does suggest to me that anything # more complex wouldn't really be tenable with this sort of API. # --- # import a.ggez import ...model::Model import ...model::Board import a.time import ..pause def update(app, model) { [R, C] = [model.R(), model.C()] board_screen_x = board_screen_y = 0 width = height = board_screen_width = board_screen_height = nil cell_height = cell_width = nil board_color = ggez::Color(0.3, 0.5, 0.8) background_color = ggez::Color(0.8, 0.5, 0.3).scale(0.5) piece_color = ggez::Color(1.0, 0.0, 0.7) filled_color = ggez::Color(0.8, 0.8, 0.8) def draw_background(ctx) { ggez::MeshBuilder().rect( 0, 0, width, height, background_color, ).build(ctx).draw(ctx) } def draw_board_background(ctx) { ggez::MeshBuilder().rect( board_screen_x, board_screen_y, board_screen_width, board_screen_height, board_color, ).build(ctx).draw(ctx) } def board_cell_xy(r, c) = { x0 = board_screen_x + c * cell_width y0 = board_screen_y + r * cell_height [x0, y0] } def draw_board(ctx) { for [r, c] in model.board().filled_cells() { [x, y] = board_cell_xy(r, c) mb = ggez::MeshBuilder() mb.rect(x, y, cell_width, cell_height, filled_color) mb.build(ctx).draw(ctx) } nil.map(model.piece(), def(piece) = { for [r, c] in piece.occupied_cells() { [x, y] = board_cell_xy(r, c) mb = ggez::MeshBuilder() mb.rect(x, y, cell_width, cell_height, piece_color) mb.build(ctx).draw(ctx) } }) } def draw_score(ctx) { state = model.state() rhs_x = width * 0.55 rhs_y = height * 0.1 score_text = ggez::SimpleText( "cleared lines = " + str(model.cleared_line_count()), scale=width / 50, ) score_text.draw(ctx, [rhs_x, rhs_y]) if state is :lose { go_text = ggez::SimpleText("GAME OVER") text_width = go_text.width(ctx) text_height = go_text.height(ctx) go_text.draw(ctx, [width / 2 - text_width / 2, height / 2 - text_height / 2]) } } app.update( init = def(ctx) { nonlocal width, height, board_screen_height, board_screen_width nonlocal board_screen_x, board_screen_y nonlocal cell_width, cell_height [width, height] = ctx.size() board_screen_x = width * 0.05 board_screen_y = height * 0.05 board_screen_width = (width * 0.9) / 2 board_screen_height = height * 0.9 cell_height = board_screen_height / R cell_width = board_screen_width / C }, update = def(ctx) { model.tick(time::now()) }, draw = def(ctx) { draw_background(ctx) draw_board_background(ctx) draw_board(ctx) draw_score(ctx) }, key_down = def(ctx, key, mods) { if mods == [] or mods == [:repeat] { if key is :Escape or key is :P { pause::update(app, model) } elif key is :A or key is :Left { model.move_left() } elif key is :D or key is :Right { model.move_right() } elif key is :S or key is :Down { model.move_down() } elif key is :W or key is :Up { model.rotate() } elif key is :Space or key is :Return { model.hard_drop() } else { print('Unrecognized key: %r' % [key]) } } else { print('Unhandled key combo: [%r, %r]' % [key, mods]) } }, gamepad_button_down = def(ctx, button, gamepad) { if button is :DPadLeft { model.move_left() } elif button is :DPadRight { model.move_right() } elif button is :DPadDown { model.move_down() } elif button is :South { model.rotate() } elif button is :East { model.rotate(3) } elif button is :North or button is :West { model.hard_drop() } elif button is :Start { pause::update(app, model) } else { print('Unrecognized gamepad button %r, %r' % [button, gamepad]) } }, ) }