Animação para Roblox

IKControl: fazer a mão do personagem alcançar um objeto sem animar o braço

Cinemática inversa deixa você posar vários ossos mexendo em um só. O IKControl traz isso para fora do editor de animação, em tempo de jogo — e tem quatro propriedades que precisam estar certas.

por Gustavo Cantino9 min de leituraIntermediário

Produzido com IA a partir das fontes citadas abaixo, e conferido por checagens automáticas contra essas mesmas fontes. Nenhuma pessoa leu este texto antes da publicação. Política Editorial

Placa tipográfica com a categoria Animação para Roblox em destaque, o nível intermediário abaixo, e um campo de barras verticais verde-limão sobre fundo escuro.

Fazer um personagem encostar a mão numa maçaneta parece um problema de animação, e é — até a maçaneta mudar de altura. Aí a animação que encaixava numa porta não encaixa na outra, e a solução vira uma animação por porta.

Cinemática inversa resolve isso invertendo a pergunta. Em vez de "que ângulos os ossos precisam ter", ela responde "onde a mão precisa estar" — e calcula os ângulos sozinha.

O problema

A definição oficial é curta: cinemática inversa é uma técnica comum de animação para fazer personagens se moverem e interagirem com o ambiente de forma realista.

O motivo de existir também está escrito, e é honesto sobre o trabalho que ela substitui: criar movimento realista à mão costuma exigir muitas iterações e ajustes finos de várias juntas. Quem já tentou posar um braço osso por osso — ombro, cotovelo, pulso, e de volta ao ombro porque o resultado ficou torto — reconhece a descrição.

A promessa é a inversão: com IK, dá para posar e animar várias partes do personagem ajustando um único objeto.

A documentação dá três exemplos, e eles cobrem bem o espaço de uso:

  • Posar a mão ajustando automaticamente os membros ligados — pulso, cotovelo e ombro.
  • Fazer os pés interagirem de forma realista com superfícies e inclinações diferentes.
  • Mover um único objeto alvo para criar rápido interações realistas com personagem e adereços.

O segundo é o que mais muda a percepção de qualidade num jogo, e o que menos gente implementa: personagem parado numa rampa com os dois pés na mesma altura é uma das coisas que faz um jogo parecer rígido, e quase ninguém consegue nomear o que está errado.

Passo a passo

O IKControl acrescenta IK aos rigs de forma procedural, fora do editor de animação. Isso é o ponto central: ele funciona em tempo de jogo, reagindo ao mundo, e não numa animação gravada.

A documentação avisa antes de listar: as propriedades obrigatórias precisam estar certas para evitar resultado inesperado e não natural. São quatro.

Type especifica o tipo de comportamento do controle. Os tipos comuns são Position e Transform. A diferença prática: Position faz o efetor perseguir a posição do alvo; Transform persegue posição e orientação. Mão que segura uma alavanca precisa estar na posição certa e girada certo — é Transform. Mão que aponta para um ponto é Position.

EndEffector é a BasePart ou Bone do rig que persegue o alvo. No exemplo oficial, o osso da mão esquerda como efetor, para alcançar uma maçaneta.

Target é o objeto que o efetor alcança ou aponta. E a definição é generosa: pode ser qualquer objeto com posição no mundo — uma parte, um attachment, o que existir ali.

ChainRoot define a cadeia de partes ou ossos que o controle afeta. Todas as partes conectadas entre o ChainRoot e o EndEffector são afetadas.

Essa última é a que dá controle fino sobre o resultado, e o exemplo da documentação explica melhor que qualquer descrição: pondo o ChainRoot no braço superior esquerdo, o IK vale para o braço esquerdo inteiro; pondo no antebraço esquerdo, vale só do cotovelo para baixo.

É uma escolha de estilo, não de correção. Cadeia longa alcança mais longe e mexe no ombro, o que pode brigar com a animação que está tocando. Cadeia curta preserva a pose do ombro e alcança menos.

A regra de bolso que funciona: comece pela cadeia mais curta que resolve o caso, e só aumente se o alcance faltar. Cada osso a mais na cadeia é um osso a mais que o IK pode posar de um jeito que você não previu — e é sempre o ombro, sempre numa animação específica, sempre depois que o jogo já está no ar.

Há também uma consequência que só aparece com o rig em movimento. Uma cadeia que inclui o ombro compete com a animação do tronco: o personagem que anda e alcança ao mesmo tempo tem o ombro puxado pelos dois lados, e o resultado é um balanço que não vem de lugar nenhum. Cadeia curta não tem esse problema porque o ombro continua sendo da animação.

Para testar, a documentação sugere um caminho que dispensa qualquer script: dá para acrescentar e editar IKControl por script ou direto pelo Explorer — e dá até para acrescentar e mudar durante um teste de jogo. Basta iniciar o teste, achar o modelo do seu personagem na Workspace (ele tem o nome da sua conta Roblox), acrescentar um Attachment à raiz, posicioná-lo à frente, e criar o IKControl no Humanoid apontando para ele.

Artefato

O script abaixo cria o IKControl em tempo de jogo e faz a mão direita alcançar o objeto marcado mais próximo, soltando quando ele se afasta.

Duas decisões dele valem explicação. A primeira: o controle nasce desligado. Sem alvo não há o que alcançar, e um IK ligado sem alvo produz uma pose arbitrária.

A segunda é a que separa IK que parece bom de IK que parece defeito: o peso sobe aos poucos. Ligar o controle de uma vez faz o braço saltar para a posição, no mesmo quadro. É o efeito mais artificial que o IK consegue produzir, e é a primeira coisa que se nota num jogo.

Erro comum

O erro visualmente mais chamativo é o cotovelo dobrando para o lado errado.

Isso acontece porque, matematicamente, há mais de uma forma de a mão chegar ao ponto pedido — e nada no cálculo sabe que cotovelos humanos só dobram num sentido. O resultado é um braço que alcança o alvo com perfeição e parece quebrado.

A solução oficial são as restrições: elas servem para limitar como as juntas se movem ao alcançar o alvo, e é isso que garante que cotovelo e joelho dobrem de forma natural.

Mas aqui está o detalhe que faz as pessoas desistirem: restrição e IK só conversam se três condições forem cumpridas, e uma delas é geométrica e não perdoa.

A primeira é de parentesco — a restrição e o IKControl precisam compartilhar o mesmo modelo pai.

A segunda é sobre os attachments referenciados pela restrição: eles precisam estar nas mesmas partes que o Part0 e o Part1 do Motor6D correspondente.

A terceira é a exigente: as posições relativas dos attachments da restrição precisam ser iguais às posições correspondentes nos CFrames C0 e C1 do Motor6D. Não "parecidas" — iguais. Um attachment posicionado no olho, arrastado com o mouse até ficar visualmente certo, quase certamente não satisfaz isso. E o sintoma de não satisfazer não é um erro: é uma junta que se comporta de um jeito inexplicável.

A forma de acertar é calcular a posição a partir do Motor6D, em vez de posicionar à mão. É o tipo de coisa que se resolve com um script de configuração rodado uma vez sobre o rig, e que nunca se resolve arrastando attachments no viewport.

Vale dizer isso com todas as letras porque é o ponto em que a maioria abandona as restrições: elas não são difíceis de entender, são difíceis de posicionar. Quem tenta no olho conclui que restrição não funciona, e volta a conviver com o cotovelo invertido.

O segundo erro comum é o IK brigando com a animação. O controle mexe nos mesmos ossos que a animação que está tocando, e quando os dois discordam o resultado é um membro tremendo. A saída é o peso: em vez de ligado ou desligado, uma transição, e um peso menor que o máximo quando se quer influência parcial em cima da animação.

O terceiro é esperar que o IK alcance o inalcançável. Se o alvo está mais longe que o braço, a cadeia estica até o limite e fica ali, rígida. Conferir a distância antes de ligar — como o artefato faz — evita a pose de boneco esticado.

Próximo passo

O uso de maior retorno visual não é a mão: são os pés. Personagem parado numa rampa com os dois pés na mesma altura, ou flutuando um centímetro acima do degrau, é um defeito que o jogador sente sem saber nomear.

A receita é a mesma do braço, com um raycast por pé para achar a altura do chão e um alvo posicionado ali. A cadeia curta costuma bastar, e o peso proporcional ao quanto o personagem está parado evita que o efeito brigue com a animação de caminhada.

Depois disso, vale investir nas restrições. Elas dão trabalho por causa das exigências de posição, e são o que separa um personagem que alcança coisas de um personagem que parece alcançar coisas. A documentação descreve o processo com uma restrição de dobradiça no cotovelo e uma de esfera no pulso, que é o par que resolve a maior parte dos braços.

Fontes

Como corrigimos erros

Artefato pronto pra usar

Criar IKControl em tempo de jogo para a mão alcançar um alvo próximo (ServerScriptService)
lua
-- ServerScriptService: faz a mão do personagem alcançar o objeto mais
-- próximo marcado como alcançável, e soltar quando ele fica longe.
--
-- As quatro propriedades obrigatórias, e o que cada uma decide:
--   Type       -> COMO alcançar (Position segue só a posição;
--                 Transform segue posição E orientação)
--   EndEffector-> QUEM alcança  (a mão)
--   Target     -> O QUE alcançar (qualquer objeto com posição no mundo)
--   ChainRoot  -> ATÉ ONDE a cadeia vai (o ombro = braço inteiro;
--                 o antebraço = só do cotovelo para baixo)

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")

local DISTANCIA = 6 -- studs: até onde o braço tenta alcançar
local TAG = "Alcancavel"

local function alvoMaisProximo(raiz)
	local melhor, melhorDist = nil, DISTANCIA
	for _, objeto in CollectionService:GetTagged(TAG) do
		local posicao = objeto:IsA("BasePart") and objeto.Position
			or (objeto:IsA("Attachment") and objeto.WorldPosition)
		if posicao then
			local d = (posicao - raiz.Position).Magnitude
			if d < melhorDist then
				melhor, melhorDist = objeto, d
			end
		end
	end
	return melhor
end

local function prepararBraco(character)
	local humanoid = character:WaitForChild("Humanoid")
	local raiz = character:WaitForChild("HumanoidRootPart")
	local mao = character:WaitForChild("RightHand", 10)
	local ombro = character:WaitForChild("RightUpperArm", 10)
	if not mao or not ombro then
		-- rig sem essas partes: provavelmente não é R15
		warn("rig sem RightHand/RightUpperArm: IK nao aplicado")
		return
	end

	-- O IKControl mora no Humanoid (ou no AnimationController).
	local ik = Instance.new("IKControl")
	ik.Name = "AlcancarComADireita"
	ik.Type = Enum.IKControlType.Position
	ik.EndEffector = mao
	ik.ChainRoot = ombro
	ik.Enabled = false -- nasce desligado: sem alvo, não há o que alcançar
	ik.Parent = humanoid

	local conexao
	conexao = RunService.Heartbeat:Connect(function()
		if not character.Parent or humanoid.Health <= 0 then
			conexao:Disconnect()
			ik:Destroy()
			return
		end

		local alvo = alvoMaisProximo(raiz)
		if alvo then
			ik.Target = alvo
			-- Weight sobe aos poucos: ligar de uma vez faz o braço
			-- SALTAR para a posição, que é o efeito mais artificial
			-- que o IK consegue produzir.
			ik.Enabled = true
			ik.Weight = math.min(ik.Weight + 0.08, 1)
		else
			ik.Weight = math.max(ik.Weight - 0.08, 0)
			if ik.Weight <= 0 then
				ik.Enabled = false
			end
		end
	end)
end

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(prepararBraco)
end)
Neste artigo

Continue por aqui