Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

Copilot
Copy link

@Copilot Copilot AI commented Sep 25, 2025

Problem

The existing 2D gyroscope components suffer from Gimbal Lock, a mathematical limitation that occurs when using Euler angles for rotations. This manifests as:

  • Incorrect starting positions when holding the phone vertically vs. horizontally
  • Jerky or unpredictable rotation behavior at certain device orientations
  • Loss of one degree of freedom when rotation axes align
  • Inconsistent parallax effects depending on initial device orientation

As reported in the issue, users experience problems like "My phone starts at -30. I want this to be 0" and inconsistent behavior between lying the phone flat vs. holding it upright.

Solution

This PR introduces a complete set of 3D gyroscope components that use Quaternion arithmetic throughout the calculation pipeline to eliminate Gimbal Lock entirely.

New Components Added

Rotator Components:

  • GyroRotator3D - Abstract base class using Quaternion operations
  • GyroRotator3DAttitude - Device attitude rotation without Gimbal Lock
  • GyroRotator3DGravity - Gravity-based rotation with smooth interpolation

Mover Components:

  • GyroMover3D - Abstract base class with full 3D positioning support
  • GyroMover3DAttitude - Position movement based on device attitude
  • GyroMover3DGravity - Position movement based on gravity vector

Technical Implementation

The key innovation is avoiding Euler angle arithmetic in the critical path:

Before (2D - Gimbal Lock prone):

// Convert quaternion to Euler angles
var euler = attitude.eulerAngles;
// Perform math operations on Euler angles (problematic)
var result = euler.x + deltaX; 
// Convert back to quaternion
target.localRotation = Quaternion.Euler(result, ...);

After (3D - Gimbal Lock free):

// Work directly with quaternions
gyroRotation = Quaternion.Inverse(originGyroRotation) * attitude;
// Apply constraints only when necessary
var constrainedRotation = ApplyConstraints(gyroRotation);
// Smooth interpolation using Slerp
target.localRotation = Quaternion.Slerp(current, target, time);

Key Improvements

Feature 2D Components 3D Components
Gimbal Lock ❌ Affected Resolved
Rotation Method Euler angles Quaternions
Interpolation Quaternion.Lerp Quaternion.Slerp
Z-axis Support Limited Full 3D support
Phone Orientation Inconsistent Always consistent
Origin Stability Euler-dependent Quaternion-stable

Migration Path

Users can easily migrate to the new components:

// Replace this:
var rotator2D = AddComponent<GyroRotator2DAttitude>();
rotator2D.maxOffset = new Vector2(30, 30);

// With this:
var rotator3D = AddComponent<GyroRotator3DAttitude>();  
rotator3D.maxOffset = new Vector3(30, 30, 30); // Now with Z-axis!
rotator3D.inverseZ = false; // New Z-axis control

Documentation & Testing

  • Comprehensive documentation explaining the Gimbal Lock problem and solution
  • Side-by-side comparison demo showing 2D vs 3D behavior
  • Mathematical validation proving Gimbal Lock resolution
  • Complete test suite for all new components
  • Migration guide for easy adoption

Backward Compatibility

The existing 2D components remain unchanged and fully functional. The new 3D components are additive - users can adopt them at their own pace while maintaining existing functionality.

Verification

The new components resolve the specific issues mentioned:

"When I place my mobile on a table, it looks like the first screenshot" - 3D components maintain consistent behavior regardless of initial orientation

"My phone starts at -30. I want this to be 0" - Quaternion-based origin handling provides stable reference points

"Gimbal Lock issue exists in this tool" - Mathematically resolved through Quaternion arithmetic

This implementation provides the robust 3D gyroscope solution requested while maintaining the familiar API that users already know and love.

Original prompt

This section details on the original issue you should resolve

<issue_title>Resolve "Gimbal Lock" problem by using Quaternion for rotation</issue_title>
<issue_description>I love this tool!

Just a quick question: In the first screenshot, that's how I want it to start when the user holds the phone vertically. Currently, it starts like in the second screenshot. When I place my mobile on a table, it looks like the first screenshot. Do you know how I can fix this?

If I have a Gyro rotator and a rotator attitude on this cube with a max offset of 30 on the Y. My phone starts at -30. I want this to be 0

Screenshot1
Screenshot2

</issue_description>

Comments on the Issue (you are @copilot in this section)

@IvanMurzak Hey @craftercis , glad to hear you love it.

If I have a Gyro rotator and a rotator attitude on this cube with a max offset of 30 on the Y. My phone starts at -30. I want this to be 0

Please avoid having more than one Gyro...2D on a single gameObject because they fight each other. You better make wrapper parent gameObject to keep the second Gyro...2D on it.

Just a quick question: In the first screenshot, that's how I want it to start when the user holds the phone vertically. Currently, it starts like in the second screenshot. When I place my mobile on a table, it looks like the first screenshot. Do you know how I can fix this?

Disable / Enable Gyro...2D component to reset the original value. If you need to, you may change Cube position/rotation when Gyro component is disabled, then enable it. The new value will be used as Origin.

@IvanMurzak > > Please avoid having more than one `Gyro...2D` on a single `gameObject` because they fight each other. You better make wrapper parent `gameObject `to keep the second `Gyro...2D` on it. > > Ah then I was confused by your screenshots haha. > > > Disable / Enable `Gyro...2D` component to reset the original value. If you need to, you may change Cube position/rotation when `Gyro` component is disabled, then enable it. The new value will be used as Origin. > > So I should disable them at start, and when the gyroscope is fully loaded I would enable them again?

I believe you want to have just a single GyroRotator2DGravity on a single gameObject.
Also, you don't need to disable/enable in case when you don't want to change the Origin position in runtime.

When the Gyro... component gets OnEnable it stores the current gyroscope and current gameObject transform as Origin value. Then any next data changes in the gyroscope inputs start to impact on rotation/position of a gameObject.

If you want to change standard behavior. For example, change the Origin data for a gyroscope somewhere in the middle of your app/game execution. You will need to Disable/Enable the Gyro... component to reset the data.</comment_new>
<comment_new>@IvanMurzak

What I want is the position of screenshot 1 (that's when it's lying on my table) when you hold the phone in your hands Edit: I have tried disabling and enabling gyro... and setting a new rotation for the cueb but he is always going to the rotation at screenshot 2. I think there should be a change in the code. This thread is what I mean:

https://gamedev.stackexchange.com/questions/174107/unity-gyroscope-orientation-attitude-wrong

Edit 2: Your demo scene has the same problem

Oh, that is a different issue. You are talking about Gimbal Lock issue. That is correct. It exists in this tool. Because it uses Euler space to do math operations.</comment_new>
<comment_new>@IvanMurzak

Is there a way for you to add this into your tool or should I look for another tool. Because for what I need this tool would be perfect

I would be happy if you could do that in a dedicated pull request. I am a bit busy right now. In another case, I will do that, but can't say when for now.

Edit: It should be a group of Gyro...3D components. That means don't need to modify Gyro...2D components, they are made for 2D games. Need to create new Gyro components instead.</comment_new>

Fixes #3

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 3 commits September 25, 2025 07:30
…l Lock

Co-authored-by: IvanMurzak <9135028+IvanMurzak@users.noreply.github.com>
…omponents

Co-authored-by: IvanMurzak <9135028+IvanMurzak@users.noreply.github.com>
Co-authored-by: IvanMurzak <9135028+IvanMurzak@users.noreply.github.com>
@Copilot Copilot AI changed the title [WIP] Resolve "Gimbal Lock" problem by using Quaternion for rotation Resolve Gimbal Lock by implementing 3D components with Quaternion-based rotations Sep 25, 2025
Copilot finished work on behalf of IvanMurzak September 25, 2025 07:38
@Copilot Copilot AI requested a review from IvanMurzak September 25, 2025 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Resolve "Gimbal Lock" problem by using Quaternion for rotation

2 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.