5.6 KiB
Tilemap and Metatile Runtime Binary Layout Agenda
Status
Open
Purpose
Convergir a discussão sobre o contrato de mapa para handheld em duas camadas:
- formato de autoria/edição (JSON legível),
- formato de execução (layout binário compacto em runtime).
O objetivo é fechar qual estrutura será adotada para tilemap bank, tileset bank e dados de colisão/flags, com foco em previsibilidade de memória e streaming de mapas.
Domain Owner
docs/packer
Este tema é owner de packer porque envolve transformação de artefatos (JSON -> BIN), contrato de build e layout de saída para consumo do runtime.
Domínios impactados (cross-domain):
docs/vm-arch(contrato de leitura em runtime e limites de memória),docs/studio(edição/preview de mapas no workspace),docs/compiler/pbs(integração futura com referências de assets em código, quando aplicável).
Problem
Hoje existe alinhamento conceitual de que:
- visual e colisão devem ser separados por responsabilidade;
- o mapa não deve repetir metadados extensos por célula;
- apenas uma janela ativa de até
9mapas deve ficar residente em memória.
Mas ainda não existe decisão formal sobre:
- layout binário final por célula em runtime,
- orçamento por mapa e por janela ativa,
- responsabilidade exata entre
map banketileset bank.
Sem esse contrato, o packer não fecha especificação de saída e o runtime/studio ficam sem baseline único.
Context
Premissas atuais da discussão:
tileset bankpode ter tamanho diferente dos demais banks;map banknão precisa seguir o mesmo tamanho detileset bank;- mapa deve referenciar IDs compactos (visual, colisão e flags), em vez de duplicar estrutura completa por célula;
- paletas por bank continuam sendo opção válida para preservar decisões artísticas locais;
- orçamento alvo foi discutido no contexto de
64 KiBpor map bank e janela ativa de3x3mapas (9residentes).
Options
Option A — Célula u8 (mapa ultra-compacto)
- Cada célula armazena apenas
metatileId(0..255). - Colisão/flags vêm de tabela auxiliar por
metatileId.
Option B — Célula u16 bit-packed (recomendação inicial)
- Cada célula usa
16 bitscom divisão sugerida:visualId:10 bits(0..1023),collisionId:5 bits(0..31),flags:1 bit(0..1) ou reservado para evolução.
- Permite desacoplamento visual/lógico sem custo de
u32.
Option C — Célula u32 (maior flexibilidade)
- Exemplo de divisão:
visualId:12 bits,collisionId:8 bits,flags/event:12 bits.
- Ganho de expressividade para triggers/eventos inline; custo de memória dobra vs
u16.
Tradeoffs
- Option A minimiza RAM e I/O, mas limita variedade visual e desloca muita semântica para tabelas externas.
- Option B oferece bom equilíbrio para handheld: compacta, previsível e com espaço suficiente para muitos casos de mapa.
- Option C simplifica evolução funcional (eventos por célula), mas pressiona memória e banda de streaming sem necessidade comprovada agora.
Runtime Binary Structure (focus)
Estrutura sugerida para runtime (baseada em Option B):
-
Map Header (fixo)
magic(4 bytes)version(u16)width(u16)height(u16)cellEncoding(u8) — ex.:1 = U16_PACKED_V1visualBankId(u16)collisionBankId(u16)reserved/checksum(conforme decisão posterior)
-
Cell Stream
- vetor contínuo com
width * heightcélulasu16, little-endian; - leitura linear favorece cache e descompressão simples.
- vetor contínuo com
-
Optional Chunks (future-proof)
- bloco opcional de
eventTriggers; - bloco opcional de
spawnPoints; - bloco opcional de
navHints.
- bloco opcional de
Packing de célula (U16_PACKED_V1):
bits 0..9=>visualIdbits 10..14=>collisionIdbit 15=>flag0
Decodificação de referência:
visualId -> metatile visual table -> 4 subtiles (8x8) + palette/flip/prioritycollisionId -> collision/material table -> walk/solid/swim/damage/etc.
Memory Notes for Active Window (9 maps)
Assumindo 64 KiB por map bank:
1mapa residente:64 KiB9mapas residentes:576 KiB
Capacidade por encoding dentro de 64 KiB:
u8:65,536células (256x256máximo quadrado)u16:32,768células (~181x181máximo quadrado, ou retângulos equivalentes)u32:16,384células (128x128máximo quadrado)
Recommendation
Adotar Option B como baseline para decisão:
- autoria em JSON orientada a IDs (
visualId,collisionId,flags); - empacotamento determinístico para
U16_PACKED_V1no build; - janela ativa de runtime limitada a
9mapas com budget explícito; - extensão para
u32somente via nova versão de encoding e evidência de necessidade.
Open Questions
collisionIdcom5 bits(32classes) é suficiente para os biomas/projetos previstos?flag0deve ser reservado para trigger rápido ou para variação visual contextual?- Quais chunks opcionais entram já em
V1e quais ficam paraV2? - O
map bankseguirá estritamente64 KiBou terá tamanho variável com metadado de capacidade? - Qual política de compressão do stream (
none,LZ4, etc.) será padrão no packer?
Expected Follow-up
- Abrir
decisionemdocs/packer/decisionsfechando o encodingU16_PACKED_V1. - Propagar contrato de leitura para
docs/vm-arch. - Definir no
docs/studio/specso schema de edição JSON correspondente. - Planejar PR de implementação (
packer+runtime) com testes de roundtrip (JSON -> BIN -> decode).