• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
Question by BadgerMeles · May 31, 2010 at 10:10 PM · camerarotationcontroller

Rotating local Z-axis (roll) on a MouseLook (or similarly) controlled camera?

Desired Result: Similar to a space craft, capable of maneuvering in any direction wit$$anonymous$$n a 3d environment: keyboard movement (X/Y/Z translation), mouse Yaw/Pitch (X/Y rotation), and keyboard Roll (Z rotation).

Problem: Can't manipulate the Z-axis rotation for roll.

Basic example in a (v2.6) new scene-

  1. Select the default Main Camera
  2. Component -> Camera-Control -> MouseLook
  3. Component -> Physics -> Rigid Body
  4. In the Inspector, turn off 'Use Gravity'
  5. Create a new JavaScript asset and apply it to the Main Camera

JavaScript-

var speed : int = 1;
function FixedUpdate() {
  if (Input.GetKey("w")) {
    rigidbody.AddRelativeForce(Vector3.forward * speed);
  }
}

T$$anonymous$$s behaves exactly the way I want, except for roll. It works great for forward momentum with the mouse controlling your direction of travel (yaw/pitch), and I can easily add additional controls using other Vector3's (x/y/z translation). However, I just can't seem to find any way to rotate the Z-axis for that desired roll effect.

I've tried using transform.Rotate() and rigidBody.AddRelativeTorque(), but both only work if the MouseLook node is detached/disabled.

I've also tried mimicking MouseLook behavior directly in the above script by applying Input.GetAxis("Mouse X/Y") to transform.Rotate(), w$$anonymous$$ch does seem to work for Pitch/Yaw, and roll does rotate the Z axis - but it would only rotate wit$$anonymous$$n 0-8 degrees, and moving the mouse would cause it to "jitter" wit$$anonymous$$n 0-15 degrees.

I found that attempting to use rigidBody.AddRelativeTorque() with Input.GetAxis("Mouse X/Y"), causes it to spin uncontrollably. W$$anonymous$$ch I t$$anonymous$$nk does make sense, since it's constantly applying torque based on the mouse's position - not the desirable effect.

From what I can tell so far, setting the X/Y rotation effects the Z axis as well, w$$anonymous$$ch is where my problem is. Perhaps t$$anonymous$$s is part of Quaternion nature to avoid gimbal lock? That's just a curious guess, as I don't understand the Quaternion concept well enough to grasp why t$$anonymous$$s is happening.

For reference, http://answers.unity3d.com/questions/2544/first-person-flight-controls helped me get t$$anonymous$$s far - but unless I'm missing somet$$anonymous$$ng, it doesn't quite fulfill t$$anonymous$$s scenario.

So, the basic question is: How do I let the user 'roll' the camera on the Z axis with a keystroke, w$$anonymous$$le still controlling the yaw/pitch (X/Y) axis with the mouse?

Thank you for reading, and even more thanks for any answers or suggestions!

~Badger

Comment

People who like this

0 Show 0
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
Best Answer

Answer by BadgerMeles · Jun 01, 2010 at 11:55 AM

The following is the full control script that I wrote after finding the solution. It's a strong basis for the 3d-space controller I was looking for - and fun to fly around with! For best testing results, I suggest setting your rigid body Mass, Drag and Angular Drag all to 1; also, make sure Gravity, Is Kinematic and Freeze Rotation are all disabled.

var speed : int = 10; // Relative force applied for standard ws,ad,zx,qe movement. var mouseSensativity : int = 15; // Speed of mouse movements for X/Y rotation, looking around. var invertPitch : boolean = false; // Invert the X rotation axis. False: MouseDown=LookDown, True:MouseDown=LookUp // Whether to use transform.Rotate() or rigidbody.AddRelativeTorque(); enum rotationMethods { Torque = 0, Rotate = 1 }; var rotationMethod = rotationMethods.Rotate;

function FixedUpdate() { // T$$anonymous$$s probably could be turned into a constant, instead of checking every update, but doing so lets the user change the setting on the fly without repercussions. var invertPitchInt; if (invertPitch) invertPitchInt = -1; else invertPitchInt = 1;

// Standard translate/position controls. if (Input.GetKey("w")) { rigidbody.AddRelativeForce (Vector3.forward speed); } if (Input.GetKey("s")) { rigidbody.AddRelativeForce (Vector3.forward -1 speed); } if (Input.GetKey("a")) { rigidbody.AddRelativeForce (Vector3.left speed); } if (Input.GetKey("d")) { rigidbody.AddRelativeForce (Vector3.right speed); } if (Input.GetKey("z")) { rigidbody.AddRelativeForce (Vector3.down speed); } if (Input.GetKey("x")) { rigidbody.AddRelativeForce (Vector3.up * speed); }

 // Keyboard controls to mimic mouse movements.
 if (Input.GetKey("i")) { // Simulate increasing the X axis, mouse movement up.
     if (rotationMethod == rotationMethods.Torque) { rigidbody.AddRelativeTorque (invertPitchInt * -mouseSensativity * Time.deltaTime, 0, 0); }
     else { transform.Rotate(invertPitchInt *  -mouseSensativity * Time.deltaTime, 0, 0); }
 }
 if (Input.GetKey("k")) { // Simulate decreasing the X axis, mouse movement down.
     if (rotationMethod == rotationMethods.Torque) { rigidbody.AddRelativeTorque (invertPitchInt * mouseSensativity * Time.deltaTime, 0, 0); }
     else { transform.Rotate(invertPitchInt * mouseSensativity * Time.deltaTime,0,0); }
 }
 if (Input.GetKey("j")) { // Simulate increasing the Y axis, mouse movement left.
     if (rotationMethod == rotationMethods.Torque) { rigidbody.AddRelativeTorque (0, -mouseSensativity * Time.deltaTime, 0); }
     else { transform.Rotate(0, -mouseSensativity * Time.deltaTime, 0); }
 }
 if (Input.GetKey("l")) { // Simulate decreasing the Y axis, mouse movement right.
     if (rotationMethod == rotationMethods.Torque) { rigidbody.AddRelativeTorque (0, mouseSensativity * Time.deltaTime, 0); }
     else { transform.Rotate(0, mouseSensativity * Time.deltaTime, 0); }
 }

 // Actual mouse movement controls
 if (Input.GetAxis("Mouse X")) {
     if (rotationMethod == rotationMethods.Torque) { rigidbody.AddRelativeTorque( 0, Input.GetAxis("Mouse X") * mouseSensativity, 0); }
     else { transform.Rotate(0, Input.GetAxis("Mouse X") * mouseSensativity, 0); }
 }
 if (Input.GetAxis("Mouse Y")) {
     if (rotationMethod == rotationMethods.Torque) { rigidbody.AddRelativeTorque( invertPitchInt * Input.GetAxis("Mouse Y") * -mouseSensativity, 0, 0); }
     else { transform.Rotate(invertPitchInt * Input.GetAxis("Mouse Y") * -mouseSensativity, 0, 0); }
 }

 // Roll controls
 if (Input.GetKey("q")) {
     if (rotationMethod == rotationMethods.Torque) { rigidbody.AddRelativeTorque(0, 0, speed * Time.deltaTime); }
     else { transform.Rotate( 0, 0, -speed * Time.deltaTime); }
 }
 if (Input.GetKey("e")) {
     if (rotationMethod == rotationMethods.Torque) { rigidbody.AddRelativeTorque(0, 0, -speed * Time.deltaTime); }
     else { transform.Rotate( 0, 0, speed * Time.deltaTime); }
 }

}

I don't know if it's "proper" to post t$$anonymous$$s, too, but I t$$anonymous$$nk it can be removed if overkill. T$$anonymous$$s is the test script I was working with, so others that might have similar issues can see what I tested, and why it didn't work. It's not very pretty, but hopefully the comments explain my thought process. I'm still getting used to t$$anonymous$$s, though, so my apologies if it's not very clear.

var speed : int = 10; // Relative force applied for standard ws,ad,zx movement. var mouseSensativity : int = 15; // Rotation method used for rolling. {q/e} enum rollMethods { Torque = 0, Rotate = 1 }; var rollMethod = rollMethods.Rotate; // Rotation method used for mouse, or mouse-simulated key strokes {ik,jl} enum rotationTypes { Torque = 0, Rotate = 1 }; var rotationType = rotationTypes.Rotate; // Option to roll before or after the X/Y mouse rotations. enum whenToRollOptions {BeforeMouseLook = 0, DuringMouseLook = 1, AfterMouseLook = 2}; var whenToRoll = whenToRollOptions.DuringMouseLook; // Switch between the mouse-simulated keyboard, or actual mouse movement. var useKeyboardMouseSimulator : boolean = false; // Select w$$anonymous$$ch test result to use. enum mouseLookTests { TestA = 0, TestB = 1, TestC = 3, TestD = 4, TestE = 5, TestF = 6, TestG = 7, TestH = 8 } var mouseLookTest = mouseLookTests.TestA;

var clampedRotationX : float = 0.0; var clampedRotationY : float = 0.0; var clampedRotationZ : float = 0.0;

function FixedUpdate() { // Standard translate/position controls. if (Input.GetKey("w")) { rigidbody.AddRelativeForce (Vector3.forward speed); } /else/ if (Input.GetKey("s")) { rigidbody.AddRelativeForce (Vector3.forward -1 speed); } if (Input.GetKey("a")) { rigidbody.AddRelativeForce (Vector3.left speed); } /else/ if (Input.GetKey("d")) { rigidbody.AddRelativeForce (Vector3.right speed); } if (Input.GetKey("z")) { rigidbody.AddRelativeForce (Vector3.down speed); } /else/ if (Input.GetKey("x")) { rigidbody.AddRelativeForce (Vector3.up * speed); }

 if (useKeyboardMouseSimulator) { // Manual key controls simulating mouse movement.
     if (Input.GetKey("q")) {
         if (rollMethod == rollMethods.Torque) { rigidbody.AddRelativeTorque(0,0,speed * Time.deltaTime); }
         else { transform.Rotate(0,0,speed * Time.deltaTime); }
     } else if (Input.GetKey("e")) {
         if (rollMethod == rollMethods.Torque) { rigidbody.AddRelativeTorque(0,0,-speed * Time.deltaTime); }
         else { transform.Rotate(0,0,-speed * Time.deltaTime); }
     }

     // ** -- Converting t$$anonymous$$s block to accept Mouse input should be the solution I'm looking for -- **
     // The result these keys produce is the exact same result the mouse movement should produce: i=MouseDown, k=MouseUp, j=MouseLeft, k=MouseRight
     // Using Rotate is much more responsive, and likely the best result for user controlled mouse-like movement, and probobly is the result I'm most likely to desire to use.
     // However, I'd also like to have the option to use the Torque method, if it would be possible to impliment - just to see how it functions.
     if (Input.GetKey("i")) { // Simulate increasing the X axis, mouse movement up.
         if (rotationType == rotationTypes.Torque) { rigidbody.AddRelativeTorque (speed * Time.deltaTime, 0, 0); }
         else { transform.Rotate(speed * Time.deltaTime, 0, 0); }
     } else if (Input.GetKey("k")) { // Simulate decreasing the X axis, mouse movement down.
         if (rotationType == rotationTypes.Torque) { rigidbody.AddRelativeTorque (-speed * Time.deltaTime, 0, 0); }
         else { transform.Rotate(-speed * Time.deltaTime,0,0); }
     }
     if (Input.GetKey("j")) { // Simulate increasing the Y axis, mouse movement left.
         if (rotationType == rotationTypes.Torque) { rigidbody.AddRelativeTorque (0, -speed * Time.deltaTime, 0); }
         else { transform.Rotate(0, -speed * Time.deltaTime, 0); }
     } else if (Input.GetKey("l")) { // Simulate decreasing the Y axis, mouse movement right.
         if (rotationType == rotationTypes.Torque) { rigidbody.AddRelativeTorque (0, speed * Time.deltaTime, 0); }
         else { transform.Rotate(0, speed * Time.deltaTime, 0); }
     }
 } else { // T$$anonymous$$s block contains my various attempts to ac$$anonymous$$eve the goal I'm after.
     clampedRotationX = ClampAngle(clampedRotationX + (Input.GetAxis("Mouse X") * mouseSensativity), -360, 360);
     clampedRotationY = ClampAngle(clampedRotationY + (Input.GetAxis("Mouse Y") * mouseSensativity), -360, 360);
     if (Input.GetKey("q")) { clampedRotationZ = ClampAngle(clampedRotationZ + (speed * Time.deltaTime), -360, 360); }
     else if (Input.GetKey("e")) { clampedRotationZ = ClampAngle(clampedRotationZ + (-speed * Time.deltaTime), -360, 360); }

     if (mouseLookTest == mouseLookTests.TestA) {
         // T$$anonymous$$s test works almost perfectly, except that the mouse movements do not follow the Z rotation propperly.
         // Moving the mouse up/down looks up/down as expected when Z=0, but as Z changes the up/down movement does not follow.
         // Basically if Z=90, moving the mouse up/down looks left/right. T$$anonymous$$s is not the desired effect. Moving the mouse up/down, should always look up/down.
         transform.localRotation = Quaternion.Euler(-clampedRotationY, clampedRotationX, clampedRotationZ);
     } else if (mouseLookTest == mouseLookTests.TestB) {
         // T$$anonymous$$s seems to have the same result as TestA.
         transform.localRotation = Quaternion.AngleAxis(clampedRotationX, Vector3.up) * Quaternion.AngleAxis(clampedRotationY, Vector3.left) * Quaternion.AngleAxis(clampedRotationZ, Vector3.forward);
     } else if (mouseLookTest == mouseLookTests.TestC) {
         // T$$anonymous$$s freaks out beyond my understanding.
         transform.localRotation.x = clampedRotationX; // clampedRotationY;
         transform.localRotation.y = clampedRotationY; // clampedRotationX;
         transform.localRotation.z = clampedRotationZ;
     } else if (mouseLookTest == mouseLookTests.TestD) {
         // T$$anonymous$$s applies a constant rotation to the object, meaning it will always be rotating as long as clampedRotationX/Y/Z have non-zero values.
         // Effectively t$$anonymous$$s makes it impossible to control, since the camera will just start spinning.
         transform.Rotate(clampedRotationY, clampedRotationX, clampedRotationZ);
     } else if (mouseLookTest == mouseLookTests.TestE) {
         // Same as TestD, just with Torque/force instead of direct rotation.
         rigidbody.AddRelativeTorque(clampedRotationY, clampedRotationX, clampedRotationZ);
     } else if (mouseLookTest == mouseLookTest.TestF) {
         // T$$anonymous$$s works perfectly! Used without torque, best used with 'Freeze Rotation' enabled to avoid Torque forces interfearing with your rotational axis.
         if (Input.GetKey("q")) { transform.Rotate(Input.GetAxis("Mouse Y") * -mouseSensativity, Input.GetAxis("Mouse X") * mouseSensativity, speed * Time.deltaTime); }
         else if (Input.GetKey("e")) { transform.Rotate(Input.GetAxis("Mouse Y") * -mouseSensativity, Input.GetAxis("Mouse X") * mouseSensativity, -speed * Time.deltaTime); }
         else { transform.Rotate(Input.GetAxis("Mouse Y") * -mouseSensativity, Input.GetAxis("Mouse X") * mouseSensativity, 0); }
     } else if (mouseLookTest == mouseLookTest.TestG) {
         // T$$anonymous$$s works perfectly! T$$anonymous$$s is truely what I was after, with 'Freeze Rotation' disabled, Torque forces apply beautifully.
         if (Input.GetKey("q")) { rigidbody.AddRelativeTorque(Input.GetAxis("Mouse Y") * -mouseSensativity, Input.GetAxis("Mouse X") * mouseSensativity, speed * Time.deltaTime); }
         else if (Input.GetKey("e")) { rigidbody.AddRelativeTorque(Input.GetAxis("Mouse Y") * -mouseSensativity, Input.GetAxis("Mouse X") * mouseSensativity, -speed * Time.deltaTime); }
         else { rigidbody.AddRelativeTorque(Input.GetAxis("Mouse Y") * -mouseSensativity, Input.GetAxis("Mouse X") * mouseSensativity, 0); }
     } else if (mouseLookTest == mouseLookTests.TestH) {
         // T$$anonymous$$s also works as expected, and accepts the roll/rotate options accurately.
         // T$$anonymous$$s was used to test/prove that X/Y rotation for looking can be 'quick' using transform.Rotate(), w$$anonymous$$le leaving Z roll effected by physics/torque/drag, producing an interesting control result.
         // Set rollMethod=Torque and rotationMethod=Rotate to test. -- Though I've read that using Rotate() on a Torque controlled objct, can have unpredictable results.
         if (rotationType == rotationTypes.Torque) { rigidbody.AddRelativeTorque(Input.GetAxis("Mouse Y") * -mouseSensativity, Input.GetAxis("Mouse X") * mouseSensativity, 0); }
         else { transform.Rotate(Input.GetAxis("Mouse Y") * -mouseSensativity, Input.GetAxis("Mouse X") * mouseSensativity, 0); }
         if (Input.GetKey("q")) {
             if (rollMethod == rollMethods.Torque) { rigidbody.AddRelativeTorque(0, 0, -speed * Time.deltaTime); }
             else { transform.Rotate(0, 0, -speed * Time.deltaTime); }
         }
         if (Input.GetKey("e")) {
             if (rollMethod == rollMethods.Torque) { rigidbody.AddRelativeTorque(0, 0, speed * Time.deltaTime); }
             else { transform.Rotate(0, 0, speed * Time.deltaTime); }
         }
     }
 }

}

function ClampAngle (angle : float, min : float, max : float) { if (angle < -360) angle += 360; if (angle > 360) angle -= 360; return Mathf.Clamp (angle, min, max); }

Hopefully t$$anonymous$$s helps someone else sometime!

~Badger

Comment

People who like this

0 Show 0 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image

Answer by Tetrad · May 31, 2010 at 10:19 PM

I don't have an exact answer for you, but the reason your MouseLook script breaks anyt$$anonymous$$ng you try to do with roll is because the MouseLook script sets the transform.rotation of your object. So, depending on the order of operations of update, the MouseLook will hammer the rotation, back to be facing upwards, probably causing the "jittering" effect you're talking about.

If you open the default MouseLook script it does somet$$anonymous$$ng like t$$anonymous$$s:

// Read the mouse input axis rotationX += Input.GetAxis("Mouse X") * sensitivityX; rotationY += Input.GetAxis("Mouse Y") * sensitivityY;

     rotationX = ClampAngle (rotationX, minimumX, maximumX);
     rotationY = ClampAngle (rotationY, minimumY, maximumY);

     Quaternion xQuaternion = Quaternion.AngleAxis (rotationX, Vector3.up);
     Quaternion yQuaternion = Quaternion.AngleAxis (rotationY, Vector3.left);

     transform.localRotation = originalRotation * xQuaternion * yQuaternion;

You might be able to add a Z rotation in there and just multiply them all together. I'd suggest trying that.

Comment

People who like this

0 Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image BadgerMeles · May 31, 2010 at 11:31 PM 0
Share

That's what I mentioned didn't work when mimicking MouseLook. Using transform.Rotate(rotationX,rotationY,0) spins the camera uncontrollably, as does rigidBody.AddRelativeTorque() and transform.localRotate *= xQuaternion * yQuaternion. Using transform.localRotation = Quaternion.Euler(rotationY * -1, rotationX,0); or transform.localRotation = Quaternion.AngleAxis(...) (as MouseLook does) works for X/Y, but it sets Z. Quaternion.AngleAxis(transform.localRotaion.z*10, Vector3.forward) just sets Z, snapping back when the key is released. I don't know how to otherwise add a Z-rotation.

avatar image Tetrad · Jun 01, 2010 at 02:52 PM 0
Share

Why not juts mimic what rotationX and rotationY does for your Z component? So at the end of the day you have something like

 transform.localRotation = originalRotation * xQuaternion * yQuaternion * zQuaternion.

You could get the zQuaternion by keeping a rotationZ variable that you add to or subtract from depending on whatever your input variables are, and then do the AngleAxis thing appropriately (using the forward vector).

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

No one has followed this question yet.

Related Questions

Make the player go into the direction I am looking at 0 Answers

Camera following Pivot not working 1 Answer

camera rotation interferes with controller rotation 0 Answers

Help, please with rotation input 0 Answers

Camera Angle Conflict Pitch & Pan...I'm Lost 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges