Animação Roblox, headless
Primeira vez nesta máquina? Leia
LOCAL.mdnesta pasta. Se não existir, execute a skillroblox-skills-setup(ou copieLOCAL.template.mdparaLOCAL.mde preencha-a).LOCAL.mdcontém seus caminhos e localizações de ferramentas; é seu e nunca é compartilhado.
Crie animação como dados (uma especificação JSON de poses ao longo do tempo), asse-a em uma Ação real do Blender, renderize-a e veja o resultado antes de acreditar nele.
O ciclo é: criar → renderizar cada quadro → organizar em uma folha de contato → ver → corrigir → repetir. Tudo aqui existe porque alguma etapa deste ciclo capturou uma falha que todas as outras verificações perderam.
Leia isto primeiro: a armadilha que mais perde tempo
A maioria dos rigs do Roblox vem com uma Ação do Blender que contém a pose de repouso. O Blender reavalia essa Ação durante a renderização. Então, se você definir valores de pose e renderizar:
- cada sonda numérica relata que sua pose foi aplicada perfeitamente
- cada quadro renderizado mostra a pose padrão do rig
- os dados e a imagem nunca se reconciliam, e não há erro
Limpar a Ação também não ajuda, porque essa Ação É a pose de repouso — limpe-a e os membros colapsam no torso.
A correção: insira KEYFRAMES nessa mesma Ação, então execute frame_set() por quadro e renderize. A animação então é a Ação, nada pode sobrescrevê-la, ossos não tocados mantêm seus valores de repouso por extrapolação constante, e você termina com uma Ação real — que é o que uma exportação Roblox precisa de qualquer maneira. scripts/anim_render.py já faz isso.
Lista completa em references/traps.md — 28 delas. Leia antes de depurar qualquer coisa.
Crie em FK. Não em IK.
Uma KeyframeSequence do Roblox é uma árvore de rotações por junta, uma por Motor6D. Isso é FK. Cada rig R15 vem com um osso de controle por junta para corresponder. Controlar o personagem com alvos IK é fazer um round-trip por um solver que o Roblox nunca pediu, e pagar por isso:
- alvos IK apenas de posição deixam a ROLL do membro livre, então as partes Roblox bloco apresentam qualquer face que o solver escolher e saltam entre quadros. Em um avatar texturizado, isso se lê como cores mudando instantaneamente — o sintoma que faz uma animação parecer bugada
- o solver pula entre ramos igualmente bons quadro a quadro, então os membros saltam enquanto cada quadro ainda verifica perfeitamente
- é lento
FK não tem ramos, não tem singularidades, não tem ambiguidade de roll, é exatamente 1:1 em graus e exporta diretamente para o Roblox.
A maioria dos rigs vem em modo IK, o que faz todos os ossos FK_* parecerem inertes. Inverta o interruptor — não conclua que o FK está morto:
"properties": {"ARM_IK_FK.L": 0.0, "ARM_IK_FK.R": 0.0,
"LEG_IK_FK.L": 0.0, "LEG_IK_FK.R": 0.0}
Medido em um rig R15 gratuito da comunidade, e agradavelmente uniforme: cada membro descansa apontando reto para baixo; em cada osso FK_* X negativo balança para frente, Z positivo balança para a direita do personagem, 1:1 em graus, idêntico em ambos os lados — sem inversão de espelho. Ângulos filhos são relativos ao pai.
A lição geral, qualquer que seja o rig que você usar: se um osso de controle está inerte, procure um interruptor que o desabilita antes de descartá-lo. Rigs estilo Rigify R15 carregam propriedades personalizadas ARM_IK_FK / LEG_IK_FK que padronizam para 1.0 (IK). Defina-as para 0.0, adicione keyframes (para que frame_set não possa revertê-las), e crie em FK. As convenções de direção acima foram medidas em um rig; calibrate_rig.py e sweep_control.py as medem novamente no seu, então execute-os em qualquer rig que você não tenha usado. O caminho do seu rig .blend vai em LOCAL.md.
Recorra ao IK apenas quando um membro deve atingir um ponto exato no mundo. Então leia as armadilhas 12–15.
Faça parecer Roblox, não a referência
- 30 FPS, e um movimento de combate dura 0,3–0,7s. O padrão do Editor de Animação é 30fps. O tempo da vida real é muito lento para um jogo. Pegue a mecânica de como o movimento realmente funciona; pegue o tempo do Roblox.
- Cinco poses: idle, antecipação, contato, follow-through, recuperação.
- Facilidade por segmento —
"ease": "in"para acelerar no golpe,"out"para estabilizar. Um smoothstep fixo desacelera em cada keyframe, então o golpe desacelera para uma parada em seu próprio quadro de contato e tudo fica mole. - Exagere. A animação de combate real do Roblox vai muito além do alcance humano e lê-se pela silhueta.
- Renderize em cinza simples (
"plain": true). Uma textura de avatar movimentada transforma um rig bloco em uma confusão ilegível e você acaba depurando a fantasia em vez da pose. As animações reais do Roblox são exibidas em um manequim simples exatamente por esse motivo.
references/pose-recipes.md tem os números reais — tabelas de ângulos medidas para guarda, antecipação, chamber, contato e follow-through, o esqueleto de tempo e as duas regras que pegam as pessoas (ombro + cotovelo devem ficar abaixo de ~120 total; um chute alto precisa de UpperLeg X abaixo de −120 ou a perna se funde com o torso). Comece a partir delas e ajuste em relação à pré-visualização. Não as derive novamente.
Seja rápido — medido, não adivinhado
Todo o ciclo leva segundos. Se uma passagem leva minutos, algo está errado. Medido em um PC de médio porte, uma animação de 24–36 quadros:
| etapa | tempo | notas |
|---|---|---|
| Blender carrega o rig | 0,7s | a inicialização nunca é o problema |
| sonda numérica (mapa de controle FK inteiro) | 0,1s | faça perguntas ao rig com números |
renderização de pré-visualização ("preview": true) | 4–6,5s | 1 vista, 320x240, 1 amostra |
| renderização completa, 2 vistas @ 640x560 | 9–17s | faça isso uma vez, no final |
| codificação GIF | 0,3s | |
to_roblox.py → Luau | 0,1s | |
| construir + verificar no Studio | uma chamada execute_luau |
Tempo de máquina para uma animação finalizada no Studio: bem menos de um minuto. O que realmente custa tempo são as iterações de criação, então gaste-as na pré-visualização e nunca na renderização completa. Com references/pose-recipes.md para começar, um segundo movimento deve acertar em 1–3 pré-visualizações — o soco e o aceno em references/ estavam certos já na primeira tentativa.
"report": true imprime a posição de cada junta em cada batida: quando uma pose parece errada, nomeia a junta em uma execução em vez de cinco passagens de renderização adivinhando.
Não renderize para responder a uma pergunta que uma sonda pode responder.
Não negociáveis
Cada um está aqui porque pular algo enviou algo quebrado que parecia bom:
- Nunca confie em uma execução que saiu com 0.
anim_render.pyafirma a integridade do rig por quadro, afirma que os quadros foram escritos e termina com código diferente de zero caso contrário. - Veja os quadros. Toda passagem.
- Com uma referência, construa o lado a lado e julgue por ele — nunca apenas pela renderização.
- Inspecione o próprio GIF finalizado, não os PNGs a partir dos quais você o codificou.
- Nunca relaxe uma tolerância para transformar uma luz vermelha em verde. Corrija a especificação.
Fluxo de trabalho
1. Calibre o rig (qualquer rig que você não tenha usado)
blender --background <rig>.blend --enable-autoexec --python scripts/calibrate_rig.py
blender --background <rig>.blend --enable-autoexec --python scripts/sweep_control.py -- FK_UpperLeg.R:rot TORSO:rot
calibrate_rig.py relata a armação de deformação vs controle, a armadilha da Ação, o estado do interruptor IK/FK e quais controles realmente movem algo.
sweep_control.py fornece a direção, sinal e faixa utilizável de cada eixo — ele mede a direção do osso, bem como a posição, para que controles apenas de rotação não sejam perdidos.
Se os ossos FK_* parecerem inertes, isso é o interruptor IK/FK, não um rig morto.
2. Crie a especificação
fps, duration, w, h, margin, views, properties, plain, preview,
report, e keyframes[] de {t, ease, bones: {Bone: {rot|loc|target|aim}}}.
rot é o que você quer quase sempre. loc para controles sem efetor final (ROOT). target / aim são a saída de escape do IK — veja armadilhas 12–15.
Três coisas que dão errado toda vez se você as esquecer:
- Ângulos filhos FK se combinam com o pai. Ombro 50° + cotovelo 70° coloca o antebraço a 120° da vertical, apontando para cima sobre o ombro. Mantenha ombro + cotovelo abaixo de ~110° para uma guarda.
- Inclinação e torção vão em
UpTorso, que aciona apenas a parte superior do corpo.TORSOtambém aciona as pernas, então incliná-lo derruba a figura e desliza o pé plantado. O rig também amplifica:X = +15balança a cabeça 23°. - Contra-rotacione o tornozelo de apoio,
FK_Foot.X ≈ -(FK_UpperLeg.X + FK_LowerLeg.X), ou o pé inclina com a perna e o personagem parece que está em um declive.
Veja references/example-fk-roundhouse.json (chute de 0,8s) e references/example-fk-punch.json (cruzado de 0,5s), além de references/pose-recipes.md.
2a. SE UM ACESSÓRIO ESTÁ NA MÃO, PESQUISE COMO ELE É REALMENTE SEGURADO. PRIMEIRO.
Esta etapa acontece ANTES de você criar um keyframe, e não é opcional. No momento em que um clipe tem uma arma ou ferramenta, "a pose se lê" deixa de ser uma questão sobre o rig e se torna uma questão sobre esse objeto, que é um fato sobre o mundo e não algo para inferir de uma matriz.
A falha que isso existe para prevenir, de uma construção real: uma glaive foi criada com sua cabeça para BAIXO no quadril do portador. Cada etapa downstream então fez seu trabalho perfeitamente - o motor reproduziu a pegada criada, a faixa de pose correspondeu à folha de contato quadro a quadro, erro de pegada 0,000, zero amostras da arma dentro do corpo - e o jogo enviou uma alabarda carregada de cabeça para baixo. Uma pose errada criada aqui é invisível para todas as verificações depois dela, porque todas as verificações depois dela estão medindo concordância com ISSO. Quando um humano finalmente olhou, o veredito foi que estava de cabeça para baixo. Eles estavam certos, e o custo foi uma sessão inteira downstream de uma decisão tomada em dez segundos aqui.
Antes de criar:
- Pesquise como a classe de arma é carregada e usada. Fotografias de praticantes, não arte conceitual, não outros jogos. Dez segundos de leitura batem quatro rodadas de adivinhação de números.
- Escreva a resposta na especificação como
_intent, como uma frase sobre a arma: "cabeça para cima, haste cruzando o ombro de ataque", "lâmina angulada para cima e para frente a partir de uma mão abaixada". Essa frase é contra a qual a folha de contato será julgada - veja o passo 4. - Verifique as óbvias da lista abaixo antes de recorrer a uma pesquisa.
| classe | em repouso | em um balanço |
|---|---|---|
| alabarda, glaive, naginata, lança | cabeça para CIMA - apoiada no ombro, base plantada no chão, ou em guarda com a lâmina na altura do peito ou acima. Nunca pendendo aos pés | a cabeça lidera o arco; a haste cruza na frente do corpo, nunca no mesmo plano vertical que o braço |
| espada | angulada para cima e para frente a partir de uma mão abaixada, cerca de +35 graus. Reta para baixo lê-se como caída | a aresta lidera; o pulso não é uma dobradiça, todo o braço carrega o arco |
| machado, martelo | cabeça para cima ou apoiada no ombro, o peso carregado alto | a cabeça é a massa - ela chega por último e chega com mais força |
| arma de punho, manopla | vestida, não segurada; uma guarda de lutador, ambos os punhos para cima | os quadris a impulsionam, não o ombro |
Segure o acessório em uma pose que o próprio corpo do portador poderia atingir. Um transporte com uma mão de qualquer coisa mais longa que o personagem é alto não fica no mesmo plano que o braço, ou ele colide.
3. Renderize e verifique
blender --background <rig>.blend --enable-autoexec --python scripts/anim_render.py -- spec.json out/
ffmpeg -start_number 0 -i "out/side_%03d.png" -vf "scale=240:-1,tile=6x6" sheet.png
A câmera se enquadra nos limites de toda a animação e resolve sua distância do campo de visão, então nada sai do quadro; margin (padrão 1.10) é o único controle. Renderize pelo menos dois ângulos — um chute de perna direita é invisível da esquerda.
Então julgue a folha em relação à REFERÊNCIA, não em relação a si mesma. Abra a frase _intent do passo 3a e verifique a folha quadro a quadro contra ela: a cabeça está para cima, a lâmina está liderando, a haste cruza na frente do corpo. Uma folha de contato apenas prova que a renderização corresponde à especificação. Ela não pode dizer que a especificação estava correta, e todas as etapas downstream a tratarão como verdade absoluta - então este é o último ponto no pipeline onde uma pose errada é barata de corrigir. Se a folha e a referência discordarem, a referência vence e a especificação muda.
4. Entregue um GIF, depois verifique o GIF
ffmpeg -framerate 30 -start_number 0 -i "out/side_%03d.png" -framerate 30 -start_number 0 -i "out/tq_%03d.png" -filter_complex "[0]scale=420:-1[a];[1]scale=420:-1[b];[a][b]hstack=2,split[s0][s1];[s0]palettegen=stats_mode=diff[p];[s1][p]paletteuse=dither=bayer:bayer_scale=3" -loop 0 anim.gif
Leia a folha UMA VEZ, no tamanho que você precisa e não maior. Uma folha de contato é o objeto mais caro neste pipeline: uma imagem é reenviada em cada chamada de ferramenta posterior no mesmo contexto, então uma leitura de folha em resolução total cedo custa centenas de milhares de tokens até o final de uma execução. Reduza-a, corte para o membro em questão quando essa for a questão, e não a reabra para verificar novamente algo que você poderia ter medido. Onde uma verificação pode ser um NÚMERO - a arma sai da mão, ela passa através de um membro, em que ângulo ela está - torne-a um número e leia uma linha de texto.
Em seguida, extraia quadros de volta do GIF finalizado e veja-os — o movimento se lê de forma diferente de imagens estáticas, e as configurações de codificação falham silenciosamente:
ffmpeg -i anim.gif -vf "select='not(mod(n,2))',tile=4x3" -vsync 0 verify.png
Um clipe criado quebra aqueles que você não criou
O script Animate padrão do Roblox toca idle, andar, correr, pular, cair e escalar com prioridade Core. Qualquer coisa que você criar e tocar com prioridade Idle ou superior as mascara em toda junta que poseia, enquanto estiver tocando.
Então um loop idle de corpo inteiro — a correção correta para um personagem parado com uma arma — impede as pernas de se moverem quando o jogador anda, porque seu clipe está poseando as pernas em uma prioridade mais alta que o ciclo de andar. Parece exatamente "a animação de andar está faltando", e aparece apenas no personagem para o qual você criou o idle, o que faz parecer