< Volver 

Blocks Simulator: Rotate

2022-12-26

La herramienta de rotar gira un bloque alrededor de sus propios ejes con arcos y ajusta el ángulo al incremento más cercano. Este artículo cubre el orden de multiplicación que elige los ejes, el redondeo, por qué al servidor le bastan tres números y por qué los tres campos de ángulo son tres tipos distintos de rotación.

LuauRobloxGameplayMath
Ir a vista rápida

Girar un bloque sobre sus propios ejes.

Resumen. La herramienta de rotar (C) pone tres arcos en el bloque seleccionado, uno por cada eje del propio bloque. Arrastrar un arco gira el bloque alrededor de ese eje, y el ángulo se ajusta al múltiplo más cercano de un incremento, 15° por defecto. El código es corto. Guarda el CFrame del bloque cuando se presiona el botón y lo multiplica por la derecha por una rotación del ángulo ajustado, y ese orden es lo que hace que el bloque gire sobre sus propios ejes y alrededor de su centro. En el script dejé un comentario que dice “this rotation system was too complex for me to do, #stackoverflow4ever”, y la función de ajuste es la misma en la versión que escribí como juank, así que esta parte no la deduje yo. Este artículo explica por qué funciona, qué recibe el servidor (tres números) y por qué le alcanza, y qué hacen de verdad los tres campos de ángulo de la ventana, que resultaron ser tres cosas distintas.

1. El problema

La herramienta usa ArcHandles. Cuando se arrastra un arco, reportan su eje y el ángulo girado desde que se presionó el botón, en radianes. El bloque tiene que girar ese ángulo, ajustado, alrededor de su propio eje, y quedarse donde está, porque un bloque que orbita alrededor del origen del mundo cuando lo rotas no sirve para nada.

2. El orden de la multiplicación

Un CFrame es una posición 𝐩 y una rotación R. Al componer dos, se aplica primero el de la derecha, en las coordenadas del de la izquierda:

(𝐩,R)⋅(𝟎,Q)=(𝐩, RQ)

Multiplicar el CFrame del bloque por la derecha por una rotación pura Q deja la posición como estaba, así que el bloque gira alrededor de su centro, y aplica Q en los ejes del propio bloque: CFrame.Angles(θ, 0, 0) a la derecha lo gira alrededor de su propio eje X, apunte a donde apunte ese eje en el mundo. Multiplicar por la izquierda,

(𝟎,Q)⋅(𝐩,R)=(Q𝐩, QR)

giraría alrededor de los ejes del mundo y además haría orbitar la posición alrededor del origen. La herramienta hace lo primero:

arrows.Adornee.CFrame = cf * CFrame.Angles(unpack(Vector(axis, angle)))

donde cf es el CFrame guardado al presionar. Igual que en la herramienta de mover, el resultado es absoluto, calculado desde el estado inicial y el ángulo total, así que no se desvía y no depende de cómo llegó el mouse.

3. Ajustar al más cercano

Vector(axis, angle) pasa el ángulo a grados, lo redondea al múltiplo más cercano del incremento, lo devuelve a radianes y lo pone en la casilla que toca de una tabla que unpack reparte en CFrame.Angles:

local function Round(num)
	return math.floor((num / value.Value) + 0.5) * value.Value
end
local Vector = function(axis, delta)
	local arg = rad(Round(deg(delta)))

	return axis == X_AXIS and ({arg, 0, 0})
		or axis == Y_AXIS and ({0, arg, 0})
		or axis == Z_AXIS and ({0, 0, arg});
end

Con θ el ángulo en grados e i el incremento:

θs=i⌊θi+12⌋,|θs−θ|≤i2

El error es como mucho medio incremento hacia cualquier lado, así que girar un arco hacia adelante o hacia atrás se siente igual, a diferencia del ceil de la herramienta de mover. El incremento llega hasta 360°. Un campo vacío equivale a 15°, y “0” a 0.01°.

4. Tres números al servidor

Al soltar, la herramienta le manda a RotateRep el bloque y su Orientation, tres ángulos en grados, y el servidor asigna Orientation. No manda la posición y no hace falta: como muestra la sección 2, rotar no la cambió, así que la posición que tiene el servidor sigue siendo la correcta.

Orientation es un conjunto de ángulos de Euler que Roblox aplica en el orden Y, X, Z:

R=Ry(ψ)Rx(ϕ)Rz(ρ)

Leer Orientation descompone la rotación del bloque en esos tres ángulos, y asignarla reconstruye la rotación a partir de ellos. Probé la ida y vuelta en un bloque con una rotación arbitraria y los vectores de dirección volvieron con una diferencia de 4×10−6, que es la precisión de los floats. Los ángulos de Euler tienen configuraciones donde la descomposición no es única (cuando el ángulo X es ±90°, las rotaciones en Y y en Z giran alrededor del mismo eje), pero cualquiera de los tríos posibles reconstruye la misma rotación, así que aquí eso no rompe nada. Es el mensaje más pequeño de las tres herramientas de transformación, 3 números contra los 12 de un CFrame, y es correcto, cosa que no creo que hubiera podido justificar en ese entonces.

5. Los campos son tres rotaciones distintas

La ventana muestra la Orientation del bloque en tres campos. Escribir un ángulo asigna ese componente y lo manda al servidor. Por el orden en que se aplica Orientation, los tres campos no giran el bloque como lo hacen los arcos, ni lo giran igual entre ellos.

  • Cambiar Y cambia el factor de más a la izquierda: Ry(ψ+δ)RxRz=Ry(δ)R. Es una rotación alrededor del eje vertical del mundo.
  • Cambiar Z cambia el factor de más a la derecha: RyRxRz(ρ+δ)=RRz(δ). Es una rotación alrededor del propio eje Z del bloque, lo mismo que hace el arco Z.
  • Cambiar X queda en medio: RyRx(ϕ+δ)Rz=RyRx(δ)Ry−1R, una rotación alrededor del eje X del mundo después de girarlo con el ángulo ψ. Coincide con el arco X solo cuando ρ=0.

Lo comprobé con un bloque en una orientación de (10,40,25) y un cambio de 30°. El campo Y coincide con una rotación alrededor del eje Y del mundo con una diferencia menor a 10−7, el campo Z coincide con el arco Z, y el campo X se aleja del arco X hasta 0.22 en sus vectores de dirección, mientras que coincide exacto con la rotación alrededor del eje X girado. Nada de esto es un bug del código. Así funcionan los ángulos de Euler, y la ventana muestra los mismos tres números que el panel de propiedades de Studio. Pero un jugador que rota con los arcos y luego escribe en los campos está mezclando dos tipos de rotación sin saberlo.

Los botones de borrar que hay al lado de los campos le mandan al servidor la orientación con ese componente en cero, sin cambiar el bloque en local, así que el cambio aparece cuando la replicación lo trae de vuelta.

6. La primera versión

La versión que escribí como juank tenía las mismas dos funciones, con nombres en español (redondear y vector), y la misma multiplicación. Lo que cambió en la reescritura es lo que las rodea. La vieja era un Tool, conectaba los eventos del mouse cada vez que se equipaba y disparaba el remote con la Orientation en cada evento de arrastre. Su ciclo para leer el incremento corría until sen == sen+1, una condición que nunca se cumple, como forma de repetir para siempre.

7. Limitaciones

  • Lo que se ajusta es el ángulo girado, no la orientación final. Un bloque en 7° con incremento de 15° pasa a 22°, 37° y así, nunca a 15° ni a 30°.
  • El servidor acepta cualquier orientación.
  • Los campos y los arcos son operaciones distintas, como explica la sección 5, y nada en la ventana lo dice.

8. Lo que me dejó

Es la herramienta que menos entendía cuando la escribí, y el comentario lo dice. Mirándola ahora, todo cabe en una regla: multiplicar por la derecha trabaja en los ejes del bloque, y multiplicar por la izquierda en los del mundo. La misma regla explica por qué al servidor le bastan tres números, y por qué el campo Y gira el bloque alrededor del mundo mientras el campo Z lo gira alrededor de sí mismo.

Este texto de Mastedore. Todos los derechos reservados.