Este artículo aborda el uso de sensores nativos del teléfono para controlar la rotación de una cámara virtual en Unity, incluyendo la calibración hacia el norte verdadero, compatible con Android e iOS sin necesidad de plugins adicionales como ARKit o ARCore.
Planteamiento del problema
En aplicaciones donde se requiere que la orientación del dispositivo móvil coincida con la del mundo físico, es esencial sincronizar la rotación de la cámara virtual con los ejes del entorno real. Soluciones comunes basadas en sensores pueden no alinearse correctamente con el norte geográfico, especialmente en dispositivos iOS, lo que resulta en una experiencia inmersiva incompleta.
Situación actual
Las implementaciones disponibles presentan limitaciones significativas: suelen ser obsoletas, con compatibilidad restringida a versiones antiguas de Unity o iOS; algunos enfoques introducen complejidad innecesaria; y en iOS, a menudo carecan de calibración norte-sur. Además, muchas soluciones dependen de plugins externos o no manejan adecuadamente los cambios de orientación de pantalla.
Objetivos específicos
La solución debe lograr: compatibilidad multiplataforma (Android/iOS), integración de calibración hacia el norte, independencia de plugins, facilidad de implementación medinate un solo script adjunto a la cámara, y soporte para diversas orientaciones de pantalla.
Causa técnica
En Android, la interfaz de Unity Input.gyro.attitude ya fusiona datos del magnetómetro, proporcionando alineación directa con el entorno físico. En iOS, esta fusión no ocurre automáticamente; se requiere integrar manualmente los datos de la brújula (Input.compass.trueHeading o Input.compass.magneticHeading). Sin embargo, estos valores dependen de la orientación de la pantalla y pueden ser inexactos si el dispositivo no está nivelado.
Solución propuesta
El siugiente código en C# realiza la calibración necesaria para iOS, manteniendo el comportamiento estándar en Android. Se utilizan sensores giroscópicos y de brújula, con correcciones para el sistema de coordenadas y suavizado de rotaciones.
public class ControlCamaraGiroscopio : MonoBehaviour { private double _timestampUltimaLectura = 0; private Quaternion _ajusteActual = Quaternion.identity; private Quaternion _ajusteObjetivo = Quaternion.identity;
void Iniciar()
{
Input.location.Start();
Input.gyro.enabled = true;
Input.compass.enabled = true;
}
void Actualizar()
{
Quaternion lecturaGiro = Input.gyro.attitude;
Quaternion orientacionGiroscopio = new Quaternion(lecturaGiro.x, lecturaGiro.y, -lecturaGiro.z, -lecturaGiro.w);
orientacionGiroscopio = Quaternion.Euler(90, 0, 0) * orientacionGiroscopio;
#if UNITY_ANDROID transform.rotation = orientacionGiroscopio; #else if (Input.compass.timestamp > _timestampUltimaLectura) { _timestampUltimaLectura = Input.compass.timestamp;
Vector3 vectorGravedad = Input.gyro.gravity.normalized;
Vector3 nortePlano = Input.compass.rawVector - Vector3.Dot(vectorGravedad, Input.compass.rawVector) * vectorGravedad;
Quaternion orientacionBrujula = Quaternion.Inverse(Quaternion.LookRotation(nortePlano, -vectorGravedad));
orientacionBrujula.z *= -1;
orientacionBrujula.w *= -1;
_ajusteObjetivo = orientacionBrujula * Quaternion.Inverse(orientacionGiroscopio);
}
_ajusteActual = Quaternion.Slerp(_ajusteActual, _ajusteObjetivo, 0.1f);
transform.rotation = Quaternion.Euler(0, 180, 0) * _ajusteActual * orientacionGiroscopio;
#endif } }
</div>El script se adjunta directamente al objeto de cámara y gestiona la rotación según los sensores, con diferenciación por plataforma mediante directivas de preprocesador.