Referência da API¶
Gerada automaticamente a partir das docstrings de src/ (R31.4). Cada seção é um
módulo do jogo — a documentação aqui é sempre a mesma coisa que está no código.
src.assets¶
src.assets
¶
Resolve o caminho de um asset bundlado, somente-leitura (R21.2).
Diferente de storage.save_dir() (que resolve onde GRAVAR e evita sys._MEIPASS
de proposito, ja que esse diretorio e apagado ao fechar o processo), aqui o
caso e o oposto: o PyInstaller onefile EXTRAI os dados empacotados (via
datas= no .spec) para sys._MEIPASS a cada execucao — e exatamente onde um
asset builtin deve ser lido no executavel empacotado.
asset_path(filename)
¶
Resolve o caminho absoluto de um asset em assets/filename.
Source code in src/assets.py
16 17 18 19 20 21 22 | |
src.bands¶
src.bands
¶
Faixas laterais: corte transversal do subsolo do bioma (R25.2, R24.4).
Quando a tela e mais larga que a area jogavel, cada lateral recebe um corte transversal do terreno, opaco e ocupando a altura inteira do canvas. A area jogavel passa a ser lida como um poco vertical cortado na terra, a unica parte por onde se enxerga o ceu — a estetica de mineshaft do Minecraft.
A opacidade nao e enfeite, e o mecanismo de R24.4: as faixas sao desenhadas DEPOIS
das colunas, entao uma coluna que ainda nao entrou na area jogavel fica escondida
atras da terra. Combinado com o spawn em play.right (R24.3), o jogador enxerga o
obstaculo no mesmo instante em que enxergaria numa tela 2:3 — a oclusao e o que
impede uma tela mais larga de virar vantagem.
Ver specs/v3/design.md secao 32.5.
DEEP_BLOCK = {'overworld': 'stone', 'cave': 'cobblestone', 'nether': 'obsidian'}
module-attribute
¶
Camada profunda de cada bioma: Overworld grama -> terra -> pedra, Cave pedra ->
pedregulho, Nether netherrack -> obsidiana. Chaveado pelo id do bioma, no mesmo
padrao de decor._LAYERS, para que a decoracao nao vire campo de Biome.
ORE_VEIN_PERIOD = 90
module-attribute
¶
Distancia horizontal entre veios de minerio dentro da faixa.
TOPSOIL_ROWS = 3
module-attribute
¶
Fileiras do bloco principal entre a linha do chao e a camada profunda.
SideBands
¶
Desenha as duas faixas laterais, cada uma como uma unica imagem por bioma.
O conteudo so muda quando o canvas ou o bioma mudam, entao cada parede e construida uma vez e guardada com o renderizador, que a chaveia por bioma, lado e tamanho — a mesma disciplina dos gradientes de ceu.
Source code in src/bands.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | |
draw(renderer, textures, biome)
¶
Cobre as laterais do canvas. Sem sobra horizontal (tela 2:3), nao faz nada.
Source code in src/bands.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
src.biome¶
src.biome
¶
Definicoes de bioma e gerenciador de transicao (R5, R2.5, R7.3).
Biome
dataclass
¶
Parametros de um bioma: threshold de ativacao, velocidade, texturas e ceu.
Source code in src/biome.py
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
BiomeManager
¶
Acompanha o bioma ativo e as transicoes de fade/banner conforme o score.
Source code in src/biome.py
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | |
__init__()
¶
Inicia no primeiro bioma (Overworld), sem fade nem banner em curso.
Source code in src/biome.py
95 96 97 98 99 100 | |
draw_background(renderer)
¶
Desenha o ceu do bioma atual, cruzando com o anterior durante o fade.
Source code in src/biome.py
130 131 132 133 134 135 136 137 138 139 140 141 | |
draw_banner(renderer)
¶
Desenha o nome do bioma no topo da area jogavel enquanto o banner dura.
Source code in src/biome.py
143 144 145 146 147 | |
update(score)
¶
Detecta cruzamento de threshold (R5.1) e ativa fade + banner (R5.4).
Retorna True se houve transicao.
Source code in src/biome.py
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | |
src.bird¶
src.bird
¶
Classe Bird: fisica e animacao do passaro (R1).
ANGLES = tuple(range(MAX_ANGLE_DOWN, MAX_ANGLE_UP + 1, ANGLE_FALL_STEP))
module-attribute
¶
Os 31 angulos que a abelha pode assumir, e a grade a que qualquer angulo e preso.
Nao e uma escolha de renderizacao: a fisica ja produzia so estes valores. flap fixa
o angulo em MAX_ANGLE_UP e a queda o decrementa de ANGLE_FALL_STEP em
ANGLE_FALL_STEP ate MAX_ANGLE_DOWN, entao o conjunto alcancavel e exatamente esta
progressao. Torna-la explicita e o que permite pre-computar as 31 x 2 = 62 texturas em
vez de descobri-las uma a uma durante o jogo (R27.2).
Bird
¶
Fisica e animacao do passaro: gravidade, flap, rotacao e hitbox.
Sem __dict__ por instancia: e uma classe de dados pequena e estavel, o caso
em que __slots__ rende — menos memoria e acesso a atributo mais direto (R27.3).
Source code in src/bird.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
rect = pygame.Rect(0, 0, hitbox_size, hitbox_size)
instance-attribute
¶
Hitbox reduzida (~85% do sprite) centralizada (R3.5).
Atributo persistente, e nao property: Game._collision_texture a le uma vez
por coluna, dentro do laco, e cada leitura construia dois pygame.Rect novos —
dezenas de objetos descartaveis por frame para uma geometria que muda uma vez
por passo de simulacao (R27.3). Quem mexe em pos fora de update/update_idle
precisa chamar sync_rect.
__init__(x, y)
¶
Cria o passaro na posicao (x, y), parado, com hitbox sincronizada.
Source code in src/bird.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | |
draw(renderer, textures)
¶
Consulta a textura do (frame de asa, angulo) atual e desenha (R27.2).
Nao ha rotacao aqui: as 62 combinacoes ja foram rotacionadas por
precompute_sprites na inicializacao, e quantize garante que a consulta
sempre caia numa delas. pygame.transform.rotate aloca uma superficie nova a
cada chamada e reamostra o sprite inteiro — barato uma vez, caro sessenta
vezes por segundo (R27.3).
Source code in src/bird.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
flap()
¶
Aplica o impulso de subida e vira o sprite para cima.
Source code in src/bird.py
58 59 60 61 | |
sync_rect()
¶
Recentra a hitbox persistente na posicao atual.
centerx/centery em vez de center = (x, y): a forma com tupla constroi um
objeto por chamada, e a chamada acontece uma vez por passo de simulacao.
A conta e a mesma da property que existia aqui — round(pos) primeiro, meia
largura depois — e nao round(pos + size / 2): para um size impar as duas
divergem, e a hitbox nao pode mudar de lugar por causa de uma otimizacao.
Source code in src/bird.py
97 98 99 100 101 102 103 104 105 106 107 108 | |
update()
¶
Avanca um passo de fisica: gravidade, posicao, rotacao e clamp no teto.
Source code in src/bird.py
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | |
update_idle()
¶
Flutuacao senoidal no estado PRONTO, sem gravidade (R6.1).
Source code in src/bird.py
83 84 85 86 87 88 89 | |
precompute_sprites(renderer, textures)
¶
Rotaciona as 62 combinacoes de (frame de asa, angulo) de uma vez (R27.2).
Chamada na inicializacao e de novo apos um redimensionamento, que descarta o cache de imagens do renderizador. Sem ela as texturas nasceriam sob demanda — uma por frame durante a primeira queda, que e justamente o momento em que o jogador esta olhando o movimento.
Source code in src/bird.py
149 150 151 152 153 154 155 156 157 158 159 | |
quantize(angle)
¶
Angulo da grade de ANGLES mais proximo de angle.
A fisica so produz angulos da grade, entao no jogo esta funcao devolve o proprio valor. Ela existe para que isso deixe de ser uma suposicao: sem ela, um angulo fora da grade — vindo de um estado restaurado, de um ajuste futuro no passo de queda ou de um teste — pediria uma textura que ninguem pre-computou, e a rotacao voltaria para dentro do frame, exatamente onde nao pode estar.
Source code in src/bird.py
126 127 128 129 130 131 132 133 134 135 136 | |
src.config¶
src.config
¶
Constantes globais do jogo.
CORNER_TOLERANCE = 4
module-attribute
¶
Sobreposicao minima, em px, exigida nos dois eixos para contar como colisao (R3.6).
Bird.rect e sempre um quadrado reto (nunca acompanha o angulo de rotacao do sprite
desenhado, de +30 a -60 graus), entao a caixa reta "sobra" alem do contorno visivel da
abelha exatamente nas diagonais — onde ficam os cantos internos do vao das colunas e a
quina do chao. Sem esta tolerancia, 1px de sobreposicao em qualquer eixo ja mata;
com ela, um resvalar raso (fundo num eixo, raso no outro — a marca de um erro de
AABB-vs-rotacao) deixa de contar, e uma batida de frente (que invade os dois eixos
rapido) continua matando na mesma velocidade de sempre.
ground_y()
¶
Topo do chao, na borda de baixo da area jogavel — nao na do canvas (R24.5).
O que fica abaixo dele e faixa decorativa de chao (R25.1): Ground.draw enche
ate a base do canvas, entao a fileira extra e consequencia direta do canvas mais
alto, sem regra nova.
Source code in src/config.py
88 89 90 91 92 93 94 95 | |
play()
¶
Area jogavel dentro do canvas.
Onde a abelha voa, onde as colunas existem e onde a colisao acontece. Sempre 480x720 de mundo, em qualquer tela (R24.1).
Source code in src/config.py
79 80 81 82 83 84 85 | |
screen_h()
¶
Altura do canvas logico ativo (R23.4). Ver screen_w().
Source code in src/config.py
74 75 76 | |
screen_w()
¶
Largura do canvas logico ativo (R23.4).
Deixou de ser constante de modulo na v3: o canvas tem a proporcao da tela real,
entao a largura so existe depois que o display foi criado. Funcao pelo mesmo
motivo de ground_y() — uma constante importada congelaria o valor de quem
importou antes do viewport ser definido.
Source code in src/config.py
63 64 65 66 67 68 69 70 71 | |
set_viewport(vp)
¶
Define o viewport ativo.
Chamado pelo Game depois de criar o display e de novo a cada
redimensionamento da janela (R23.6).
Source code in src/config.py
39 40 41 42 43 44 45 46 | |
viewport()
¶
Viewport ativo.
Sem display criado (testes de unidade, scripts), o padrao e o canvas 2:3 — identico a area jogavel, que e exatamente o comportamento da v2.
Source code in src/config.py
49 50 51 52 53 54 55 56 57 58 59 60 | |
src.decor¶
src.decor
¶
Decoracao de fundo com parallax, duas camadas por bioma (R7.4).
MIN_STRIP_TILES = 4
module-attribute
¶
Piso de ladrilhos por faixa, para uma camada de periodo grande num canvas estreito nao virar uma faixa de dois ladrilhos, onde a repeticao seria obvia.
STRIP_SPANS = 3
module-attribute
¶
Quantas larguras de canvas a faixa de uma camada cobre, no minimo.
O numero e um acordo entre memoria e repeticao. As formas do parallax sao deterministicas por indice, mas nao periodicas: a faixa e que impoe um periodo, e a partir dele o fundo se repete. Tres larguras dao dezenas de segundos de rolagem antes do ciclo fechar — mais que uma partida tipica — sem que uma camada custe mais que alguns megabytes.
DecorManager
¶
Acompanha o deslocamento das duas camadas de parallax de fundo.
Source code in src/decor.py
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 | |
__init__()
¶
Inicia as duas camadas sem deslocamento.
Source code in src/decor.py
249 250 251 252 | |
draw(renderer, decor_id, *, far=True, near=True)
¶
Duas faixas pre-renderizadas, ate quatro desenhos por frame (R27.2, R27.3).
A v2 refazia as duas camadas a cada frame: uma dezena de ladrilhos, cada um
com um random.Random novo — um Mersenne Twister de 624 palavras — so para
sortear de novo a mesma forma que o indice ja determinava. Aqui o sorteio
acontece uma vez, na construcao da faixa, e o frame vira um ou dois recortes
por camada.
far e near desligam cada camada por nivel de qualidade (R29.2). Quem decide
e o Game, com a tabela de quality.py: este modulo nao conhece niveis, so
recebe o que desenhar — a mesma disciplina de mao unica que mantem mobs.py
sem dependencia de jogo (R25.4). A deriva continua sendo atualizada em
update() mesmo com a camada desligada, para que voltar ao nivel de cima nao
produza um salto no cenario.
Source code in src/decor.py
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | |
update(speed)
¶
Avanca o deslocamento das duas camadas, cada uma no seu fator de parallax.
Source code in src/decor.py
254 255 256 257 | |
ore_vein_shapes(x, idx, ground_y)
¶
Gera os quatro cubos de um veio de minerio, com a cor sorteada por idx.
Publico e devolvendo formas, em vez de desenhar, porque tem dois consumidores que
pintam em alvos diferentes: a camada de parallax da Cave, que preenche pelo
renderizador, e as faixas laterais, que constroem uma superficie uma unica vez
(bands.py, R25.2).
Source code in src/decor.py
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | |
src.game¶
src.game
¶
Classe Game: loop principal e maquina de estados (R6).
MAX_FRAME_MS = 250.0
module-attribute
¶
Teto do tempo real que um unico frame pode acumular (R28.3).
Uma pausa longa — o app em segundo plano, o sistema travando — devolveria um delta de segundos, e sem este corte ele viraria uma rajada de passos que faria o jogo pular para frente. R16.1 ja pausa o jogo ao ir para segundo plano; isto e a segunda linha de defesa, para o que nao vem acompanhado de evento nenhum.
MAX_STEPS = 5
module-attribute
¶
Teto de passos de simulacao por frame (R28.2).
Impede a espiral da morte: se um passo passar a custar mais que STEP_MS, cada frame
pediria mais passos do que consegue rodar, e a divida cresceria sem fim ate o jogo
parar de responder. Chegando ao teto, o atraso restante e descartado — perde-se tempo
de jogo, que e o preco de continuar respondendo.
RESIZE_SETTLE_FRAMES = 12
module-attribute
¶
Frames sem novo evento de redimensionamento antes de aplicar o novo canvas — ~200ms a 60 FPS, o bastante para o arrasto assentar sem parecer travado.
STEP_MS = 1000 / FPS
module-attribute
¶
Duracao de um passo de simulacao, em milissegundos (R28.1).
update() continua sendo exatamente o que era: um passo logico de 1/60 s, com as
mesmas constantes de fisica. O que mudou foi quem decide quantos deles rodam por
frame — o tempo real, e nao mais o desenho.
Game
¶
Orquestra estado, entrada, atualizacao e desenho do jogo inteiro.
Source code in src/game.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | |
accumulator = 0.0
instance-attribute
¶
Tempo real ja decorrido e ainda nao simulado, em milissegundos (R28.1).
E o que faz o resto de um frame nao ser jogado fora: a 60 FPS o relogio do
pygame devolve inteiros alternando entre 16 e 17 ms, e descartar a diferenca
para STEP_MS (16,67) faria o jogo correr devagar num aparelho que nao perdeu
um quadro sequer.
last_score = None
instance-attribute
¶
Pontuacao da partida anterior nesta execucao, so em memoria (R36.3). None
ate a primeira transicao GAME_OVER -> PRONTO; nunca gravado em disco.
__init__()
¶
Inicializa pygame, janela, renderer e todos os subsistemas do jogo.
Source code in src/game.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | |
apply_resize(size)
¶
Recalcula o canvas e as faixas para uma janela size (R23.6).
A area jogavel nao muda: continua sendo a mesma coluna de 480x720 de mundo, so o canvas ao redor e elastico (R24.1).
Com a camada de render no lugar, trocar o canvas e uma chamada: o
display.quit()/init() que a task 48 precisava para contornar o SCALED
desapareceu junto com o SCALED. As imagens chaveadas pelo tamanho do canvas
(gradiente de ceu, faixas laterais) sao descartadas para nao ficarem ocupando
memoria de video sem nunca mais serem pedidas.
forget_images nao sabe distinguir o que depende do canvas do que nao depende,
entao as 62 rotacoes da abelha e os 18 sprites de mob caem junto e sao refeitos
aqui — fora do frame, como na inicializacao.
Source code in src/game.py
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
draw()
¶
Desenha um quadro completo: fundo, decoracao, jogo e UI do estado atual.
Source code in src/game.py
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
handle_events()
¶
Coleta as acoes de entrada do quadro e as aplica ao estado do jogo.
Source code in src/game.py
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | |
reset()
¶
Recria todo o estado de uma partida nova (passaro, biomas, colunas, score).
Source code in src/game.py
210 211 212 213 214 215 216 217 218 219 220 221 222 | |
run()
¶
Laco principal: relogio, simulacao em passos fixos e desenho, ate fechar.
Source code in src/game.py
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | |
simulate(frame_ms)
¶
Roda os passos fixos que frame_ms de tempo real comprou (R28.1-R28.3).
Devolve quantos rodaram. A conta e a mesma de qualquer acumulador: o tempo decorrido entra, os passos inteiros saem, e o resto fica para o frame seguinte. Tres detalhes e que sao o requisito:
- o delta e cortado antes de virar passos (R28.3), entao uma pausa de dois segundos vira 250 ms de jogo e nao dois segundos de avanco instantaneo;
- o numero de passos tem teto (R28.2), entao um aparelho onde o passo custa mais que 1/60 s desiste de recuperar em vez de afundar;
- batido o teto, o acumulador zera. Sem isso a divida ficaria guardada e o frame seguinte comecaria ja devendo, o que e a mesma espiral, so que mais lenta.
O que nao muda e o principal: update() continua sendo um passo de 1/60 s. A
60 FPS este laco roda exatamente um por frame, e o jogo e o mesmo da v2 ate no
pixel (R28.4).
Source code in src/game.py
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 | |
update()
¶
Avanca um passo fixo de simulacao conforme o estado atual do jogo.
Source code in src/game.py
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | |
GameState
¶
Bases: Enum
Os quatro estados da maquina de estados do jogo (R6).
Source code in src/game.py
82 83 84 85 86 87 88 | |
src.ground¶
src.ground
¶
Chao rolante de blocos, sincronizado a velocidade das colunas (R7.3).
Ground
¶
Chao rolante: deslocamento visual e faixa de colisao fixa na base do canvas.
Source code in src/ground.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
rect = pygame.Rect(0, config.ground_y(), config.screen_w(), GROUND_H)
instance-attribute
¶
Faixa de colisao do chao, construida uma vez e movida no lugar (R27.3).
__init__()
¶
Inicia sem deslocamento, com a faixa de colisao na linha do chao atual.
Source code in src/ground.py
12 13 14 15 16 | |
draw(renderer, textures, block_main, block_edge)
¶
Uma faixa pre-renderizada, um desenho por frame (R27.2).
A v2 refazia a mesma parede de blocos a cada frame — 22 blits num canvas de 480 de largura, e mais quanto maior a tela — para um resultado que so difere entre frames por alguns pixels de deslocamento horizontal. Aqui a parede e construida uma vez por par de blocos e o rolamento vira o retangulo de origem andando dentro dela.
Continua usando as texturas do bioma atual, sem congelamento (R7.3): o par de blocos entra na chave da faixa, entao trocar de bioma pede outra faixa em vez de reaproveitar a anterior.
Source code in src/ground.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
sync_rect()
¶
Reancora a faixa de colisao na linha do chao e na largura do canvas atuais.
Nao e chamada no passo de simulacao, ao contrario das irmas em Bird e
PipePair: a geometria do chao nao depende de nada que role durante a partida,
so do canvas. Quem chama e Game.apply_resize. Um sync_rect() dentro de
update seria codigo que nenhum teste consegue justificar — foi escrito,
sobreviveu a rodada de mutacao por ser inerte, e saiu.
Source code in src/ground.py
22 23 24 25 26 27 28 29 30 31 32 | |
update(speed)
¶
Avanca o deslocamento visual do chao pela velocidade atual.
Source code in src/ground.py
18 19 20 | |
src.input¶
src.input
¶
InputManager: unifica teclado, mouse, toque e controle Xbox em acoes abstratas (R10, R15).
CONSUMED_EVENTS = (pygame.QUIT, *FOCUS_LOST_EVENTS, pygame.WINDOWRESIZED, pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN, pygame.FINGERDOWN, pygame.JOYBUTTONDOWN, pygame.JOYDEVICEADDED, pygame.JOYDEVICEREMOVED)
module-attribute
¶
Todo tipo de evento que poll reconhece — e, por configure_event_filter, todo
tipo que chega a existir como objeto Python.
E a mesma lista para as duas coisas de proposito: um tipo tratado no poll mas
esquecido aqui seria filtrado antes de chegar la, e o bug apareceria como uma acao
que simplesmente parou de funcionar.
InputManager
¶
Traduz teclado, mouse, toque e joystick em acoes abstratas do jogo.
Source code in src/input.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | |
__init__(renderer)
¶
Detecta e guarda os joysticks ja conectados na inicializacao.
Source code in src/input.py
73 74 75 76 77 78 79 | |
poll()
¶
Consome a fila de eventos e retorna (acoes abstratas, pedido de fechar janela).
Source code in src/input.py
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | |
configure_event_filter()
¶
Deixa entrar na fila so o que o jogo consome (R27.6). Chamar apos pygame.init().
Lista de permissao, e nao de bloqueio: bloquear nominalmente FINGERMOTION e
MOUSEMOTION resolveria o caso conhecido de hoje e deixaria o de amanha passar.
O caso que motiva a regra e o FINGERMOTION. Enquanto o dedo esta na tela, o
Android o emite na taxa de amostragem do digitalizador — 120 a 240 Hz em aparelhos
correntes —, e cada evento vira um objeto Python que e criado, percorrido no laco
de poll e descartado, tres a quatro vezes por frame, para nao virar acao nenhuma.
MOUSEMOTION tem o mesmo perfil no desktop. Sao alocacoes por frame no caminho
quente, que e o que a secao 36 do design esta eliminando (R27.3).
Bloquear e mais forte que ignorar: o SDL descarta o evento antes de ele virar
objeto — pygame.event.post de um tipo bloqueado sequer entra na fila.
Source code in src/input.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | |
src.mobs¶
src.mobs
¶
Mobs decorativos das faixas (R25.3, R25.4).
Nove mobs voxel — creeper, bruxa e aldeao no Overworld; enderman, aranha e esqueleto
na Cave; ghast, blaze e piglin no Nether — habitam exclusivamente as faixas
decorativas: a de chao estendida, abaixo da area jogavel, e as laterais, o corte
transversal do subsolo. Nunca a area jogavel. Isso nao e disciplina de quem desenha:
cada mob e recortado contra o retangulo da faixa antes de ir para a tela, e as faixas sao
disjuntas de viewport.play por construcao (viewport.py), entao um pixel de mob
dentro do campo de jogo nao tem por onde acontecer.
O recorte tambem e o que faz a lateral parecer um poco de mineracao de verdade: o mob some devagar atras da parede de terra em vez de piscar ao cruzar a borda.
R25.4 e a restricao que importa, e este cabecalho de imports e a prova dela.
mobs.py nao importa game, bird, pipes, score nem biome — recebe o id do
bioma, um deslocamento e o Viewport ativo, e mais nada. Nao ha caminho pelo qual um
mob colida, pontue ou mude velocidade, porque nao ha caminho pelo qual ele alcance
qualquer uma dessas coisas. E uma dependencia de mao unica, e e o que torna o
requisito verificavel por teste em vez de por inspecao.
Ver specs/v3/design.md secao 35.
BIOME_MOBS = {'overworld': ('creeper', 'witch', 'villager'), 'cave': ('enderman', 'spider', 'skeleton'), 'nether': ('ghast', 'blaze', 'piglin')}
module-attribute
¶
Tres variedades por bioma (R25.3), chaveadas pelo id do bioma — o mesmo padrao de
bands.DEEP_BLOCK e de decor._LAYERS, para que a decoracao nao vire campo de
Biome.
FIELD_SPANS = 3
module-attribute
¶
Quantas larguras de canvas um campo cobre antes de se repetir, no minimo.
Mesmo acordo entre memoria e repeticao das faixas de parallax (decor.STRIP_SPANS),
so que aqui o custo e ainda menor: um campo e uma tupla de posicoes, nao pixels.
IDLE_FRAME_INTERVAL = 6
module-attribute
¶
Frames entre os dois quadros de idle: a mesma cadencia da asa da abelha
(bird.WING_FRAME_INTERVAL).
Repetida aqui em vez de importada, e a repeticao e o ponto: mobs.py nao depende de
nenhum modulo de jogo (R25.4), e uma constante de seis linhas nao vale abrir a
primeira excecao a isso.
MIN_SLOTS = 6
module-attribute
¶
Piso de posicoes por campo, para que as tres variedades do bioma (R25.3) aparecam
mesmo num canvas estreito, onde a conta de FIELD_SPANS daria menos que isso.
MOB_FACTOR = 0.15
module-attribute
¶
Deriva propria dos mobs, como fracao da velocidade do bioma.
Metade da camada distante do parallax (decor.FAR_FACTOR, 0.3) — quanto mais lento,
mais longe. E o que os coloca claramente atras do cenario e o que impede que algo no
fundo compita com a coluna pela atencao do jogador.
MOB_SIZE = 32
module-attribute
¶
Lado do sprite desenhado, em pixels de canvas.
O dobro exato dos 16x16 de origem: escala inteira, sem pixel de largura desigual, o
mesmo criterio com que textures.scale_pixel_perfect amplia os blocos. Menor que os
48 da abelha de proposito — o mob e cenario, e nao pode ser confundido com o jogador.
SIDE_PERIOD = 160
module-attribute
¶
Distancia horizontal entre dois mobs vizinhos do mesmo campo.
Sao os unicos numeros de espacamento: o desvio sorteado dentro de cada periodo nunca
passa de period - MOB_SIZE, entao dois vizinhos ficam sempre a pelo menos um sprite
de distancia e nenhum par se sobrepoe — nem na emenda do ciclo.
MobField
¶
Os mobs em cena: deriva propria, idle proprio, e nenhuma outra informacao.
Uma instancia por partida, como DecorManager. O estado inteiro sao dois numeros —
o quanto o campo ja derivou e em que quadro de idle ele esta.
Source code in src/mobs.py
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | |
__init__()
¶
Inicia sem deriva, no primeiro quadro de idle.
Source code in src/mobs.py
160 161 162 163 164 | |
draw_ground(renderer, biome_id)
¶
Mobs da faixa de chao estendida. Desenhados depois do chao (design secao 32.6).
Source code in src/mobs.py
178 179 180 181 | |
draw_sides(renderer, biome_id)
¶
Mobs das faixas laterais. Desenhados depois das faixas, que sao opacas.
Um unico campo alimenta os dois lados, e nao um por lado: assim o mob que sai pela esquerda da faixa direita e o mesmo que, muito depois, entra pela direita da esquerda — como se tivesse atravessado a terra por tras do poco.
Source code in src/mobs.py
183 184 185 186 187 188 189 190 191 | |
update(speed)
¶
Avanca a deriva e a animacao de idle.
speed zero congela a deriva e deixa o idle correndo, que e o que a tela
PRONTO pede: o cenario respira antes de a partida comecar, sem sair do lugar.
Source code in src/mobs.py
166 167 168 169 170 171 172 173 174 175 176 | |
precompute(renderer)
¶
Constroi os 18 sprites de uma vez, fora do frame.
Chamada na inicializacao e de novo apos um redimensionamento, que descarta o cache
de imagens do renderizador — mesma disciplina de bird.precompute_sprites. Os
sprites nao dependem do canvas, mas forget_images nao sabe distinguir isso.
Source code in src/mobs.py
111 112 113 114 115 116 117 118 119 120 | |
src.particles¶
src.particles
¶
Sistema de particulas de bloco quebrando na colisao (R3.2).
Particle
¶
Um fragmento de bloco em queda livre, com cor amostrada da textura atingida.
Sem __dict__ por instancia: sao 16 por explosao, todas iguais em forma (R27.3).
Source code in src/particles.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | |
alive
property
¶
Verdadeiro enquanto a duracao restante for maior que zero.
rect = pygame.Rect(0, 0, size, size)
instance-attribute
¶
Retangulo de desenho persistente, pelo mesmo motivo da hitbox da abelha:
draw roda uma vez por particula por frame, e eram 16 Rect novos por frame
na tela de GAME_OVER (R27.3).
__init__(pos, vel, lifetime, size, color)
¶
Cria a particula em pos, com velocidade, duracao, tamanho e cor dados.
Source code in src/particles.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | |
draw(renderer)
¶
Desenha a particula como um quadrado solido na sua cor.
Source code in src/particles.py
63 64 65 | |
update()
¶
Avanca um passo: gravidade, posicao, contagem regressiva de vida.
Source code in src/particles.py
47 48 49 50 51 52 | |
ParticleSystem
¶
Conjunto de particulas vivas: dispara explosoes e atualiza/desenha todas.
Source code in src/particles.py
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | |
__init__()
¶
Inicia sem nenhuma particula em cena.
Source code in src/particles.py
71 72 73 | |
burst(pos, texture, n=16)
¶
Amostra cores da textura do bloco atingido e dispara explosao radial (R3.2).
Source code in src/particles.py
75 76 77 78 79 80 81 82 83 84 85 86 87 88 | |
draw(renderer)
¶
Desenha todas as particulas vivas.
Source code in src/particles.py
103 104 105 106 | |
update()
¶
Atualiza todas as particulas e remove as que ja expiraram.
Source code in src/particles.py
90 91 92 93 94 95 96 97 98 99 100 101 | |
src.perf¶
src.perf
¶
Instrumentacao de desempenho: medicao por frame e sobreposicao de diagnostico (R30).
Desligada por padrao. So e ligada quando a variavel de ambiente BLOCKY_PERF esta
definida com um valor diferente de vazio e de "0" — assim o custo em producao e uma
comparacao contra None por frame, e nenhuma medicao entra no caminho quente (R30.2).
O ponto de medir e nao otimizar no escuro: os numeros produzidos aqui (no aparelho) e
por scripts/benchmark.py (no CI) sao o que prova que cada task de otimizacao da v3
funcionou, em vez de supor. Ver specs/v3/design.md secao 39.
GC_THRESHOLD = (50000, 50, 50)
module-attribute
¶
Limiares do coletor ciclico depois de tune_gc(), contra os (700, 10, 10) padrao.
Setenta vezes mais alocacoes liquidas de contentor entre varreduras da geracao 0. Com a disciplina de alocacao da task 58 no lugar, o laco em regime permanente produz um saldo liquido proximo de zero, entao o limiar alto adia a varredura para muito alem da duracao de uma partida.
WINDOW_FRAMES = 60
module-attribute
¶
Tamanho da janela da media movel: ~1 segundo a 60 FPS.
FrameProfiler
¶
Acumula tempo de atualizacao, tempo de desenho e duracao do frame (R30.1).
O uso previsto por frame e: frame(ms) com o retorno de Clock.tick(), depois
begin() antes de update(), end_update() logo apos, e end_draw() apos o
desenho. Cada end_* mede desde a ultima marcacao e reinicia o cronometro, entao
nao ha necessidade de chamar begin() de novo entre as duas etapas.
Source code in src/perf.py
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | |
backend = 'surface'
instance-attribute
¶
Caminho de renderizacao em uso, preenchido pela camada de render (R26.5).
draw_ms
property
¶
Tempo medio de desenho na janela, em milissegundos.
fps
property
¶
Taxa de quadros media derivada da duracao real dos frames.
update_ms
property
¶
Tempo medio de update() na janela, em milissegundos.
__init__(window=WINDOW_FRAMES)
¶
Cria o profiler com janelas de media movel de window frames.
Source code in src/perf.py
111 112 113 114 115 116 117 118 | |
begin()
¶
Inicia a cronometragem de uma etapa do frame.
Source code in src/perf.py
124 125 126 | |
end_draw()
¶
Fecha a medicao do desenho.
Source code in src/perf.py
132 133 134 | |
end_update()
¶
Fecha a medicao da atualizacao e reinicia o cronometro para o desenho.
Source code in src/perf.py
128 129 130 | |
frame(frame_ms)
¶
Registra a duracao real do frame, tal como reportada pelo relogio do pygame.
Source code in src/perf.py
120 121 122 | |
lines()
¶
Monta as linhas da sobreposicao, em maiusculas (a fonte bitmap nao tem minusculas).
Source code in src/perf.py
159 160 161 162 163 164 165 166 | |
draw_overlay(renderer, profiler)
¶
Desenha a sobreposicao de diagnostico no canto superior esquerdo (R30.1).
Usa a fonte bitmap propria (pixelfont), guardada como imagem por (texto, escala,
cor) — entao so o primeiro frame de cada valor distinto paga a renderizacao dos
glifos, e o custo da sobreposicao nao mascara o que ela esta medindo.
Source code in src/perf.py
169 170 171 172 173 174 175 176 177 178 179 180 181 182 | |
enabled()
¶
Indica se a instrumentacao foi ligada por variavel de ambiente (R30.2).
Source code in src/perf.py
37 38 39 | |
tune_gc()
¶
Tira o coletor ciclico do caminho do frame (R27.3). Chamar ao fim da inicializacao.
Sao tres passos, nessa ordem:
gc.collect()— recolhe o lixo da propria inicializacao antes de congelar, para nao congelar justamente o que deveria ser jogado fora.gc.freeze()— move tudo que sobrou para a geracao permanente, que o coletor nao percorre. Quase todo o jogo e construido aqui e vive ate o fim (texturas, atlas, glifos, definicoes de bioma), entao isso remove de uma vez a maior parte do trabalho de qualquer varredura futura.gc.set_threshold(*GC_THRESHOLD)— o que ainda for varrido, sera muito mais raro.
gc.disable() foi descartado de proposito: acabaria com as pausas, mas qualquer
ciclo criado em tempo de execucao vazaria pelo resto da sessao. Elevar o limiar
mantem a rede de seguranca e adia a varredura para alem de uma partida inteira
(design secao 36).
Efeito colateral que importa em teste: o congelamento e permanente e global. Quem
chama isto fora do jogo (a suite, por criar Game centenas de vezes) precisa
desfaze-lo com gc.unfreeze() e devolver os limiares.
Source code in src/perf.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | |
src.pipes¶
src.pipes
¶
PipePair e PipeManager: obstaculos do jogo (R2).
PipeManager
¶
Cria, move, descarta e desenha as colunas em cena para um bioma.
Source code in src/pipes.py
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | |
__init__(gap_size, block_main, block_edge)
¶
Inicia sem colunas, com os parametros de abertura e textura do bioma atual.
Source code in src/pipes.py
135 136 137 138 139 140 141 | |
draw(renderer, textures)
¶
Desenha so as colunas que caem dentro do canvas (R27.7).
Source code in src/pipes.py
180 181 182 183 184 | |
sync_rects()
¶
Reposiciona os retangulos de todas as colunas (R23.6).
Chamado no redimensionamento, que muda a linha do chao sob colunas que nao se moveram.
Source code in src/pipes.py
171 172 173 174 175 176 177 178 | |
update(speed, gap_size, block_main, block_edge)
¶
Move as colunas (R2.3), spawna novas com o bioma atual (R2.1/R2.2/R2.5).
Remove as que saem da tela (R2.4).
Source code in src/pipes.py
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | |
PipePair
¶
Uma coluna com abertura: metade de cima e metade de baixo do obstaculo.
Sem __dict__ por instancia: ha varias colunas vivas a cada instante e todas
tem exatamente estes campos (R27.3).
Source code in src/pipes.py
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | |
bottom_rect = pygame.Rect(0, 0, PIPE_W, 0)
instance-attribute
¶
Metade de baixo, da outra beirada da abertura ate a linha do chao.
Os dois sao atributos persistentes, e nao property: um frame de jogo os le
quatro vezes por coluna — duas na colisao e duas no desenho — e cada leitura
construia um pygame.Rect novo (R27.3). Quem move a coluna ou troca o canvas
de baixo dela precisa chamar sync_rects.
top_rect = pygame.Rect(0, 0, PIPE_W, 0)
instance-attribute
¶
Metade de cima da coluna, do topo do canvas ate a beirada da abertura.
__init__(x, gap_y, gap_size, block_main, block_edge)
¶
Cria a coluna em x, com a abertura centrada em gap_y.
Source code in src/pipes.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | |
draw(renderer, textures)
¶
Duas faixas pre-renderizadas, dois desenhos por coluna (R27.2).
A pilha de blocos e sempre a mesma imagem — o que muda de coluna para coluna e onde ela e cortada. Entao cada par de blocos vira duas faixas de altura de canvas, uma com a fileira de borda embaixo (coluna de cima) e outra com ela em cima (coluna de baixo), e desenhar uma coluna vira escolher o recorte.
As texturas continuam sendo as do bioma congelado na criacao da coluna (R2.5): o par entra na chave das faixas, entao uma coluna do overworld que sobreviva a entrada na cave continua saindo de terra e grama.
Source code in src/pipes.py
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | |
off_screen()
¶
Descarte na borda esquerda da area jogavel, nao do canvas (R2.4).
O que sai dela ja esta atras da faixa decorativa e nao volta a ser visto.
Source code in src/pipes.py
55 56 57 58 59 60 | |
sync_rects()
¶
Reposiciona as duas metades para o x atual e para a linha do chao atual.
Depende de config.ground_y(), que muda quando a janela e redimensionada — por
isso Game.apply_resize chama isto, e nao so o passo de simulacao: entre o
redimensionamento e o proximo update ha frames desenhados (PRONTO, PAUSADO,
GAME_OVER) que usariam a altura antiga.
Source code in src/pipes.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
visible()
¶
Se alguma parte da coluna cai dentro do canvas (R27.7).
A v2 desenhava sempre um par a mais do que precisava: _spawn cria a coluna
em play.right, que num canvas 2:3 e a propria borda da tela. Eram duas
pilhas inteiras de blocos por frame num obstaculo que ninguem podia ver.
O recorte e contra o canvas, e nao contra a area jogavel: numa tela larga a coluna passa um bom tempo entre a borda do canvas e a da area jogavel, e la ela existe de verdade — e a faixa lateral, desenhada depois, que a esconde (R24.4). Descartar antes disso deixaria a faixa transparente onde ela nao cobrisse.
Source code in src/pipes.py
62 63 64 65 66 67 68 69 70 71 72 73 74 75 | |
src.pixelfont¶
src.pixelfont
¶
Fonte bitmap 5x7 gerada por codigo, sem depender de fontes do sistema (R7.6).
Necessaria porque o Android nao possui as fontes usadas via pygame.font.SysFont
na v1 (ex.: "couriernew"), o que quebraria o alinhamento/legibilidade da UI.
render(text, scale, color)
¶
Renderiza texto (maiusculas) na fonte bitmap 5x7, com cache por (texto, escala, cor).
A conversao para o formato do display (R27.4) acontece ao preencher o cache, e nao a cada chamada: a superficie e montada uma vez por (texto, escala, cor) e devolvida pronta dai em diante. E onde a placar, que muda de texto a cada ponto, paga a conversao — uma vez por valor novo, nunca por frame.
Source code in src/pixelfont.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | |
src.quality¶
src.quality
¶
Qualidade adaptativa: o jogo mede o proprio desempenho e se ajusta (R29).
Nem todo aparelho vai sustentar 60 FPS depois de tudo que a v3 fez. A alternativa a este modulo nao e "rodar liso em todo lugar" — e engasgar num aparelho modesto e deixar o jogador com a impressao de que o jogo e ruim. Aqui o jogo desliga decoracao ate caber.
A regra que nao se negocia (R29.3). Nenhum nivel toca em velocidade de coluna, tamanho de abertura, hitbox ou pontuacao. O que se desliga e sempre decorativo, em ordem crescente de importancia visual: primeiro os mobs e a camada distante do parallax, depois o parallax inteiro. Isso e o que preserva a comparabilidade dos recordes que R24 garante — dois jogadores em aparelhos diferentes continuam jogando o mesmo jogo, um deles com menos enfeite.
Nivel BAIXO reduz o desenho, nao a simulacao. render_fps cai para 30, e os
passos logicos continuam sendo 60 por segundo (o acumulador da secao 37 cuida disso).
Menos quadros, mesma velocidade de jogo — que e precisamente a distincao que R28
existe para fazer.
Ver specs/v3/design.md secao 38.
DROP_FPS = 50.0
module-attribute
¶
Media abaixo disto na janela inteira derruba um nivel (R29.2).
FILENAME = 'quality.json'
module-attribute
¶
Ao lado do highscore.json, no diretorio gravavel por plataforma (R29.5).
RISE_FPS = 58.0
module-attribute
¶
Media acima disto sobe um nivel. A folga de 8 FPS para o limiar de queda e a histerese propriamente dita (R29.4): entre 50 e 58 nada acontece, e e essa faixa morta que impede um aparelho no limite de alternar para sempre.
RISE_WINDOW_FRAMES = 300
module-attribute
¶
Janela para subir: ~5 s, mais que o dobro da de descida (R29.4).
Assimetrica de proposito. Descer e barato e reversivel; subir cedo demais devolve o engasgo que motivou a descida, e o jogador ve o cenario piscando entre dois niveis.
WINDOW_FRAMES = 120
module-attribute
¶
Janela de medicao para descer de nivel: ~2 s a 60 FPS (R29.1).
Curta o bastante para que os primeiros segundos ruins nao se arrastem, longa o bastante para que um engasgo isolado nao decida nada — um travamento de 300 ms no meio de 120 quadros derruba a media de 60 para ~52 FPS, bem acima do limiar de queda.
Level
¶
Bases: IntEnum
Os tres niveis, ordenados: BAIXO < MEDIO < ALTO.
IntEnum porque a ordem e usada — subir e descer de nivel sao comparacoes, e
"esta abaixo do maximo" e um <.
Source code in src/quality.py
34 35 36 37 38 39 40 41 42 43 | |
Quality
¶
Mede a taxa de quadros em JOGANDO e ajusta o nivel (R29.1, R29.2, R29.4).
O estado sao tres numeros: quantos quadros ja entraram na janela, quanto tempo eles somaram, e o nivel atual. Sem lista, sem alocacao por quadro — esta medicao roda no caminho quente do laco e nao pode ser ela mesma o custo (secao 36 do design).
Source code in src/quality.py
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | |
dirty = False
instance-attribute
¶
Nivel mudou e ainda nao foi gravado. Ver Game._flush_quality: gravar aqui
seria escrita em disco dentro do frame de JOGANDO, que R27.5 proibe.
render_fps
property
¶
Limite de quadros por segundo para o relogio do laco principal.
settings
property
¶
O que esta ligado no nivel atual.
__init__(level=Level.ALTO)
¶
Comeca no nivel dado — o gravado da sessao anterior, no jogo (R29.5).
Source code in src/quality.py
141 142 143 144 145 146 147 148 | |
frame(frame_ms, playing)
¶
Registra um quadro e, ao fechar a janela, decide se troca de nivel.
playing e falso fora de JOGANDO, e ai a janela e descartada, nao apenas
pausada (R29.1): a tela PRONTO desenha menos e o GAME_OVER congela o cenario,
entao as duas medem outra coisa. Pior, uma janela que atravessasse a pausa
mediria o tempo parado como quadros lentissimos e derrubaria o nivel de um
aparelho que estava indo bem.
Source code in src/quality.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | |
Settings
dataclass
¶
O que cada nivel liga e desliga. Tudo aqui e decorativo, por construcao (R29.3).
Source code in src/quality.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | |
far_parallax
instance-attribute
¶
Camada distante do parallax — nuvens, estalactites, poças de lava.
mobs
instance-attribute
¶
Mobs decorativos das faixas (mobs.py, R25.3).
near_parallax
instance-attribute
¶
Camada proxima — colinas, veios de minerio, pilares.
particles
instance-attribute
¶
Particulas por explosao de colisao (particles.burst, R3.2).
render_fps
instance-attribute
¶
Quadros desenhados por segundo. A simulacao continua a 60 passos logicos.
load_level(path=None)
¶
Le o nivel gravado, ou ALTO se nao houver um utilizavel (R29.5, R29.6).
Mesma disciplina de score.load_highscore: arquivo ausente, ilegivel, com JSON
quebrado ou com um nivel que nao existe resulta em ALTO e redeteccao. Um arquivo
de preferencia corrompido nunca pode impedir o jogo de abrir — e comecar no maximo
e a escolha certa por ser a unica que se corrige sozinha em dois segundos, ao
contrario de comecar no minimo.
Source code in src/quality.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
save_level(level, path=None)
¶
Grava o nivel detectado (R29.5). Falha de escrita e ignorada, como no recorde.
Source code in src/quality.py
112 113 114 115 116 117 118 119 | |
src.render¶
src.render
¶
Camada de renderizacao: uma interface, dois caminhos de desenho (R26).
A v2 passava uma pygame.Surface de modulo em modulo e desenhava nela. A v3 quer a
GPU do aparelho, mas nao pode exigi-la: o projeto vai ate a API 21 do Android, onde
um renderizador acelerado nao e garantido. Este modulo resolve os dois lados de uma
vez — expoe uma interface pequena (sete operacoes, secao 33.1 do design) que
bird, pipes, ground, decor, particles, biome e ui usam sem saber qual
caminho esta ativo (R26.4), e escolhe o caminho sozinho numa cascata de tres niveis
(R26.1, R26.2, R26.3):
| Nivel | Tentativa | backend |
|---|---|---|
| 1 | Renderer(window, accelerated=1, vsync=True) |
gpu-accelerated |
| 2 | Renderer(window, accelerated=-1, vsync=True) |
gpu-software |
| 3 | SurfaceRenderer — Surface.blit, o caminho da v2 |
surface |
Cada queda de nivel e registrada em log com o erro que a causou (R26.5) e nenhuma
falha escapa de create(): o jogo nunca deixa de abrir por causa do renderizador,
mesma disciplina de degradacao graciosa que o audio segue desde a v1 (R8.4).
Ver specs/v3/design.md secao 33.
Dest = pygame.Rect | tuple[int, int]
module-attribute
¶
Destino de um desenho: um Rect (o desenho e esticado para ele) ou apenas o canto
superior esquerdo (o desenho sai no tamanho nativo da imagem).
ENV_RENDER_BATCHING = 'SDL_RENDER_BATCHING'
module-attribute
¶
Nome de ambiente do hint SDL_HINT_RENDER_BATCHING.
1 agrupa desenhos consecutivos que compartilham textura e modo de mistura numa so
submissao ao driver. E o que transforma os muitos blit de um frame — o chao
ladrilhado, as colunas, os glifos do HUD — em poucas chamadas de verdade (design
secao 40). Lido pelo SDL na criacao do renderizador, entao tem que estar no ambiente
antes dela.
ENV_SCALE_QUALITY = 'SDL_RENDER_SCALE_QUALITY'
module-attribute
¶
Nome de ambiente do hint SDL_HINT_RENDER_SCALE_QUALITY.
0 e vizinho mais proximo: simultaneamente o mais barato e o unico correto para
arte em pixel, porque filtragem linear borraria a estetica voxel do jogo (R26.7).
GpuImage
¶
Bases: Image
Imagem residente na GPU: uma Texture do pygame._sdl2.video.
Source code in src/render.py
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | |
alpha
property
writable
¶
Opacidade aplicada pela GPU na hora do desenho, sem tocar nos pixels.
__init__(texture)
¶
Envolve uma textura ja enviada para a GPU.
Source code in src/render.py
203 204 205 206 | |
GpuRenderer
¶
Bases: Renderer
Desenho pela GPU, sobre pygame._sdl2.video (design secao 33.2).
logical_size faz o proprio SDL escalar o canvas para a janela, na GPU — o mesmo
que pygame.SCALED fazia na v2, mas agora com todo o desenho anterior tambem na
GPU. vsync=True elimina quadros desperdicados e rasgo de imagem (R26.6).
Source code in src/render.py
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
window_size
property
¶
Tamanho da janela do _sdl2, que o SDL mantem atualizado sozinho.
__init__(window, renderer, canvas, backend)
¶
Assume a posse de uma janela e de um renderizador SDL ja criados.
Source code in src/render.py
253 254 255 256 257 258 259 260 261 262 263 264 265 266 | |
clear(color)
¶
Preenche o canvas inteiro com color.
Source code in src/render.py
294 295 296 297 | |
draw(image, dest, area=None)
¶
Desenha a textura, esticando-a quando dest traz um tamanho.
Source code in src/render.py
299 300 301 | |
fill(color, rect)
¶
Preenche rect, ligando a mistura alfa so quando a cor pede.
Source code in src/render.py
303 304 305 306 307 | |
make_image(surface)
¶
Envia a superficie para a GPU uma unica vez (R27.2).
O blend_mode e fixado em mistura alfa para casar com o caminho de
superficie, onde uma superficie com alfa por pixel mistura ao ser blitada e
Image.alpha vale para as duas. Numa textura opaca com alfa 255 a mistura
nao muda o resultado, entao um so modo serve para os dois casos.
Aqui nao se converte: Texture.from_surface ja faz a conversao no envio, e
no caminho de GPU nao ha formato de display para converter (a janela vem do
_sdl2, sem display.set_mode).
Source code in src/render.py
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | |
present()
¶
Troca o buffer da GPU.
Source code in src/render.py
309 310 311 | |
resize(canvas)
¶
Troca o canvas logico. Uma atribuicao, e o SDL reescala na GPU.
Source code in src/render.py
273 274 275 276 | |
snapshot()
¶
Le o alvo de renderizacao de volta para uma Surface.
Source code in src/render.py
317 318 319 | |
to_logical(x, y)
¶
Desfaz a escala de logical_size, pelo proprio SDL.
Source code in src/render.py
313 314 315 | |
Image
¶
Uma imagem pronta para desenhar, no formato que o renderizador ativo entende.
Nenhum modulo de jogo constroi uma diretamente: todas nascem de
Renderer.make_image, a partir de uma Surface gerada por codigo na
inicializacao (R7.1). alpha e de leitura e escrita, e e o que o fade de bioma
(R5.4) e o escurecimento das telas de pausa e game over usam.
Source code in src/render.py
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | |
alpha
property
writable
¶
Opacidade da imagem inteira, de 0 (invisivel) a 255 (opaca).
raw
instance-attribute
¶
A imagem de verdade — Texture na GPU, Surface fora dela. So o renderizador
que a criou desempacota este campo; para os modulos de jogo a imagem e opaca.
size
instance-attribute
¶
Largura e altura em pixels de canvas.
Renderer
¶
Interface de desenho comum aos dois caminhos (R26.4).
As sete operacoes da seção 33.1 cobrem o desenho inteiro. A que carrega mais peso
e draw com area opcional: e ela que permite recortar uma faixa de uma strip
pre-renderizada, que e o mecanismo do atlas (design secao 34) — no caminho de GPU
vira o srcrect do Renderer.blit, e no de superficie o terceiro argumento de
Surface.blit.
Ao redor delas ficam as operacoes que existem porque, desde a task 49, e o
renderizador que possui a janela: image/forget_images (o cache de imagens
construidas por codigo), window_size, resize e snapshot.
Source code in src/render.py
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | |
backend
instance-attribute
¶
Caminho efetivamente em uso, exposto ao log e a instrumentacao (R26.5).
size
instance-attribute
¶
Tamanho do canvas logico. Todo desenho e feito nessas coordenadas; levar o canvas para a tela real e problema do renderizador, nao de quem desenha.
window_size
property
¶
Tamanho real da janela em pixels, que nao e o do canvas logico.
__init__()
¶
Prepara o cache de imagens comum aos dois caminhos.
Source code in src/render.py
116 117 118 | |
clear(color)
¶
Preenche o canvas inteiro, apagando o frame anterior.
Source code in src/render.py
156 157 158 | |
draw(image, dest, area=None)
¶
Desenha image (ou o recorte area dela) em dest.
Source code in src/render.py
160 161 162 | |
fill(color, rect)
¶
Preenche um retangulo com uma cor, misturando quando ela tem alfa.
Source code in src/render.py
164 165 166 | |
forget_images()
¶
Descarta o cache de imagens.
Usado no redimensionamento: gradiente de ceu e faixas laterais sao chaveados pelo tamanho do canvas, entao as versoes antigas nunca mais seriam pedidas e ficariam ocupando memoria de video a toa.
Source code in src/render.py
138 139 140 141 142 143 144 145 | |
image(key, factory)
¶
Imagem construida uma vez e guardada com o renderizador (R27.2).
factory so e chamada no primeiro pedido de cada key. E por aqui que todo
conteudo gerado por codigo — texturas de bloco, glifos de texto, gradientes de
ceu, sprites rotacionados da abelha — vira imagem sem que nenhum modulo de
desenho precise guardar cache proprio nem saber qual backend esta ativo.
Source code in src/render.py
124 125 126 127 128 129 130 131 132 133 134 135 136 | |
make_image(surface)
¶
Converte uma Surface numa imagem do renderizador ativo.
Source code in src/render.py
120 121 122 | |
present()
¶
Publica o frame montado, levando o canvas logico para a tela real.
Source code in src/render.py
168 169 170 | |
resize(canvas)
¶
Troca o canvas logico, acompanhando a janela que o jogador arrastou (R23.6).
Source code in src/render.py
152 153 154 | |
snapshot()
¶
Le o canvas de volta como Surface. Diagnostico e testes apenas.
No caminho de GPU e uma transferencia de VRAM para RAM, cara demais para o
laco principal. Chamar antes de present().
Limite do caminho de GPU, medido: Renderer.to_surface devolve uma superficie
do tamanho logico mas copia os pixels fisicos do alvo, sem desfazer a escala
de logical_size. So e fiel quando o canvas tem o tamanho da janela — que e o
caso dos testes e do desktop. Com escala diferente de 1 a leitura sai recortada;
para conferir um canvas de celular no PC, force a janela com BLOCKY_CANVAS ou
use o SurfaceRenderer, cujo snapshot e sempre o canvas inteiro.
Source code in src/render.py
180 181 182 183 184 185 186 187 188 189 190 191 192 193 | |
to_logical(x, y)
¶
Converte um ponto em pixels reais da janela para coordenada de canvas.
Os dois caminhos convertem a mesma coisa da mesma forma, e e isso que permite
a input.py tratar mouse e toque por um caminho unico (R34.1).
Source code in src/render.py
172 173 174 175 176 177 178 | |
SurfaceImage
¶
Bases: Image
Imagem em memoria principal: uma pygame.Surface, o caminho da v2.
Source code in src/render.py
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | |
alpha
property
writable
¶
Alfa de superficie inteira.
None do pygame vira 255, para que ler o valor logo apos criar a
imagem devolva o mesmo que no caminho de GPU.
__init__(surface)
¶
Envolve uma superficie ja convertida para o formato do display.
Source code in src/render.py
225 226 227 228 | |
SurfaceRenderer
¶
Bases: Renderer
Desenho por Surface.blit, o caminho da v2 (design secao 33.4).
E a ultima rede de seguranca da cascata, e tambem o que mantem a suite de testes
simples: funciona em qualquer lugar onde o pygame funcione, inclusive sob
SDL_VIDEODRIVER=dummy.
O desenho acontece num canvas fora da tela, que present() escala para a janela
com barras nas sobras. Nao se usa pygame.SCALED aqui de proposito: com ele o
SDL entrega o mouse ja convertido mas o toque nao, e to_logical teria
significados diferentes conforme o backend — exatamente a assimetria que a v3
esta desfazendo (R34.1).
Source code in src/render.py
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 | |
window_size
property
¶
Tamanho da janela pelo SDL, e nao pela superficie de display.
Surface.get_size() so acompanha o arrasto no proximo set_mode;
display.get_window_size() ja reflete o tamanho novo.
__init__(canvas, window, *, fullscreen=False)
¶
Cria o display real e o canvas fora da tela onde o jogo desenha.
Source code in src/render.py
336 337 338 339 340 341 342 343 344 345 346 347 348 | |
clear(color)
¶
Preenche o canvas inteiro com color.
Source code in src/render.py
377 378 379 | |
draw(image, dest, area=None)
¶
Desenha a superficie, escalando so quando dest pede outro tamanho.
Source code in src/render.py
381 382 383 384 385 386 387 388 389 390 | |
fill(color, rect)
¶
Preenche rect, misturando quando a cor tem alfa.
A superficie de mistura e cacheada por tamanho: o escurecimento de PAUSADO e de GAME_OVER cobre o canvas inteiro todo frame, e aloca-la a cada um era justamente a maior fonte de lixo por frame da v2 (design secao 30).
Source code in src/render.py
392 393 394 395 396 397 398 399 400 401 402 | |
make_image(surface)
¶
Converte para o formato do display antes de guardar (R27.4).
E aqui que a conversao rende: neste caminho a imagem e uma Surface, e
cada draw dela e um blit que pagaria conversao de formato se ela nao
estivesse no formato do display. Superficies que ja chegam convertidas da
origem (texturas, glifos) passam por aqui de novo, o que e barato e mantem a
garantia valendo para quem constroi a superficie na hora.
Source code in src/render.py
366 367 368 369 370 371 372 373 374 375 | |
present()
¶
Escala o canvas para a janela e publica o frame.
Source code in src/render.py
404 405 406 407 408 409 410 411 412 413 414 415 416 | |
resize(canvas)
¶
Recria o canvas fora da tela e reancora o display na janela arrastada.
Source code in src/render.py
359 360 361 362 363 364 | |
snapshot()
¶
Copia o canvas fora da tela.
Source code in src/render.py
423 424 425 | |
to_logical(x, y)
¶
Desfaz a escala e o deslocamento aplicados por present().
Source code in src/render.py
418 419 420 421 | |
convert(surface)
¶
Devolve surface no formato de pixel do display (R27.4).
Toda superficie nasce no formato padrao do pygame, que nao e necessariamente o do
display. Blitar uma superficie de formato diferente faz o SDL converter pixel a
pixel, a cada blit; converte-la uma vez, na inicializacao, paga esse custo de uma
vez so. Por isso a conversao acontece na origem — em textures.generate_all e no
cache de pixelfont.render — e nao apenas aqui na hora de virar imagem.
convert_alpha so para quem tem alfa por pixel, e convert para o resto:
convert() numa superficie SRCALPHA descartaria a transparencia (as asas da
abelha, o vazado dos glifos), e convert_alpha() numa superficie opaca
acrescentaria um canal alfa que so faria o blit passar pelo caminho de mistura
sem necessidade.
Sem formato de display definido a conversao levanta pygame.error. E condicao
real em dois lugares: nos testes que rodam antes de qualquer set_mode, e no
caminho de GPU, que nunca chama display.set_mode porque a janela vem do
_sdl2 — e onde a conversao seria inocua de qualquer forma, ja que
Texture.from_surface converte no envio. A superficie original serve, so mais
lenta: nunca vale impedir o jogo de abrir por causa de uma otimizacao.
Source code in src/render.py
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 | |
create(canvas, window, *, fullscreen=False, title='', icon=None)
¶
Cria o melhor renderizador disponivel para este aparelho (R26.1, R26.2, R26.3).
Desce a cascata de tres niveis e devolve o primeiro que funcionar, registrando em log o erro de cada queda (R26.5). Nunca levanta excecao: o ultimo nivel e o caminho de superficie da v2, que funciona em qualquer lugar onde o pygame funcione.
O icon entra por aqui porque cada caminho o define de um jeito — pela Window
do _sdl2 ou pelo modulo display —, e quem chama nao deve precisar saber qual.
Os dois hints do SDL sao definidos aqui, e nao no Game, porque e aqui que o
renderizador nasce — o SDL le os dois na criacao dele, e um hint definido depois
nao tem efeito nenhum. setdefault nos dois: investigar outro valor continua
sendo questao de exportar a variavel, sem editar codigo.
Source code in src/render.py
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 | |
src.scale¶
src.scale
¶
Calcula a escala/letterbox que ajusta a resolucao logica fixa a uma janela real.
Nunca corta a imagem (R14.3). Ao contrario do zoom/corte (cover: cresce ate cobrir a janela inteira,
cortando o excedente de um dos eixos — usado ate a task 40), aqui a escala
usada e sempre a MENOR das duas (fit: encolhe ate caber inteiro na janela),
sobrando barra (letterbox/pillarbox) no eixo que nao bate — a mesma conta que
pygame.SCALED ja faz internamente para o mouse; FINGERDOWN chega sem essa
conversao pronta, entao input.py reaproveita esta funcao para desfaze-la
(task 41).
fit_scale(canvas_w, canvas_h, window_w, window_h)
¶
Calcula a escala e os offsets de letterbox de um canvas numa janela.
Retorna (escala, offset_x, offset_y) para desenhar um canvas
canvas_w x canvas_h inteiro dentro de uma janela window_w x window_h,
sem cortar nada. Os offsets sao sempre >= 0 (a barra sobra, centralizada,
nunca falta). Dados invalidos devolvem escala 1 sem deslocamento (nunca
deveria ocorrer com uma janela real).
Source code in src/scale.py
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
src.score¶
src.score
¶
Persistencia do recorde em highscore.json (R4.3, R4.4, R4.5).
load_highscore(path=None)
¶
Le o recorde salvo, ou 0 se o arquivo faltar ou estiver corrompido.
Source code in src/score.py
19 20 21 22 23 24 25 26 27 | |
save_highscore(highscore, path=None)
¶
Grava o recorde em disco, ignorando falha de escrita em silencio.
Source code in src/score.py
30 31 32 33 34 35 36 37 | |
src.sounds¶
src.sounds
¶
Sintese de sons 8-bit via stdlib (sem numpy, R9.2) e controle de mudo (R8).
MixerParams
¶
Bases: TypedDict
Argumentos nomeados de pygame.mixer.init/pre_init que o jogo define.
Tipado, e nao um dict[str, int], para que o desempacotamento nas duas chamadas
seja conferido pelo ty contra a assinatura real do pygame — que tem tambem
parametros de outros tipos (devicename).
Source code in src/sounds.py
16 17 18 19 20 21 22 23 24 25 26 27 | |
SoundManager
¶
Gera e toca os efeitos sonoros sintetizados, com mudo e degradacao graciosa.
Source code in src/sounds.py
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | |
__init__()
¶
Assume o mixer que pre_init + pygame.init() ja deixaram pronto.
So inicializa por conta propria se ele nao estiver de pe — o que acontece quando
alguem constroi o SoundManager fora do jogo (um teste, um script) e quando o
pygame.init() nao conseguiu abrir o dispositivo. No segundo caso a tentativa
aqui tambem falha, e o jogo segue mudo, como desde a v1 (R8.4).
Source code in src/sounds.py
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | |
play(name)
¶
Toca o som se o audio estiver disponivel e nao estiver mudo (R8.3, R8.4).
Source code in src/sounds.py
134 135 136 137 | |
toggle_mute()
¶
Liga/desliga o mudo (R8.3).
Source code in src/sounds.py
139 140 141 | |
mixer_params()
¶
Parametros do mixer: 44.1 kHz, 16 bits com sinal, mono.
O buffer maior so no Android, onde o padrao do pygame e pequeno demais para o caminho de audio do aparelho e o som sai crepitando (R8.4, R14.6, design secao 11). No desktop a chave nem e passada, para continuar valendo o padrao do pygame.
Source code in src/sounds.py
30 31 32 33 34 35 36 37 38 39 40 | |
pre_init()
¶
Fixa os parametros do mixer ANTES de pygame.init() (R27.6, design secao 40).
A v2 inicializava o mixer duas vezes: pygame.init() o subia com os parametros
padrao e, logo em seguida, SoundManager.__init__ o derrubava e o subia de novo
com os parametros certos. Abrir um dispositivo de audio e caro — no Android, dezenas
de milissegundos —, e fazer isso duas vezes na abertura era tempo de inicializacao
jogado fora, alem de expor uma janela em que o dispositivo esta aberto com o buffer
errado.
pre_init nao abre nada: so guarda o que o init seguinte vai usar. Por isso e
inofensiva quando o audio nao existe — a falha continua acontecendo no init, onde
ja e tratada.
Source code in src/sounds.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | |
src.storage¶
src.storage
¶
Resolve o diretorio gravavel do highscore por plataforma (R4.5).
No Android o diretorio de trabalho nao e gravavel; a v1 gravava highscore.json via caminho relativo (resolvido contra o cwd), o que falharia silenciosamente la (a escrita ja engole OSError). Este modulo resolve um diretorio correto por plataforma para score.py usar como default.
is_android()
¶
ANDROID_ARGUMENT e definido pelo python-for-android em tempo de execucao.
Source code in src/storage.py
14 15 16 | |
is_frozen()
¶
PyInstaller define sys.frozen no executavel empacotado (BlockyBee.spec).
No modo onefile, file aponta para o diretorio temporario de extracao (sys._MEIPASS), apagado ao fechar o app — usar esse caminho como base faz o highscore.json nunca sobreviver entre execucoes do .exe/binario.
Source code in src/storage.py
19 20 21 22 23 24 25 26 | |
save_dir()
¶
Diretorio gravavel para dados persistentes (recorde, qualidade), por plataforma.
Source code in src/storage.py
29 30 31 32 33 34 35 36 37 38 39 40 41 | |
src.textures¶
src.textures
¶
Geracao procedural de texturas voxel (blocos 16x16 escalados).
Box = tuple[tuple[int, int, int], int, int, int, int]
module-attribute
¶
Um cubo de um sprite: a cor e o retangulo (x, y, largura, altura) dentro dos 16x16.
MOB_MAKERS = {'creeper': make_creeper, 'witch': make_witch, 'villager': make_villager, 'enderman': make_enderman, 'spider': make_spider, 'skeleton': make_skeleton, 'ghast': make_ghast, 'blaze': make_blaze, 'piglin': make_piglin}
module-attribute
¶
Os nove mobs decorativos de R25.3, todos gerados por codigo — nenhuma imagem externa e nenhum material de terceiros, a mesma disciplina dos blocos (R7.1).
Quem distribui e desenha e mobs.py; aqui ficam so as formas, porque este e o modulo
que ja sabia pintar voxel e porque assim mobs.py nao depende de nenhum modulo de
jogo (R25.4).
generate_all(block_size)
¶
Gera todas as texturas base 16x16 e escala pixel-perfect para block_size (R7.1).
Cada textura sai ja no formato de pixel do display (R27.4). Estas sao as superficies mais reusadas do jogo — a mesma pedra vira dezenas de blocos de coluna por frame, e alimenta tambem as faixas laterais e os sprites rotacionados da abelha —, entao converte-las na geracao vale por todos esses usos de uma vez.
Source code in src/textures.py
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | |
make_bee(frame=0)
¶
Abelha voxel: corpo amarelo com listras pretas, asas em 2 frames (R7.2).
Source code in src/textures.py
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | |
make_blaze(frame=0)
¶
Blaze: nucleo em brasa cercado pelas varas, que giram entre os dois frames.
Source code in src/textures.py
286 287 288 289 290 291 292 293 294 295 296 297 | |
make_cobblestone(seed=4)
¶
Textura de paralelepipedo, 16x16.
Source code in src/textures.py
46 47 48 | |
make_creeper(frame=0)
¶
Creeper: cabeca quadrada com a face mais reconhecivel do jogo, quatro patas.
Source code in src/textures.py
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
make_dirt(seed=1)
¶
Textura de terra, 16x16.
Source code in src/textures.py
24 25 26 | |
make_enderman(frame=0)
¶
Enderman: silhueta preta e esticada, com os olhos roxos como unica cor.
Source code in src/textures.py
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | |
make_ghast(frame=0)
¶
Ghast: cubo branco flutuante com os tentaculos ondulando embaixo.
Source code in src/textures.py
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | |
make_grass_side(seed=2)
¶
Textura de terra com uma faixa de grama no topo, 16x16.
Source code in src/textures.py
29 30 31 32 33 34 35 36 37 38 | |
make_netherrack(seed=5)
¶
Textura de netherrack, 16x16.
Source code in src/textures.py
51 52 53 | |
make_obsidian(seed=6)
¶
Textura de obsidiana com veios roxos, 16x16.
Source code in src/textures.py
56 57 58 59 60 61 62 63 64 65 | |
make_piglin(frame=0)
¶
Piglin: focinho, orelhas de fora e o peitoral dourado.
Source code in src/textures.py
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | |
make_skeleton(frame=0)
¶
Esqueleto: cranio com as orbitas vazias, costelas e membros finos.
Source code in src/textures.py
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | |
make_spider(frame=0)
¶
Aranha: corpo baixo, oito patas e os olhos vermelhos.
Source code in src/textures.py
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | |
make_stone(seed=3)
¶
Textura de pedra, 16x16.
Source code in src/textures.py
41 42 43 | |
make_villager(frame=0)
¶
Aldeao: tunica marrom, bracos cruzados e o nariz que e a marca da especie.
Source code in src/textures.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
make_witch(frame=0)
¶
Bruxa: chapeu de aba larga, manto roxo e o frasco de pocao na mao.
Source code in src/textures.py
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | |
scale_pixel_perfect(surface, size)
¶
Escala uma textura quadrada para size x size sem suavizar (vizinho-mais-proximo).
Source code in src/textures.py
346 347 348 | |
src.ui¶
src.ui
¶
HUD e telas de estado, com fonte pixelada e sombra dura (R7.5, R7.6).
HUD_SCORE_MARGIN = 40
module-attribute
¶
Distancia do topo da area jogavel ate o centro da pontuacao, quando nao ha faixa de ceu para receber o HUD. E a posicao da v1/v2.
LAST_SCORE_COLOR = (235, 235, 235)
module-attribute
¶
Cor da linha de ultima pontuacao: branco quase puro, para bom contraste contra o
fundo, mas ainda visualmente subordinada ao dourado GOLD do recorde (R36.1).
draw_game_over_screen(renderer, score, highscore)
¶
Desenha a tela de GAME_OVER: placar, recorde e instrucao de reinicio.
Source code in src/ui.py
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | |
draw_hud_score(renderer, score)
¶
Desenha o placar do HUD, sobre o ceu ou logo abaixo dele conforme o espaco.
Source code in src/ui.py
262 263 264 | |
draw_mute_icon(renderer, muted)
¶
Botao de mudo tocavel no canto da tela, estilo voxel (R15.4).
Source code in src/ui.py
241 242 243 244 | |
draw_paused_overlay(renderer)
¶
Desenha o overlay de PAUSADO sobre o jogo, com a dica de mudo por seta.
Source code in src/ui.py
267 268 269 270 271 272 273 274 275 276 277 278 279 | |
draw_ready_screen(renderer, highscore, last_score)
¶
Desenha a tela PRONTO: titulo, creditos, instrucao de voo, recorde e, se houver.
A pontuacao da partida anterior nesta execucao (R36).
Source code in src/ui.py
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | |
draw_text(renderer, text, center, base_size=12, color=(255, 255, 255))
¶
Desenha text centrado em center, com sombra dura e escala ajustada a largura.
Source code in src/ui.py
106 107 108 109 110 111 112 113 114 115 116 117 118 | |
hud_score_center(base_size=12)
¶
Onde a pontuacao do HUD e desenhada.
Quando existe faixa de ceu e ela comporta o texto, a pontuacao vai para o meio dela, liberando a area de jogo (R25.7). Numa tela 2:3, sem faixa, cai exatamente onde caia na v2.
Source code in src/ui.py
247 248 249 250 251 252 253 254 255 256 257 258 259 | |
max_text_w()
¶
Largura maxima de um texto: a area jogavel menos 20px de margem de cada lado.
Medida contra a area jogavel, e nao contra o canvas, para que o texto nunca escorra por cima das faixas laterais numa tela larga (R25.2).
Source code in src/ui.py
22 23 24 25 26 27 28 | |
mute_icon_rect()
¶
Geometria do botao de mudo, no canto superior direito do canvas (R15.4).
Fica no canto da tela de verdade, e nao no da area jogavel: e o ponto mais alcancavel no celular, e uma faixa decorativa pode receber controle de interface (R25.6). Verticalmente ele mora na faixa de ceu quando ela o comporta (R25.7); se a faixa for curta demais, desce para dentro da area jogavel em vez de ficar metade em cada uma.
Funcao, e nao constante, porque o canvas so tem dimensao depois que o display
existe. input.py importa daqui para o hit-test, mantendo desenho e posicao do
botao como uma unica fonte de verdade.
Source code in src/ui.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | |
src.viewport¶
src.viewport
¶
Canvas logico e area jogavel: como o jogo preenche telas de qualquer proporcao (R23, R24).
Duas grandezas passam a ser distintas, e essa distincao e o coracao da v3:
- Area jogavel — sempre 480x720 de mundo. E onde a abelha voa, onde as colunas existem e onde a colisao acontece. Nenhuma constante de fisica ou de bioma depende da tela, entao o desafio e identico no celular e no PC e os recordes continuam comparaveis (R24.1, R24.2).
- Canvas logico — tem a proporcao real da tela (Android) ou da janela (desktop). Contem a area jogavel e, ao redor dela, as faixas decorativas (R25).
Por construcao so um dos dois eixos sobra: o canvas recebe exatamente a proporcao da tela, entao a escala final e uniforme e preenche tudo — nenhuma barra preta e nenhum corte (R23.1, R23.2, R23.4).
Ver specs/v3/design.md secao 32.
DESKTOP_WINDOW = (960, 720)
module-attribute
¶
Janela padrao do desktop: mais larga que a area jogavel, para as faixas laterais aparecerem sem o jogador precisar redimensionar nada (R23.7) e para a conferencia visual no PC ser representativa do celular.
ENV_CANVAS = 'BLOCKY_CANVAS'
module-attribute
¶
BLOCKY_CANVAS=LxA forca o canvas, para conferir a proporcao de um celular sem
aparelho. Ferramenta de desenvolvimento, no mesmo espirito de BLOCKY_PERF.
ENV_ORIENTATION = 'SDL_IOS_ORIENTATIONS'
module-attribute
¶
Nome de ambiente do hint SDL_HINT_ORIENTATIONS do SDL2.
O prefixo IOS e historico: a documentacao do proprio SDL descreve o hint como
"which orientations are allowed on iOS/Android". Escrever SDL_HINT_ORIENTATIONS
no ambiente nao teria efeito nenhum — esse e o nome da macro em C, e SDL_GetHint
procura pela string para a qual ela aponta, que e esta.
MAX_SKY_EXTRA = 2 * BLOCK
module-attribute
¶
Teto do que a sobra vertical pode virar de ceu: ate 2 fileiras de bloco.
O limite existe para que uma tela muito alongada nao vire um ceu gigante — gradiente do tamanho do canvas, parallax e um campo inteiro de mobs espalhados por uma faixa que pode passar de 250px de altura num celular alto, exatamente o tipo de decoracao mais caro de desenhar. O que passa disso vai para o chao, que e uma faixa tileavel sem gradiente nem parallax e por isso absorve altura sem custar mais. O valor foi escolhido por inspecao visual, nao por analise.
PLAY_H = 720
module-attribute
¶
Area jogavel em coordenadas de mundo. Imutavel: e a calibracao de dificuldade da v1 (task 12), que a v3 se compromete a nao mexer (R24.1).
Viewport
dataclass
¶
Canvas logico com a area jogavel posicionada dentro dele.
As faixas sao derivadas, nao armazenadas: sky_band e ground_band cobrem a
largura toda do canvas e left_band/right_band a altura toda, porque ceu,
parallax e chao sao desenhados de borda a borda e as faixas laterais os cobrem
depois (R25.5, design secao 32.6).
Source code in src/viewport.py
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
ground_band
property
¶
Fileiras extras de chao abaixo da area jogavel (R25.1). Vazia numa tela 2:3.
height
property
¶
Altura do canvas logico.
left_band
property
¶
Corte transversal do subsolo a esquerda (R25.2). Vazia numa tela 2:3.
right_band
property
¶
Corte transversal do subsolo a direita (R25.2). Vazia numa tela 2:3.
Calculada a partir de play.right, e nao como espelho de left_band, para
absorver o pixel impar quando canvas_w - PLAY_W e impar.
sky_band
property
¶
Faixa de ceu estendido acima da area jogavel (R25.1). Vazia numa tela 2:3.
width
property
¶
Largura do canvas logico.
compute(screen_w, screen_h)
¶
Deriva o canvas logico e a area jogavel de um tamanho real de tela ou janela.
O canvas recebe a proporcao da tela e cresce a partir da area jogavel, nunca
encolhendo abaixo dela. A sobra vertical vira ceu (ate MAX_SKY_EXTRA) e o
resto vira chao; a sobra horizontal vira faixa lateral, com a area jogavel
centralizada. Tamanho invalido devolve o canvas 2:3, na mesma disciplina de
scale.fit_scale — nunca deveria ocorrer com uma tela real, e um numero
estranho jamais deve impedir o jogo de abrir.
Source code in src/viewport.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
forced_size()
¶
Le BLOCKY_CANVAS=LxA, ou None se ausente ou mal formada.
Como compute e idempotente sobre a propria saida, forcar o tamanho de tela
equivale a forcar o canvas. Valor invalido e ignorado em silencio: e ferramenta
de desenvolvimento e nunca deve impedir o jogo de abrir.
Source code in src/viewport.py
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
lock_portrait_orientation()
¶
Trava a orientacao em retrato pelo lado do SDL. Chamar ANTES de pygame.init().
Reforca o orientation = portrait que o buildozer.spec ja declara: o manifesto
convence o Android, este hint convence o SDL, e o jogo nunca gira quando o
aparelho e virado (R23.5). Nao sobrescreve um valor ja definido no ambiente, para
continuar sendo possivel investigar paisagem sem editar codigo.
Source code in src/viewport.py
180 181 182 183 184 185 186 187 188 | |
screen_size()
¶
Tamanho real de tela/janela que alimenta compute.
No Android e a resolucao nativa do aparelho, que e o que faz a tela cheia
preencher os quatro lados (R23.3); no desktop e a janela padrao mais larga que a
area jogavel (R23.7). BLOCKY_CANVAS tem precedencia sobre os dois.
Source code in src/viewport.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | |