Quat (rotaciones)
Rotaciones sin bloqueo de cardán: mirar hacia, girar con velocidad fija, interpolar.
Un Quat (cuaternión) guarda una rotación sin los problemas de los ángulos de Euler (bloqueo de cardán,
saltos al pasar de 359 a 0 grados). Es lo que usa el motor por dentro. No hace falta entender sus números:
se crean con las funciones de abajo y se combinan con *.
local q = Quat.euler(0, 90, 0) -- 90 grados sobre Y
local dir = q * Vec3.forward -- girar un vector: (-1, 0, 0)
local dos = q * Quat.angleAxis(30, Vec3.right) -- primero 30 sobre X, luego q
self.entity.quaternion = Quat.lookRotation(objetivo - self.entity.position)
print(self.entity.quaternion:toEuler()) -- en grados, como entity.rotation
Crear rotaciones
| Función | Descripción |
|---|---|
Quat.identity, Quat() | Sin giro. |
Quat.euler(x, y, z), Quat.euler(Vec3) | Desde grados, en el mismo orden que entity.rotation (Z, luego X, luego Y, como Unity). |
Quat.angleAxis(grados, eje) | Un giro de tantos grados alrededor de un eje. |
Quat.lookRotation(dirección [, arriba]) | La rotación que mira hacia dirección (su forward apunta allí). arriba es Vec3.up si no se da. |
Quat.fromToRotation(desde, hasta) | El giro más corto que lleva una dirección a otra (por ejemplo, alinear con la normal del suelo). |
Quat(x, y, z, w) | Con sus cuatro números (casi nunca hace falta). |
Combinar y girar vectores
| Operación | Descripción |
|---|---|
q * v | Gira el vector v. |
q1 * q2 | Una rotación tras otra: primero q2 y después q1. |
q:inverse() | La rotación contraria (q:inverse() * q = sin giro). |
q:forward(), q:right(), q:up() | Los ejes de esa rotación. |
q:toEuler() | Grados (Vec3), como entity.rotation. |
q:normalized(), q:copy() | Normalizada y copia independiente. |
a == b, tostring(q) | Misma rotación (con un margen pequeño) y texto. |
Interpolar y medir
| Función | Descripción |
|---|---|
Quat.slerp(a, b, t) | Rotación intermedia a velocidad constante (t de 0 a 1). |
Quat.lerp(a, b, t) | Parecido y más rápido; para ángulos pequeños no se nota la diferencia. |
Quat.rotateTowards(desde, hasta, grados) | Gira hacia hasta como mucho grados: una velocidad de giro fija. |
Quat.angle(a, b) | Grados que separan dos rotaciones. |
Quat.dot(a, b) | Producto escalar (1 o -1: la misma rotación). |
En una entidad
entity.quaternion es el giro local de la entidad como Quat (lectura y escritura). Es lo mismo
que entity.rotation, pero sin pasar por grados: úsalo para interpolar o acumular giros sin saltos.
-- Girar 90 grados por segundo sobre su propio eje Y (acumulando sin errores)
function Molino:Update(dt)
self.entity.quaternion = self.entity.quaternion * Quat.angleAxis(90 * dt, Vec3.up)
end
Ejemplo: torreta que apunta
Gira hacia el jugador con una velocidad máxima y dispara cuando lo tiene casi delante.
Torreta.lua
local Torreta = { properties = { velocidadGiro = 120.0, alcance = 25.0 } }
function Torreta:Start()
self.jugador = Scene.find("Jugador")
end
function Torreta:Update(dt)
if not self.jugador then return end
local hacia = self.jugador.position - self.entity.position
if hacia:length() > self.alcance then return end
local objetivo = Quat.lookRotation(hacia)
self.entity.quaternion = Quat.rotateTowards(self.entity.quaternion, objetivo, self.velocidadGiro * dt)
if Quat.angle(self.entity.quaternion, objetivo) < 3 then
Debug.log("¡Fuego!")
end
end
return Torreta
La torreta debe estar en la raíz de la escena (o su padre sin girar):
quaternion es el giro local.
Todos los ángulos de la API van en grados; solo Mathf.sin,
Mathf.cos... usan radianes, como en math.
¿Algo no queda claro o falta algo? Pregunta en el Discord.