Cómo mover y girar objetos suavemente con TweenService

Claim Your Loot

·

Comparación de las curvas Linear, Sine, Quad, Back y Bounce de TweenService

Quieres que una puerta se abra despacio, que una plataforma suba y baje, o que una moneda gire. Lo intentas con un bucle y wait(), y te queda un movimiento a tirones que además va distinto en cada ordenador.

La solución está integrada en Roblox y se llama TweenService. Le dices dónde está el objeto, dónde quieres que acabe y cuánto debe tardar, y él calcula todos los pasos intermedios. En la demo de más abajo puedes trastear con las curvas y ver el código que sale.

Respuesta rápida

local TweenService = game:GetService("TweenService")
local info = TweenInfo.new(2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local tween = TweenService:Create(parte, info, {Position = Vector3.new(0, 20, 0)})
tween:Play()

Tres líneas: la configuración del movimiento, el tween con el destino, y darle al play.

Lo que necesitas antes de empezar

  • Un Part en el Workspace, Anchored marcado.
  • Un Script en ServerScriptService.
  • Unos 20 minutos.

Por qué no vale hacerlo con un bucle

Esto es lo que hace todo el mundo la primera vez:

-- MAL: a tirones y con velocidad distinta en cada máquina
for i = 1, 100 do
	parte.Position = parte.Position + Vector3.new(0, 0.2, 0)
	task.wait(0.03)
end

Dos problemas. El primero es que task.wait(0.03) no espera 0,03 segundos exactos: espera al menos eso, y luego hasta el siguiente fotograma. En un ordenador lento el movimiento tarda el doble.

El segundo es que va a velocidad constante, y eso se ve artificial. En la vida real las cosas arrancan despacio y frenan al llegar. Eso es lo que hacen las curvas de aceleración.

Comparación de las curvas Linear, Sine, Quad, Back y Bounce de TweenService

Paso 1: TweenInfo, la configuración

TweenInfo.new() acepta seis parámetros y todos menos el primero son opcionales:

local info = TweenInfo.new(
	2,                              -- duración en segundos
	Enum.EasingStyle.Quad,          -- la curva
	Enum.EasingDirection.Out,       -- dónde se aplica la curva
	0,                              -- repeticiones (-1 = infinito)
	false,                          -- ¿vuelve al punto de partida?
	0                               -- retraso antes de empezar
)

Los dos que de verdad cambian cómo se siente el movimiento son la curva y la dirección:

CurvaCómo se siente
LinearVelocidad constante. Robótico, útil para cosas mecánicas
SineSuave y discreto. El más seguro si dudas
Quad, Cubic, QuartCada vez más pronunciado
BackSe pasa un poco y vuelve. Bueno para interfaces
BounceRebota al llegar, como una pelota
ElasticOscila como un muelle
DirecciónQué hace
InArranca lento y acelera
OutArranca rápido y frena al final. El más natural para casi todo
InOutLento al principio y al final

Trastea con la demo hasta que entiendas la diferencia: se explica sola mucho mejor que cualquier tabla.

Los seis parámetros de TweenInfo.new en Roblox

Paso 2: crear y reproducir

-- Script en ServerScriptService
local TweenService = game:GetService("TweenService")
local parte = workspace:WaitForChild("Plataforma")

local info = TweenInfo.new(2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)

-- El tercer parámetro es una tabla de propiedades DESTINO
local tween = TweenService:Create(parte, info, {
	Position = parte.Position + Vector3.new(0, 15, 0)
})

tween:Play()

La tabla del final es la clave: pones el valor final, no el desplazamiento. TweenService se encarga de todo lo que hay en medio.

Paso 3: varias propiedades a la vez

Puedes animar todo lo que sea un número, un Vector3, un Color3 o un UDim2, y varias cosas a la vez:

local tween = TweenService:Create(parte, info, {
	Position = Vector3.new(0, 20, 0),
	Transparency = 0.5,
	Color = Color3.fromRGB(255, 100, 100),
	Size = Vector3.new(10, 1, 10)
})
tween:Play()

Todas empiezan y acaban a la vez, con la misma curva.

No se puede animar CFrame y Position en el mismo tween. Si necesitas mover y girar a la vez, usa solo CFrame:

local destino = CFrame.new(0, 20, 0) * CFrame.Angles(0, math.rad(90), 0)
local tween = TweenService:Create(parte, info, {CFrame = destino})

Paso 4: ida y vuelta, y en bucle

Con los parámetros 4 y 5 tienes movimientos que se repiten sin escribir un bucle:

-- Plataforma que sube y baja para siempre
local info = TweenInfo.new(
	3,                          -- 3 segundos cada trayecto
	Enum.EasingStyle.Sine,
	Enum.EasingDirection.InOut,
	-1,                         -- infinito
	true,                       -- vuelve al origen
	0
)

local tween = TweenService:Create(parte, info, {
	Position = parte.Position + Vector3.new(0, 10, 0)
})
tween:Play()

Con true en «vuelve al origen», un ciclo son 6 segundos: 3 de subida y 3 de bajada.

Paso 5: esperar a que acabe

tween:Play() no bloquea: el código sigue a la línea siguiente mientras el objeto se mueve. Si necesitas encadenar cosas:

tween:Play()
tween.Completed:Wait()      -- ahora sí espera
print("ya ha llegado")

O sin bloquear:

tween.Completed:Connect(function()
	print("ya ha llegado")
end)

Pruébalo aquí

El mismo tween, distinta curva

Cambia los ajustes, dale a reproducir y mira el TweenInfo que sale.

Las fórmulas son las mismas que usa Roblox, así que lo que ves aquí es lo que verás en Studio.

Errores habituales

El objeto no se mueve

Casi siempre es que no está Anchored. Un Part sin anclar lo controla la física, y la física gana: el tween tira de él y la gravedad lo devuelve. Marca Anchored.

Unable to cast value to Vector3

Le estás pasando un número donde va un Vector3. {Position = 10} está mal; es {Position = Vector3.new(0, 10, 0)}.

El tween va bien la primera vez y luego se rompe

Estás creando el tween una vez y llamando a :Play() muchas. Un tween guarda el punto de partida de cuando lo creaste. Si el objeto se ha movido, hay que crear uno nuevo cada vez.

Tween:Play() is not a valid member

Te has saltado el TweenService:Create(...) y estás llamando a :Play() sobre el TweenInfo. El TweenInfo es solo la configuración; el tween es lo que devuelve Create.

Se mueve en el cliente pero los demás no lo ven

El tween lo lanzaste desde un LocalScript. Si el movimiento tiene que verlo todo el mundo, va en el servidor. Si es solo un efecto visual para ese jugador, déjalo en el cliente y ahorras red.

Preguntas frecuentes

¿Puedo parar un tween a medias?

Sí: tween:Cancel() lo para y deja el objeto donde esté, y tween:Pause() lo congela para poder reanudarlo con :Play().

¿Cuántos tweens puedo tener a la vez?

Muchos, pero cada uno cuesta. Para decenas de objetos moviéndose igual, sale más barato mover un Model entero por su PrimaryPart.

¿Sirve para la interfaz?

Sí, y es donde mejor queda. Anima Position y Size de tipo UDim2 en los elementos de GUI. Ahí Back y Elastic lucen mucho.

¿Y si quiero un recorrido con curvas, no en línea recta?

TweenService va siempre en línea recta entre dos puntos. Para trayectorias curvas necesitas encadenar varios tweens o calcular puntos intermedios tú.


Última revisión: 1 de septiembre de 2026

Claim Your Loot

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *