| English | Português (Brasil) |
Um metasprite é um objeto lógico composto por uma lista arbitrária de sprites
de hardware 8 por 8 do NES. Um valor metasprite identifica a instância lógica;
não é um índice de OAM, e índices de componentes nunca são expostos ao código-fonte.
Metasprites e valores sprite gerenciados individualmente escrevem
no mesmo shadow de OAM e compartilham o mesmo limite físico de 64 sprites de hardware.
Metadados do PNG2CHR Studio são configurados no momento do build e importados pelo
seu name raiz:
python -m nes_pascal.cli examples/metasprite_player.nsp -o build/metasprite_player.nes --chr assets/game.chr --metasprite assets/player_idle.json
var
Player: metasprite;
begin
nes.import_metasprite(player);
Player := nes.metasprite_create(player.idle_0);
end;
--metasprite é repetível. Caminhos relativos de JSON e CHR são resolvidos a partir
do diretório do código-fonte Pascal. nes.import_metasprite é uma instrução de nível
superior em tempo de compilação e deve preceder nes.run. Ela não abre um arquivo no NES.
Símbolos de quadros utilizam <asset>.<animacao>_<quadro-base-zero>, portanto os seis
quadros idle anexados são player.idle_0 até player.idle_5.
Cada local sintaticamente distinto de nes.metasprite_create(frame) é uma instância
estática persistente. A criação se inicia oculta com o quadro selecionado e posição
zerada. Não há heap, destruição ou busca de nomes em tempo de execução.
nes.metasprite_set_position(Player, X, Y);
nes.metasprite_set_frame(Player, player.idle_2);
nes.metasprite_set_animation(Player, player.idle);
nes.metasprite_restart_animation(Player);
nes.metasprite_set_flip_horizontal(Player, true);
nes.metasprite_set_flip_vertical(Player, false);
nes.metasprite_hide(Player);
nes.metasprite_show(Player);
Posição recebe dois valores byte, setters de flip recebem boolean, e a seleção
de quadro requer um quadro simbólico do asset de criação da instância. Alterar um
quadro preserva posição, visibilidade e flips de todo o objeto. Se o novo quadro
possuir menos componentes, as entradas reservadas não utilizadas permanecem ocultas.
Alterar posição ou quadro enquanto oculto não exibe a instância. E3055 rejeita um
quadro de outro asset quando a identidade da instância é conhecida diretamente na
compilação. Como variáveis metasprite comuns são identidades opacas de um byte, o
runtime também verifica IDs de assets e ignora com segurança um emparelhamento
dinâmico incompatível em vez de ler além da capacidade reservada.
Seleção de animação, temporização automática, repetição (loop), conclusão em disparo único (one-shot), reinício e a interação com seleção manual de quadros estão documentados em Animação de sprites.
O compilador aceita metadados do PNG2CHR Studio png2chr-studio-animation versão 2.
Ele valida a estrutura exigida de objetos/arrays, tamanho de tile de origem 8 por 8,
dimensões do quadro, origem dos metadados, contagem de componentes, coordenadas com
sinal, intervalo de tiles, byte de atributos, bits de paleta, booleanos de flip,
capacidade declarada de 256 tiles, declaração de 8 KiB de CHR NROM e um objeto
opcional collision_box por quadro.
Cada quadro é armazenado como uma lista de componentes. Layouts podem ser retangulares,
esparsos, assimétricos ou utilizar deslocamentos negativos relativos à origem. Índices
de CHR repetidos e não contíguos permanecem inalterados. Apenas entradas no array sprites
de cada quadro consom OAM, de modo que tiles de origem omitidos/transparentes não a consomem.
A ordem dos componentes é preservada.
Existe uma única representação imutável de quadro. Seleção manual de quadro e
animação de sprites referenciam os mesmos IDs de quadros e as
mesmas listas de componentes; o estado de animação nunca cria uma segunda geometria,
pivô, flip ou caminho de recorte.
A caixa opcional e o fallback derivado dos componentes usados por
nes.metasprite_bounds também são metadados imutáveis
por quadro e não adicionam estado de colisão por instância.
O PNG2CHR Studio versão 2 define a origin raiz como a âncora lógica configurada nas
coordenadas de pixels do quadro de origem. Ele subtrai essa âncora durante a exportação,
de modo que cada valor animations[].frames[].sprites[].x/y já é um deslocamento com sinal
a partir da âncora. O NES Pascal consome esses deslocamentos diretamente; ele não subtrai
origin novamente.
Campos de ferramentas como coordenadas na imagem de origem, linha/coluna do tile de origem, rótulos de reutilização e estatísticas de conversão permanecem apenas no compilador. Durações de animação e política de repetição tornam-se tabelas PRG compactas e imutáveis. Gráficos de tiles permanecem exclusivamente na CHR-ROM; não são copiados para PRG.
Os metadados atualmente declaram uma capacidade de tiles e contagem final de tiles,
mas não identificam de forma inequívoca a qual pattern table de 4 KiB do NES os índices
se destinam. O NES Pascal utiliza o banco NROM CHR configurado e valida o intervalo de
tiles de um byte para sprites, mas não pode provar que arquivos JSON e CHR com nomes
diferentes vieram da mesma exportação. Um contrato futuro do PNG2CHR Studio deve adicionar
uma identidade explícita de pattern table/banco e preferencialmente um hash do conteúdo
de CHR. Os metadados anexados indicam player.chr enquanto o arquivo compatível fornecido
é game.chr; o compilador, portanto, não trata o nome de arquivo de saída das ferramentas
como identidade.
Coordenadas do quadro de origem utilizam o canto superior esquerdo da imagem como (0, 0).
width e height do quadro descrevem essa extensão de origem e são retidos para
representação em tempo de compilação e validação de bounds de colisão. Eles não
implicam uma grade retangular de componentes e não são copiados para a RAM em
runtime.
A origin nos metadados raiz seleciona um ponto nesse sistema de coordenadas de origem
tanto como âncora lógica quanto como pivô de flip de todo o metasprite. O PNG2CHR Studio
exporta uma célula de origem em (source_x, source_y) como:
component.x = source_x - origin.x
component.y = source_y - origin.y
O NES Pascal armazena component.x/y inalterados como dx/dy com sinal.
nes.metasprite_set_position(M, x, y) posiciona a âncora na coordenada lógica da tela
(x, y), de modo que X/Y lógicos sempre identificam o mesmo ponto do objeto independentemente
do quadro ou estado de flip. Uma origem (0, 0) deliberadamente utiliza o canto superior
esquerdo do quadro de origem como âncora. Uma origem dentro ou fora de um quadro pode
representar os pés de um personagem, o centro de uma nave, efeitos assimétricos ou objetos
em dobradiça (hinge).
A origem do quadro de origem, a âncora lógica e o pivô de flip são conceitos distintos,
mas a versão 2 necessita de apenas uma origin declarada: o (0, 0) do quadro de origem
é fixo, enquanto a origin configurada é tanto âncora quanto pivô. Um campo pivot separado
é desnecessário a menos que um formato de asset futuro necessite de flip em torno de um
ponto diferente da posição lógica.
O PNG2CHR Studio já utilizava o contrato acima quando emitia o esquema na versão 2. O
importador inicial do NES Pascal 0.5.3 tratava incorretamente sprites[].x/y como coordenadas
de origem e subtraía origin uma segunda vez. Isso era invisível para assets (0, 0), mas
incorreto para qualquer âncora diferente de zero. O importador agora segue o contrato existente
do produtor; não há alteração de esquema nem modo de compatibilidade para a subtração dupla incorreta.
Metadados (0, 0) existentes permanecem determinísticos e mantêm seu comportamento de
flip no canto superior esquerdo (estilo dobradiça). O NES Pascal nunca adivinha um centro a
partir de largura ou altura do quadro. Para centralizar um quadro 24 por 24, reexporte com
origin: {"x": 12, "y": 12}; os deslocamentos de componentes correspondentes tornam-se
-12, -4 e 4. Alterar apenas origin sem reexportar os deslocamentos de componentes
não satisfaz o contrato do formato. O fixture de jogador fornecido foi reancorado dessa forma.
O flip espelha cada componente 8 por 8 em torno da âncora lógica, não em torno de um
retângulo inferido de largura/altura. Para a coordenada lógica L e deslocamento com
sinal superior-esquerdo d, o posicionamento é:
não invertido: component_top_left = L + d
invertido: component_top_left = L - d - 8
De forma equivalente, o deslocamento invertido é d' = -d - 8. O 8 adicional compensa a
largura ou altura do componente. O flip horizontal aplica XOR no bit 6 de atributos da
OAM; o flip vertical aplica XOR no bit 7. O XOR preserva bits não relacionados de paleta
e prioridade e combina corretamente o flip do objeto inteiro com um componente já invertido pelo asset.
Um pivô centralizado preserva o intervalo delimitador visível correspondente enquanto espelha o posicionamento assimétrico e a orientação dos tiles dentro dele. Um pivô deliberadamente não centralizado mantém X/Y lógicos estáveis, mas pode deslocar o intervalo delimitador visível, o que é o comportamento esperado de dobradiça. O runtime não altera X/Y lógicos em nenhum dos casos.
O compilador determina a maior contagem de componentes entre todos os quadros no asset
de uma instância e reserva essa quantidade de entradas livres. Reservas explícitas
individuais são processadas primeiro, depois locais de nes.sprite_create(), e em seguida
locais de criação de metasprites na ordem do código-fonte. Entradas de metasprites podem
ser não contíguas e são registradas como proprietárias do tipo metasprite_component. A
instância resolvida mapeia privadamente posições de componentes para esses índices de OAM.
Se o total compartilhado exceder 64, E3050 interrompe a compilação. A alocação nunca sofre wrap, sobrescreve outro proprietário, trunca um quadro ou retorna um sentinela inválido. Esse máximo fixo permite que animações automáticas alternem entre quadros de tamanhos diferentes sem alocação de OAM em tempo de execução.
Cada instância utiliza quatro bytes mutáveis de RAM comum: X lógico, Y lógico, ID do quadro selecionado e flags de visibilidade/flip. O suporte a metasprites também inclui oito bytes de rascunho compartilhados em RAM comum e dois ponteiros de dois bytes compartilhados na Zero Page. Tabelas imutáveis em PRG contêm:
Largura, altura e origem não ocupam bytes em tabelas de ROM porque os deslocamentos de componentes exportados já codificam a geometria relativa à âncora.
Programas utilizando animação de sprites adicionam quatro bytes mutáveis por instância e tabelas compactas de sequência de animação na PRG-ROM. Programas que utilizam apenas operações estáticas de quadros mantêm os custos acima e não incluem o estado ou rotinas de animação.
Exibir renderiza cada componente ativo a partir do estado atual. Ocultar escreve $FF
no byte Y de cada slot detido. Renderizar um quadro menor oculta os slots restantes.
A adição de coordenadas possui sinal e é verificada antes de escrever na OAM. Um componente
é exibido apenas quando todo o tile 8 por 8 for representável: canto superior esquerdo X
0..248 e topo lógico Y 1..232. Qualquer elemento além do limite esquerdo, direito,
superior ou inferior é ocultado como um sprite de hardware inteiro; a aritmética nunca
causa wrap para a borda oposta. Caminhos de deslocamento positivo e negativo verificam
novamente os limites finais direito/inferior. Recorte em nível de pixel não está implementado.
O Y do metasprite é uma coordenada de topo lógico da tela. Para um componente visível, o
runtime escreve OAM Y = topo lógico - 1 exatamente uma vez, correspondendo à convenção
de Y de sprites do NES. O topo lógico 0 seria codificado como $FF, que este runtime
reserva como sentinela oculto, de modo que esse componente é recortado.
Isso difere intencionalmente da API de baixo nível de sprites individuais: nes.sprite_set_y
aceita e armazena o byte Y bruto da OAM sem subtrair um, enquanto o posicionamento de
metasprites aceita uma âncora lógica de tela e realiza a conversão por componente. O
comportamento existente de Y e ocultar/exibir de sprites individuais permanece inalterado.
O trabalho de layout de metasprites é executado na inicialização, no código principal
ou em nes.on_update, nunca na NMI. A NMI continua transferindo o shadow completo alinhado
a página através de um único DMA de OAM.
A capacidade de 64 entradas de OAM não é o único limite de sprites. A PPU do NES renderiza no máximo oito sprites de hardware em uma mesma scanline. Um metasprite largo, ou vários objetos compartilhando scanlines, podem apresentar desaparecimento de sprites (dropout) mesmo quando a posse total de OAM for válida. O NES Pascal não ordena sprites, não rotaciona a prioridade na OAM, não equilibra scanlines nem implementa mitigação de flicker. A animação não altera esse limite do hardware.