Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • 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
2
Question by $$anonymous$$ · Jan 12, 2014 at 06:29 PM · rotationquaternionclampnpc

limit rotation of x axis in a quaternion

i have a script wich makes an npc look at the player. when i jump towards the npc i dont want the npc to do a "backflip" to continue looking at me. i basicly want to clam rotation along one axis. here is my script

 var player : GameObject;
 var detectDistance = 20;
 var minDistance = 3;
 var attackDistance = 4;
 var moveSpeed = 8;
 var alive = true;
 var damping : float = 5;
 var maxXRot = 20;

 function Start () {    
 player = GameObject.FindGameObjectWithTag("Player");
 }

 function Update () {
 var distance = Vector3.Distance(player.transform.position, gameObject.transform.position);
 var rotation = Quaternion.LookRotation(player.transform.position - transform.position);
 if(distance <= detectDistance && distance >= minDistance && alive){
     transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * damping);
 }
 }
Comment
Add comment
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
1
Best Answer

Answer by robertbu · Jan 12, 2014 at 07:23 PM

I don't know of a clean simple solution to this problem. So here is a more complex ugly solution. I verified that it compiles, but I've run out of time to setup a scene to test the code, so it is untested at this point.

The idea is to test to see if the angle is above some maxAngle. If it is, the look at point is moved down. to make this happen, the target point is projected onto a mathematical plane facing up and running through transform.position. We can then use the distance to the point on the plane as the 'x' and the height of the point above the plane as the 'y' and use Atan2() to calculate the angle. If is out of the range of -maxAngle to +maxAngle, we adjust the height of the point to bring it into range:

 #pragma strict
 
 var player : Transform;
 var detectDistance = 20;
 var minDistance = 3;
 var attackDistance = 4;
 var moveSpeed = 8;
 var alive = true;
 var damping : float = 5;
 //var maxXRot = 20;
 
 var maxAngle = 45.0;
 
 function Start () { 
     player = GameObject.FindGameObjectWithTag("Player").transform;
 }
 
 function Update () {
     var distance = Vector3.Distance(player.position, gameObject.transform.position);
 
     if(distance <= detectDistance && distance >= minDistance && alive) {
         var dir = player.position - transform.position; 
         
         var planePoint = ProjectPointOnPlane(Vector3.up, transform.position, player.position);
         
         var dist = Vector3.Distance(planePoint, transform.position);
         var angle = Mathf.Atan2(dir.y, dist) * Mathf.Rad2Deg;
 
         if (Mathf.Abs(angle) > maxAngle) { 
             var sign = Mathf.Sign(angle);
             dir.y = sign * Mathf.Tan(maxAngle * Mathf.Deg2Rad) * dist;
         }
 
         var rotation = Quaternion.LookRotation(dir);
         transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * damping);
     }
 }
 
 function ProjectPointOnPlane(planeNormal : Vector3, planePoint : Vector3, point : Vector3) : Vector3 {
     
     var  dist = Vector3.Dot(planeNormal, (point - planePoint));
     return point + planeNormal.normalized * -dist;
 }    

With calculations this complicated I give it less than 50/50 I got everything right the first shot. If you cannot get it working, let me know, and I'll work up a test scene when I have a bit more time. Note I changed your 'Player' to a Transform rather than a GameObject.

Comment
Add comment · Show 1 · 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 semiessessi · Jan 12, 2014 at 07:41 PM 0
Share

Not to be competitve, but I recommend you read my answer as well as the OP.

The volume of math and points of failure in this code is quite redundant given the nature of the problem - even though the algorithm is conceptually quite simple.

Using a more appropriate representation for the orientation than a quaternion, and only using the quaternion to feed Unity what it needs makes the problem 'disappear naturally'. I'd guess this is the sort of 'clean' solution you refer to at the start of your answer...

avatar image
0

Answer by semiessessi · Jan 12, 2014 at 07:32 PM

Quaternion rotations are not for interpolating in this way. Having constraints allows us to do nice things - quaternions are the golden sledgehammer for when we don't know anything, as well as being a memory efficient way to encode an orientation (which is why Unity and many game engines use them internally).

Slerping or lerping a quaternion will never give you a nice result if you want any kind of constraints to apply throughout the interpolation, as the middle parts of the interpolation will most often wobble around and not be constrained to any particular plane - even if the start and end rotations are. In your case this manifests as a backflip.

A very simple solution here is to not operate on quaternions at all and to keep track of an angle yourself - which is sufficient to describe orientation in a plane (e.g. the xz plane). You can then use Quaternion.AxisAngle to construct the correct transform every frame (or every change if you really want some more performance) and stomp over whatever is in the transform. If you do a regular lerp on the angle then everything will remain in the plane normal to the axis vector.

Your axis in this case would be Vector3( 0, 1, 0 ) - assuming you are using the positive y direction for up.

EDIT: just a thought, but if you want to constrain to the family of planes within a certain angle of the xz plane then you can use two angles and do two rotations, using the transform.right (or .left) as the axis after the first rotation.

Normally this is recommended against because it has the 'gimbal lock' problem which is a strong motivation for using quaternions in other situations (camera lerping). However this is precisely the behaviour you want on a character's head which is tracking an object - it is what your head does in real life - so actually you want gimbal locking and two angles.

You can still account for being able to tilt the head back further than vertical if you want - just don't use the limit that stops at gimbal lock - you can rotate through gimbal lock and the rotation around xz will be reversed - just like what happens if you tilt your head back and arch your back to see behind you. The key point is that you can control the angles precisely with trivial logic (Mathf.Clamp). Interpolating these angles again stops 'crazy' in betweens and gives you control so that you could if you wanted to implement something like the head moving first and the body following, or tracking in x first then y, or any proportion of those things mixed together.

From the sounds of it you have a much simpler model than this where you are rotating an entire mesh - so I don't think this is relevant, but you might find it useful to know in future...

Comment
Add comment · 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

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.

Follow this Question

Answers Answers and Comments

18 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Align GameObject to Terrain angle 2 Answers

Clamping a character's head rotation. 1 Answer

How to Clamp a Quaternion Camera Rotation? 0 Answers

Clamp Quaternions Rotation for camera 1 Answer

Edited mouselook script rotation clamp not working 0 Answers


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