Plugin do Godot
Um plugin de editor em GDScript: um único EditorImportPlugin mais um punhado de builders que transformam um arquivo .proscenio numa cena nativa do Godot a cada reimportação.
O que faz
- Reimportação para uma cena nativa. O plugin regenera uma cena (Skeleton2D + Bone2D + Polygon2D / Sprite2D + AnimationPlayer) sempre que um arquivo
.proscenioentra ou muda no projeto. A reimportação sobrescreve a cena gerada por inteiro - ela reconstrói a árvore do zero e não há diff nem merge contra a saída anterior, porque o.proscenioé a fonte e a cena é um artefato gerado que a engine possui. A cena gerada roda com o plugin desinstalado: são nós puros do Godot 4, sem GDExtension e sem dependência de runtime. - Segurança da cena-wrapper. Como a reimportação sobrescreve, as edições nunca vivem dentro da cena gerada; a forma suportada de mantê-las é envolver a cena gerada no seu próprio
.tscnque a instancia. O wrapper guarda seus scripts, nós extras e o seu próprioAnimationPlayer, então ele sobrevive intocado a cada reimportação. - Leitura tipada. O importador lê o documento como um Resource tipado (
ProscenioDocument.from_dict), checa oformat_versione constrói a árvore de nós em ordem: esqueleto, atlas, slots antes dos sprites, sprites, animação.
Como é construído
Pequeno e focado: um plugin de importação e cinco builders, cada um tratando apenas dos tipos de nó que reconhece ao ler o campo type em cada elemento. Sem herança nem polimorfismo, apenas funções chamadas em sequência. A camada de leitura tipada é gerada a partir do schema.
A superfície do importador
O plugin registra um único EditorImportPlugin cuja identidade é fixa em código, não configurável por arquivo:
- Ele reivindica a extensão
.proscenio, apresenta-se no dock Import do editor comoProscenio Charactere assa cada fonte para umPackedScene.scn. - Ele declara um preset,
Default, e não expõe opções por importação - a lista de opções está vazia, então o dock Import mostra o preset sem nada a ajustar. Uma reimportação é dirigida inteiramente pelo conteúdo do.proscenio, nunca pelas configurações do dock de importação. - Ele roda na ordem de importação
100, a ordem de importação de cenas da própria engine, então ele assa depois dos recursos de ordem padrão. Essa ordenação é estrutural: a assadura resolve o atlas e os PNGs por elemento através doResourceLoadercontra os arquivos irmãos, e na ordem padrão um.prosceniopoderia ser assado antes de suas texturas importarem e entregar uma cena com texturas em branco.
Elementos: malha e sprite
Cada entrada no array elements do documento carrega um type que seleciona o nó que um builder emite. Uma entrada sem type é lida como malha - o padrão vive na camada de leitura tipada, então um elemento sem tipo nunca é descartado.
Um elemento type: "mesh" vira um Polygon2D. O builder lê:
polygon- o anel de contorno, como pares[x, y].polygons- arrays opcionais de índices de vértice por face para malhas de múltiplas faces (triangulação de automesh, recortes de múltiplas ilhas); cada face é renderizada através dePolygon2D.polygons. Ausente ou vazio significa que o anelpolygonúnico é a forma inteira.uv- coordenadas normalizadas[0, 1], escaladas para pixels de textura em tempo de construção; uma malha sem textura mantém os valores brutos.weights- pesos de vértice por osso que fazem o skinning da malha (veja Roteamento de slots para como uma malha com skinning é parenteada).texture,texture_region,modulate,z_index- os campos de aparência compartilhados abaixo.
Um elemento type: "sprite" vira um Sprite2D. O builder lê:
hframes,vframes,frame- a grade de spritesheet e a célula exibida.centered,offset- o pivô e o offset em pixels do Sprite2D.texture_region- um sub-retângulo opcional do atlas, normalizado[0, 1]como as UVs de malha e convertido para pixels assim que o tamanho da textura é conhecido. A região só é habilitada quando há uma textura presente e o retângulo em pixels pode ser definido na mesma passada; um sprite sem textura é entregue com a região desabilitada (um retângulo habilitado de área zero não desenharia nada). Quando a região está habilitada, o builder também recorta o filtro de textura na borda da região para que os quadros vizinhos do atlas não vazem na emenda.flip_h,flip_v- as inversões horizontal e vertical exclusivas do Sprite2D.texture,modulate,z_index- os campos de aparência compartilhados abaixo.
modulate (um tingimento RGBA) e z_index (ordem de desenho) se aplicam a ambos os tipos; um modulate ausente mantém o branco padrão do Godot. Ambos os tipos resolvem sua textura da mesma forma, em ordem: o próprio nome de arquivo texture do elemento carregado ao lado do .proscenio; na falta disso, <nome do elemento>.png ao lado do .proscenio (nome de arquivo por convenção); na falta disso, o atlas de todo o documento.
Um sprite de múltiplos quadros é o caso por trás da nota de prévia abaixo.
Roteamento de slots e visibilidade padrão
Slots permitem que vários elementos compartilhem uma âncora e troquem em tempo de execução. O builder de slots roda antes dos builders de elementos, para que os elementos possam ser roteados sob um slot:
- Cada entrada no array
slotsdo documento vira umNode2Dnomeado a partir do slot. Ele é parenteado sob oBone2Dcorrespondente quando o slot nomeia umbone, ou sob a raizSkeleton2Dquandoboneestá vazio ou o osso nomeado está ausente (um osso ausente registra um aviso e ancora na raiz). - Todo nome no array
attachmentsdo slot é mapeado para aquele slot. Um elemento cujo nome está nos attachments de algum slot é reparenteado sob oNode2Ddo slot em vez de ser parenteado a um osso. - O
defaultdo slot nomeia o attachment mostrado em repouso: aquele único elemento começa visível, todo outro attachment do slot começa oculto, e uma track de animaçãoslot_attachmentos alterna em tempo de execução.
Um elemento que não está em nenhum slot recai em ser parenteado sob o Bone2D do seu bone (ou a raiz do esqueleto quando o osso está ausente), e permanece visível. Uma malha com skinning (uma que carrega weights) é a exceção a tudo isso: ela deve ser irmã do Skeleton2D, então é parenteada sob o pai do esqueleto (a raiz do rig) e nunca é roteada para um slot nem sob um osso; se o Skeleton2D não tiver pai, o builder pula aquela malha e registra um erro em vez de entregar uma forma colapsada.
Tracks de animação
As animações viram uma AnimationLibrary no AnimationPlayer, com cada animação do documento contribuindo com seu length e sua flag loop. O builder emite três tipos de track, selecionados pelo type de cada track; um tipo não reconhecido registra um aviso e é pulado:
bone_transform- mira umBone2De emite até três value tracks,position,rotationescale. Apenas os canais de fato presentes nas chaves são emitidos, então uma animação só de posição não registra um canal fantasma de rotação que zeraria a pose. A rotação interpola com o modo cúbico ciente de ângulo para dar a volta corretamente em +/-pi; posição e escala usam cúbico simples.sprite_frame- mira umSprite2D(e apenas um elemento sprite; um alvo não-sprite registra um erro) e faz chaves do seuframe. Índices de quadro são discretos, então a track usa interpolação por vizinho mais próximo, sem mistura.slot_attachment- mira umNode2Dde slot e emite uma track de visibilidade por attachment filho. A cada chave, o attachment nomeado na chave é o único visível; as trocas são cortes secos (interpolação por vizinho mais próximo), não misturas.
As tracks resolvem seus alvos por nome de folha através de find_child, e é por isso que os nomes dos nós são mantidos literalmente (veja Nomes de nós).
Nomes de nós
Os builders definem o nome de cada nó como o nome do elemento, literalmente. Quando dois irmãos colidem, o add_child do Godot acrescenta automaticamente um sufixo numérico (_001, _002, ...) - esse sufixo é do Godot, não algo que o plugin escreve. O importador nunca desambigua prefixando um tipo (nenhum sprite_foo / mesh_foo), porque as tracks de animação resolvem seus alvos por nome de folha através de find_child(target, ...): um prefixo mudaria o nome que uma track procura e quebraria a busca, enquanto um sufixo numérico só cai em um nome que já era ambíguo.
A prévia de sprite difere do Blender
Um elemento sprite de múltiplos quadros é renderizado no Godot como um Sprite2D mostrando um quadro em seu tamanho nativo em pixels (region_px / hframes), enquanto o Blender mostra o quad autorado inteiro. Isso é inerente ao modelo, não um bug de importação: prévias pixel-exatas de Blender e Godot não são alcançáveis para sprites de múltiplos quadros por design, o invariante é a geometria e os limites, não os pixels. A regra de autoria que mantém os limites casando, quad_units = frame_px / pixels_per_unit, vive com os fixtures (packages/fixtures/README.md, "Sprite quads (multi-frame)").
Veja Arquitetura para como o plugin se encaixa no pipeline, e a Referência de schema para o formato .proscenio que ele lê: a forma do documento, os contratos de campo de elementos e slots, e as tracks e chaves de animação.