Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Rotating object on 2 axis

Discussion in 'Scripting' started by KodaL, Apr 18, 2011.

  1. KodaL

    KodaL

    Joined:
    Apr 4, 2008
    Posts:
    37
    I've searched the forums for the past 4 days now and while I've found various things that help, none of them are the solution or I don't understand how to make them into the solution I need.

    What I'm trying to accomplish is this with UnityScript, (for a mobile platform):

    Have a cube on screen and allow the player to rotate it both up/down, and left/right by touching the cube and dragging (or "flicking" but I'm starting with baby steps here). There is more I want to be able to do (like having it clamp into the closest 90-degree rotation when the player lifts his finger up), but for now I want to start with the basics.

    I'm running into problems with the rotation though. I've tried numerous scripts I've found on the forum but they don't seem to work right (see code at bottom).

    My object's rotation is set to (0, 0, 0) in the scene. Not sure if it matters, but my camera's rotation is set at (90, 0, 0). Basically a top down view.
    Touching and dragging up/down seems to work, but when I drag left/right it isn't rotating correctly. If I drag up/down to a different side of the cube, then try to drag left/right it will work ok sometimes, depending which side of the cube is facing the camera.

    I should also note, that I don't need the cube to rotate in both axis at once. Rather have it do one or the other depending on the movement of player's finger.

    This thread looks like it should be the answer, but it doesn't work for me at all:
    http://forum.unity3d.com/threads/22920-Please-suggest-on-rotation-issue
    I'm sure there is something I'm missing with the code. All that happens is the cube goes wild when I move my finger.

    Code (csharp):
    1.  
    2. for (var touch : iPhoneTouch in iPhoneInput.touches) {
    3.    
    4.     if (touch.phase == iPhoneTouchPhase.Began) {
    5.         touchPosition = touch.position;
    6.     }
    7.     else if (touch.phase == iPhoneTouchPhase.Moved) {
    8.         var angles = transform.eulerAngles;
    9.         rotX = angles.x;
    10.         rotY = angles.y;
    11.         rotZ = angles.z;
    12.         x = (rotX+(touchPosition.x - touch.position.x));
    13.         y = (rotY+(touchPosition.y - touch.position.y));
    14.         var rotation = Quaternion.Euler(-x  * Time.deltaTime, y *Time.deltaTime, 0);
    15.                 transform.rotation = rotation;
    16.     }
    17. }
    18.  
    19. /*
    20.  
    21. Then he adds this somewhere in the code, but I don't know where it's supposed to go:
    22. transform.Rotate(-x, y, 0, Space.World);
    23.  
    24. */
    25.  
    26.  

    These are the other top 2 scripts I've found that are also the closest to working:
    Code (csharp):
    1.  
    2. // found here in the forum:  "http://forum.unity3d.com/threads/49491-Help-with-iPhoneInput-rotating-object"
    3.  
    4. public var speed : float = 50;
    5.  
    6. function Update ()
    7. {
    8.     if (Input.touchCount > 0  Input.GetTouch(0).phase == TouchPhase.Moved) {
    9.         // Get movement of the finger since last frame
    10.         var touchDeltaPosition:Vector2 = Input.GetTouch(0).deltaPosition;
    11.         // transform.eulerAngles = Vector3(0, touchDeltaPosition.x * Time.deltaTime * speed, 0);  // wasn't working correctly
    12.         transform.Rotate(Vector3.up * touchDeltaPosition.x * Time.deltaTime * speed, Space.World);
    13.         transform.Rotate(Vector3.right * touchDeltaPosition.y * Time.deltaTime * speed, Space.World);
    14.     }
    15. }
    16.  
    Code (csharp):
    1.  
    2. // found here in the forum:  "http://forum.unity3d.com/threads/76698-Smooth-RotateAround"
    3.  
    4. var moveSpeed : float = 2.0;
    5. var damper=0.95;
    6. var leadOutMultiplier=0.03;
    7. var autoRotationSpeed = 0.5;
    8.  
    9. private var aPosX;
    10. private var bPosX;
    11. private var stopTime=0.0;
    12. private var moveMag=0.3;
    13.  
    14. function Start()
    15. {
    16.     moveMag=autoRotationSpeed;
    17. }
    18.  
    19. function Update () {
    20.     for (var i=0; i < Input.touchCount; i++)
    21.     {
    22.         var touch : Touch = Input.touches[i];
    23.         if (touch.phase == TouchPhase.Began)
    24.         {
    25.             aPosX = touch.position.x;
    26.         }
    27.         if (touch.phase == TouchPhase.Moved)
    28.         {
    29.             bPosX = touch.position.x;
    30.         }
    31.         if (touch.phase == TouchPhase.Ended)
    32.         {
    33.             aPosX=bPosX;
    34.         }
    35.         if (aPosX!=bPosX)
    36.         {
    37.             stopTime=Time.time;
    38.             moveMag = (bPosX - aPosX)/moveSpeed;
    39.         }
    40.         else
    41.         {
    42.             moveMag = Mathf.Lerp(moveMag * damper, autoRotationSpeed, (Time.time-stopTime) * leadOutMultiplier);
    43.         }
    44.         //moveMag=1;
    45.         transform.Rotate(0, -moveMag * Time.deltaTime * 80, 0);
    46.         aPosX=bPosX;
    47.     }
    48. }
    49.  
     
  2. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    Is your eventual goal for the cube always to rotate in increments of 90 degrees, either about the up axis or the side axis depending on the direction of the swipe?
     
  3. KodaL

    KodaL

    Joined:
    Apr 4, 2008
    Posts:
    37
    Thanks for your response!

    Yes, pretty much.
    • I always want the cube to end up with one of it's sides directly facing the camera.
    • I would like the option to have the cube rotate based on the swipe/drag direction (either up or side axis like you said),
    • and the option to also have it continuously rotate if the user keeps dragging their finger on the screen in the correct direction.

    Additional info that may be needed
    Eventually I want this script attached to a different game object and use raycasting to select the individual cube I want to rotate. But I think that should be easy to implement once I get the basic code right.

    I would love to eventually be able to have the cube rotate based on how fast the swipe was, with momentum/physics/etc... (if the user does a long fast swipe, the cube will rotate through more multiple faces and then slow down and stop), but I'm trying to start out small. Since I think this technique will require rigidbodies, torque and further complications.
     
  4. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    I probably wouldn't use the built-in physics system for this, or incremental rotations either.

    This is what I've suggested in the past for this sort of thing (although I admit I've never actually tried this myself).

    For something like this, I would create an array of quaternions, each of which represents one of the 24 (I think it's 24) axis-aligned orientations. Then I'd create a 'neighbor' map that indicated, for each orientation, what the four 'neighbor' orientations are. In this context, a neighbor orientation is an orientation reached by a left rotation, a right rotation, an up rotation, or a down rotation (for a total of four neighbors).

    Once you have this data available, given a cube's current orientation and the direction of a swipe, you can quickly and easily determine what the next orientation for the cube should be. This eliminates various difficulties you'd likely run into with incremental rotations or with using the physics system, such as accumulated numerical error, lack of predictability, etc.

    Now obviously you don't want the cube to just snap directly to each new orientation. However, you can achieve any animation effects you want to achieve simply by using Slerp() and varying the t parameter in an appropriate way. (For example, you can apply an ease-in-ease-out mapping, or make the cube respond dynamically to the input, e.g. by 'trying' to make a rotation, and then either returning to the original orientation if the move isn't completed, or finally accelerating into the new orientation if the move is completed.)
     
  5. KodaL

    KodaL

    Joined:
    Apr 4, 2008
    Posts:
    37
    Thanks! I see what you are saying and it makes sense... I just have no idea how to implement any of that. lol. I'm just starting to learn coding and Unity3d. So while I understand most of the basics, this sounds a bit more complicated. Arrays can be a bit intimidating to me.

    I looked up quaternions but not exactly sure how to create an array of them. (Ie: which quaternion I would use to assign in the array, and how to assign it?) It says not to modify the 'x/y/z/w' directly... speaking of which, I have no idea what 'w' even is. My cube(s) will be instantiated with a (0,0,0) rotation, so that is a base starting point I suppose.

    For the 'neighbor map', would that be another array? Or an 'enum' and 'switch/case' statement depending on what side of the cube is facing camera? Or something else entirely?

    Thanks for all the help!
     
  6. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    I do think the method I described earlier is probably the best way to go for something like this. If you're new to programming though, it might be a little much to tackle, so you might have better luck just pursuing whatever method makes the most sense to you (for now at least).

    Just to answer a couple of your questions though:

    You don't necessarily need to worry about the meaning of the x, y, z, and w elements in order to use quaternions. (It's certainly useful to understand how quaternions work at that level, but it isn't necessarily something you need to get into right away.)

    Regarding arrays (and containers in general), they're an invaluable tool, so you'll want to start learning about them as soon as you can, regardless of what method you use for this.

    As for how to set this up, I'd probably do it like this. There are 24 unique axis-aligned orientations (if I'm not mistaken). Since you can swipe left, right, up, and down, you can get to four other orientations from each of these orientation.

    To represent this, I'd create a user-defined data type (e.g. a class) that stores the following:

    - A quaternion (the orientation associated with the entry)
    - An array of four references, each of which corresponds to a swipe direction (left, right, up, down) and refers to the entry corresponding to the orientation that you get if you swipe in that direction

    In C#, it might look something like this (untested):

    Code (csharp):
    1. class OrientationNode
    2. {
    3.     Quaternion orientation;
    4.     OrientationNode[] neighbors = new OrientationNode[4];
    5.  
    6.     // Accessors, function, etc.
    7. }
    The trickiest part is probably setting up the array and establishing the neighbor relationships. You could probably do it procedurally, but another option would be simply to sit down and do it by hand. It might take a little while, but it's something that only needs to be done once.

    Again though, it is a little involved, programming-wise (other methods might prove to be more immediately intuitive).
     
  7. KodaL

    KodaL

    Joined:
    Apr 4, 2008
    Posts:
    37
    Well I'm really no closer to figuring out a way to do it the other ways I mentioned in my 1st post, then I would be trying this way... So, kinda stuck. :confused:

    A question I have about the neighbor relationship, is how to change the position for a cube who's facing (270, 0, 0) in the inspector. The down neighbor would be (360, 0, 0), or would I have the down neighbor be (0, 0, 0)? My worry is if I set it to (0, 0, 0) and then try to slerp from (270,0,0) to (0,0,0) instead of 360, wouldn't it rotate back on itself in the wrong direction of the finger drag?

    I found a forum post where the end result is what I need, http://www.youtube.com/watch?v=7kEUMGvPnMA, except the thread is from 2009 and the poster hasn't been on for a year. http://forum.unity3d.com/threads/26227-Cubes-snap-rotations
    He never came back to say how he did it. But the video is pretty much spot on to what I need. sigh.
     
  8. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    Well, it's not entirely trivial, and might be challenging without a decent level of programming experience. That's not to discourage you though - just don't expect it to necessarily be easy, that's all :)

    I don't know what method the poster in the other thread used. I'm sure it could be done using some sort of 'snapping' technique; I just think it would be more roundabout than the 'neighbor orientation' method.

    Regarding the neighbor orientations, first, forget about Euler angles (except perhaps for setting up the orientations initially). You want to interpolate using Quaternion.Slerp() rather than by interpolating angles, and trying to think of the orientations themselves in terms of Euler angles probably won't be of much help.

    Again, the hardest part is probably setting up the orientation map in the first place. You should be able to do this procedurally. It'd be a little involved, but I can try to explain further (or at least speculate) if it would be helpful.
     
  9. KodaL

    KodaL

    Joined:
    Apr 4, 2008
    Posts:
    37
    Well, if the end result is something similar to http://www.youtube.com/watch?v=7kEUMGvPnMA, then I'm happy!

    Not familiar with Euler vs Regular vs Quaternion angles. So that will be part of my learning process.

    I'm not familiar with C# at all, so I'll be writing in UnityScript. Hopefully that won't confuse matters. I also don't know how to create a class yet, so I'll start out with this section by section.

    First big thing is the "main array of quaternions, each of which representing one of the 24 axis-aligned orientations":
    • To define each quaternion in my array, would I be using the (x,y,z) values from the Inspector?
      So basically I would have the first array entry be: (0, 0, 0), second would be (90, 0, 0), third (180, 0, 0), and so on through every possible side position? But then wouldn't that give me only 6 positions, 1 for each side of the cube?

    • How would I setup the array with all the above Quaternions? Like this below:
    Code (csharp):
    1.  
    2. var myQuatArray : Quaternion[];
    3.  
    and then put in all my (x,y,z) coords into the inspector?
    Though I just noticed that it's asking for a 'w' coord also... so maybe I don't want the type to be a Quaternion? Use a Vector3 maybe?
     
  10. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    It should be similar either way. I'm not that familiar with UnityScript though, so I can't provide much in the way of examples in that area. (There are plenty of other folks around here who can help with the UnityScript side of things though.)

    Right, learning how to create a custom class type would be a good first step. (You could accomplish the same thing using a simple array of quaternions and an array of integer indices, but that'd be a little roundabout.)

    You're thinking of quaternions and Euler angles as being basically equivalent, but they're not. In other words, the x, y, and z of a quaternion don't correspond to x, y, and z rotation angles. However, you can easily build a quaternion from a set of Euler angles as follows:

    Code (csharp):
    1. Quaternion q = Quaternion.Euler(xAngle, yAngle, zAngle);
    You might be able to build the array just by figuring out the Euler-angle representation for each of the 24 orientations and building the corresponding quaternions as shown above, but that sounds a bit arduous.

    What I'd probably do instead is use Quaternion.LookRotation() to build quaternions from basis vector pairs (forward and up). There will be 6 sets of 4 orientations. For each of the 6 sets of 4, the forward vector will be +/- Vector3.right/up/forward. Within a given set of 4, the up vector will be the 4 axis-aligned vectors perpendicular to the forward vector. As an example, the '+z' set might look like this (untested):

    Code (csharp):
    1. // ...
    2. Quaternion.LookRotation(Vector3.forward, Vector3.right),
    3. Quaternion.LookRotation(Vector3.forward, -Vector3.right),
    4. Quaternion.LookRotation(Vector3.forward, Vector3.up),
    5. Quaternion.LookRotation(Vector3.forward, -Vector3.up),
    6. // ...
    You could figure out the neighbor relationships manually, or you could rotate each orientation 4 times (left, right, up, and down) and then find the other orientation in the set that matches the resulting orientation most closely (you can use the quaternion dot product for this).

    Again, this is just how I'd do it. Others might recommend just going with a simple clamping method (which, while perhaps a little less elegant, might be easier to implement).
     
  11. KodaL

    KodaL

    Joined:
    Apr 4, 2008
    Posts:
    37
    Ok, now I think I'm confused. Will have to try to let this sink in some more.

    So would the 6 sets be: +x, -x, +y, -y, +z, -z?

    I looked up Quaternion.LookRotation(), and it says "Creates a rotation that looks along forward with the the head upwards along upwards." Not sure what that means, so that is probably the problem I'm having. It needs a 'forward' and an 'upwards' Vector3 argument to pass to it, but those arguments don't have to be 'Vector3.forward' and 'Vector3.up' I'm assuming since your example code uses 'Vector3.right' as well.

    Will try to do some more searching for LookRotation() and see what I find.
     
  12. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    First, keep in mind that since I haven't tried this method myself, it's a bit speculative. I'm > 99% sure what I'm describing is correct, but in order to be 100% sure I'd have to actually try it out (sometimes problems with a method or algorithm don't reveal themselves until you actually try to implement it).

    Yup.

    Right, the 'forward' and 'up' arguments to LookRotation() don't have to be anything in particular. There's a couple of constraints on the input, but they're not really relevant to this discussion so I won't confuse things by bringing them up. Basically, the 'forward' argument can be whatever you want, and specifies in which way the object is 'looking'. That's not enough to uniquely specify an orientation though (imagine a plane flying in a specific direction - it can still roll around the forward vector without changing its direction). So, the 'up' argument provides an additional constraint that allows the orientation to be uniquely defined. To use the example of a human looking at something, the 'forward' vector would specify the direction in which you're looking, and the 'up' vector would point in the direction of the top of your head. With those two vectors, you can specify any orientation.

    So to recap, by submitting one of the six signed cardinal basis vectors (+/-x, +/-y, +/-z, just as you said) for the 'forward' vector, and then for each, submitting one of the signed cardinal basis vectors that are perpendicular to the first vector for the 'up' vector, you get the 24 orientations I mentioned. (My earlier example showed how one of these sets of four would be generated.)
     
  13. KodaL

    KodaL

    Joined:
    Apr 4, 2008
    Posts:
    37
    Ok. I think I have this part understood, though I'm not sure where/what to do with it yet. I'm guessing this is all going to go into an array or the 'class':

    +z
    Code (csharp):
    1.  
    2. Quaternion.LookRotation(Vector3.forward, Vector3.right),
    3. Quaternion.LookRotation(Vector3.forward, -Vector3.right),
    4. Quaternion.LookRotation(Vector3.forward, Vector3.up),
    5. Quaternion.LookRotation(Vector3.forward, -Vector3.up),
    6.  
    -z
    Code (csharp):
    1.  
    2. Quaternion.LookRotation(-Vector3.forward, Vector3.right),
    3. Quaternion.LookRotation(-Vector3.forward, -Vector3.right),
    4. Quaternion.LookRotation(-Vector3.forward, Vector3.up),
    5. Quaternion.LookRotation(-Vector3.forward, -Vector3.up),
    6.  
    +x
    Code (csharp):
    1.  
    2. Quaternion.LookRotation(Vector3.right, Vector3.forward),
    3. Quaternion.LookRotation(Vector3.right, -Vector3.forward),
    4. Quaternion.LookRotation(Vector3.right, Vector3.up),
    5. Quaternion.LookRotation(Vector3.right, -Vector3.up),
    6.  
    -x
    Code (csharp):
    1.  
    2. Quaternion.LookRotation(-Vector3.right, Vector3.forward),
    3. Quaternion.LookRotation(-Vector3.right, -Vector3.forward),
    4. Quaternion.LookRotation(-Vector3.right, Vector3.up),
    5. Quaternion.LookRotation(-Vector3.right, -Vector3.up),
    6.  
    +y
    Code (csharp):
    1.  
    2. Quaternion.LookRotation(Vector3.up, Vector3.right),
    3. Quaternion.LookRotation(Vector3.up, -Vector3.right),
    4. Quaternion.LookRotation(Vector3.up, Vector3.forward),
    5. Quaternion.LookRotation(Vector3.up, -Vector3.forward),
    6.  
    -y
    Code (csharp):
    1.  
    2. Quaternion.LookRotation(-Vector3.up, Vector3.right),
    3. Quaternion.LookRotation(-Vector3.up, -Vector3.right),
    4. Quaternion.LookRotation(-Vector3.up, Vector3.forward),
    5. Quaternion.LookRotation(-Vector3.up, -Vector3.forward),
    6.  
    So, the second part now is this:
    Do you happen to have an example you could share of how to do this part? The above information basically has the neighbors too, right?
     
  14. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    Based on a quick look at least, your list of orientations looks correct. So that's a good start :)

    You have at least a couple of options. One would be to store an array of quaternions (which it looks like is what you have now), and then a two-dimensional array of ints (24x4) that stores, for each orientation, the indices of the four neighbor orientations.

    The other option would be to create a custom class (as previously mentioned) that stores both the quaternion and references to the four neighbor orientations.

    I'd have to write it (I don't have anything off-hand). It's a little involved (potentially, at least), but I can give you some hints.

    If you're talking about the list you posted, then all the orientations are indeed listed there, but the connectivity information (that is, which orientations are neighbors of which) is not included.

    This is somewhat speculative, but here's what I'd probably try first.

    I'm not sure which axes you want to rotate around, but for the sake of argument, say it's the world x and y axes. For each orientation, there are then 4 rotations that can be applied: +/-90 degrees about the world x or y axis. So the first thing I'd do is build a quaternion representing each of these rotations (there should be an 'axis-angle' quaternion function that'll do this for you).

    For each of the 24 orientations, you'll then want to rotate it 4 times, once for each of the aforementioned rotations. The code to generate one of these rotated orientations would look like this (untested):

    Code (csharp):
    1. Quaternion currentOrientation = rotation[j] * orientation[i];
    In this example 'rotation' is an array containing the 4 aforementioned relative rotations, and 'orientation' is an array containing the 24 orientations. i and j are the indices of a nested loop.

    Once you've computed currentOrientation, compute the absolute value of the dot product of currentOrientation and each of the 24 orientations. Whichever orientation returns the absolute dot product closest to 1 is the neighbor for that orientation.

    The whole code for this would be a little involved and would incorporate several nested loops. If the above isn't enough information and you still find you're stuck on this, I can try to sketch it out for you.
     
  15. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    Wow, after about an hour of tinkering with this, I finally figured out how to do what you are asking.. not trivial in the slightest. I would say almost none of the stuff discussed here actually worked. I had to find a way to get around the existing rotation of the object. So if you turned it one way, it would rotate to another or in another direction.

    You are basically facing two issues here. One is how to determine which direction to flip, and then how to determine which side you are going to be showing.

    So I approached it in the mouse moving aspect first, then the flipping aspect second. If you didn't pull the cube more than 45 degrees then you werent flipping. ;)

    Secondly, I had a big fight over Quaternions, localEulerAngles and about every other method I could think of. I went to the can. (where all big ideas come from) and came back thinking of a different method:

    Why not use the object, to rotate it's self. No Quaternion * Quaternion stuff, which was only working in one direction. If the object was upside down, left is right and that isn't going to work at all.

    So I looked and I said. OK, What rotates with no regard to how the object is rotating?... Transform.Rotate(axis, angle, Space.World)!!!! Up is ALWAYS Up....

    So I proceeded to make my little drag program. I made it rotate over or back in .15 seconds so it was pretty quick, played with the axis until everything came out right....

    I got past the current rotation checks by forcing it back to the last rotation, then adding 90 degrees on the axis I was spinning. Set the transform back to the rotation, and told it to go from there.

    I hope this is what you were looking for.

    Code (csharp):
    1.  
    2. var lastRotation : Quaternion;
    3. var targetRotation : Quaternion;
    4. var mousePos : Vector2=Vector2.zero;
    5. var offset : Vector2=Vector2.zero;
    6. var doMouse=true;
    7. private var dir=0;
    8. var useX=false;
    9.  
    10. var rotStart : float=0.0;
    11. private var rotIn : float = 0.15;
    12.  
    13. var currentRotation=Vector3.zero;
    14.  
    15. function Start(){
    16.     lastRotation=transform.rotation;
    17.     targetRotation=transform.rotation;
    18. }
    19.  
    20. function Update () {
    21.     if(doMouse){
    22.         if(Input.GetButtonDown("Fire1")){
    23.             mousePos=Input.mousePosition;
    24.             offset=Vector2.zero;
    25.             lastRotation=transform.rotation;
    26.         }
    27.        
    28.         if(Input.GetButton("Fire1")){
    29.             offset=(Input.mousePosition - mousePos)/ Screen.width * 135;
    30.             transform.rotation=lastRotation;
    31.             var amount=0.0;
    32.             var axis=Vector3.zero;
    33.             if(Mathf.Abs(offset.x) > Mathf.Abs(offset.y)){
    34.                 axis=-Vector3.up;
    35.                 amount= offset.x;
    36.             } else{
    37.                 axis=Vector3.right;
    38.                 amount=offset.y;
    39.             }
    40.             transform.Rotate(axis, amount, Space.World);
    41.         }
    42.        
    43.         if(Input.GetButtonUp("Fire1")){
    44.             var rot=transform.rotation;
    45.             transform.rotation=lastRotation;
    46.             axis=Vector3.zero;
    47.             if(Mathf.Abs(offset.x) > Mathf.Abs(offset.y)){
    48.                 if(offset.x> 45.0){axis=-Vector3.up;}
    49.                 if(offset.x<-45.0){axis=Vector3.up;}
    50.             } else {
    51.                 if(offset.y> 45.0){axis=Vector3.right;}
    52.                 if(offset.y<-45.0){axis=-Vector3.right;}
    53.             }
    54.             if(axis!=Vector3.zero) transform.Rotate(axis, 90, Space.World);
    55.             targetRotation=transform.rotation;
    56.             transform.rotation=rot;
    57.             rotStart=Time.time;
    58.             lastRotation=transform.rotation;
    59.             doMouse=false;
    60.         }
    61.     } else {
    62.         transform.rotation=Quaternion.Slerp(lastRotation, targetRotation, (Time.time-rotStart) / rotIn);
    63.         if((Time.time-rotStart) / rotIn>=1.0){
    64.             lastRotation=targetRotation;
    65.             transform.rotation=targetRotation;
    66.             doMouse=true;
    67.         }
    68.     }
    69.    
    70.    
    71.    
    72. }
    73.  
    74. function Approximately(v1, v2){
    75.     return Mathf.Abs(v1.x-v2.x)<0.2  Mathf.Abs(v1.y-v2.y)<0.2  Mathf.Abs(v1.z-v2.z)<0.2;
    76. }
    77.  
    78. function fixEulerAngles(vector3 : Vector3) {
    79.     var v=vector3;
    80.     if(v.x<-180)v.x+=360;
    81.     if(v.y<-180)v.y+=360;
    82.     if(v.z<-180)v.z+=360;
    83.     if(v.x> 180)v.x-=360;
    84.     if(v.y> 180)v.y-=360;
    85.     if(v.z> 180)v.z-=360;
    86.     return v;
    87. }
    88.  
     
  16. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    By 'none of the stuff discussed here', are you referring to the method I proposed? Are you saying the method I proposed is incorrect?
     
  17. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    BigMisterB, I don't know if this:

    Was in reference to my posts, but if so, no, the method I proposed works fine.

    You did however manage to motivate me to write up and post an implementation :)

    @The OP: Here's a complete, working implementation of the method I described in my previous posts. To try it out, just attach it to an object (e.g. a cube) and use the arrow keys to rotate.

    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class CubeRotation : MonoBehaviour
    4. {
    5.     Quaternion[] orientations = new Quaternion[] {
    6.         Quaternion.LookRotation(Vector3.forward, Vector3.right),
    7.         Quaternion.LookRotation(Vector3.forward, -Vector3.right),
    8.         Quaternion.LookRotation(Vector3.forward, Vector3.up),
    9.         Quaternion.LookRotation(Vector3.forward, -Vector3.up),
    10.         Quaternion.LookRotation(-Vector3.forward, Vector3.right),
    11.         Quaternion.LookRotation(-Vector3.forward, -Vector3.right),
    12.         Quaternion.LookRotation(-Vector3.forward, Vector3.up),
    13.         Quaternion.LookRotation(-Vector3.forward, -Vector3.up),
    14.         Quaternion.LookRotation(Vector3.right, Vector3.forward),
    15.         Quaternion.LookRotation(Vector3.right, -Vector3.forward),
    16.         Quaternion.LookRotation(Vector3.right, Vector3.up),
    17.         Quaternion.LookRotation(Vector3.right, -Vector3.up),
    18.         Quaternion.LookRotation(-Vector3.right, Vector3.forward),
    19.         Quaternion.LookRotation(-Vector3.right, -Vector3.forward),
    20.         Quaternion.LookRotation(-Vector3.right, Vector3.up),
    21.         Quaternion.LookRotation(-Vector3.right, -Vector3.up),
    22.         Quaternion.LookRotation(Vector3.up, Vector3.right),
    23.         Quaternion.LookRotation(Vector3.up, -Vector3.right),
    24.         Quaternion.LookRotation(Vector3.up, Vector3.forward),
    25.         Quaternion.LookRotation(Vector3.up, -Vector3.forward),
    26.         Quaternion.LookRotation(-Vector3.up, Vector3.right),
    27.         Quaternion.LookRotation(-Vector3.up, -Vector3.right),
    28.         Quaternion.LookRotation(-Vector3.up, Vector3.forward),
    29.         Quaternion.LookRotation(-Vector3.up, -Vector3.forward)
    30.     };
    31.    
    32.     int[,] neighborIndices = new int[24, 4];
    33.  
    34.     int orientationIndex;
    35.     Quaternion orientation1;
    36.     Quaternion orientation2;
    37.     bool rotating;
    38.     float rotationTimer;
    39.    
    40.     void Start()
    41.     {
    42.         Quaternion[] rotations = new Quaternion[] {
    43.             Quaternion.AngleAxis(-90, Vector3.up),
    44.             Quaternion.AngleAxis(90, Vector3.up),
    45.             Quaternion.AngleAxis(-90, Vector3.right),
    46.             Quaternion.AngleAxis(90, Vector3.right)
    47.         };
    48.        
    49.         for (int i = 0; i < 24; ++i) {
    50.             for (int j = 0; j < 4; ++j) {
    51.                 var currentOrientation =
    52.                     rotations[j] * orientations[i];
    53.                 float maxDot = 0f;
    54.                 int neighborIndex = 0;
    55.                 for (int k = 0; k < 24; ++k) {
    56.                     float dot = Mathf.Abs(Quaternion.Dot(
    57.                         currentOrientation, orientations[k]));
    58.                     if (dot > maxDot) {
    59.                         maxDot = dot;
    60.                         neighborIndex = k;
    61.                     }
    62.                 }
    63.                 neighborIndices[i, j] = neighborIndex;
    64.             }
    65.         }
    66.        
    67.         transform.rotation = orientations[orientationIndex];
    68.     }
    69.    
    70.     void Update()
    71.     {
    72.         const float rotationTime = .5f;
    73.         if (rotating) {
    74.             rotationTimer += Time.deltaTime;
    75.             if (rotationTimer >= rotationTime) {
    76.                 transform.rotation = orientation2;
    77.                 rotating = false;
    78.             } else {
    79.                 float t = rotationTimer / rotationTime;
    80.                 transform.rotation = Quaternion.Slerp(
    81.                     orientation1, orientation2, t);
    82.             }
    83.         } else {
    84.             int rotation = -1;
    85.             if (Input.GetKeyDown("right")) {
    86.                 rotation = 0;
    87.             } else if (Input.GetKeyDown("left")) {
    88.                 rotation = 1;
    89.             } else if (Input.GetKeyDown("down")) {
    90.                 rotation = 2;
    91.             } else if (Input.GetKeyDown("up")) {
    92.                 rotation = 3;
    93.             }
    94.             if (rotation != -1) {
    95.                 orientationIndex =
    96.                     neighborIndices[orientationIndex, rotation];
    97.                 orientation1 = transform.rotation;
    98.                 orientation2 = orientations[orientationIndex];
    99.                 rotationTimer = 0f;
    100.                 rotating = true;
    101.             }
    102.         }
    103.     }
    104. }
    This is just an example, and doesn't necessarily exemplify best practices with respect to coding style and so forth. Also, to keep things simple, I just used the arrow keys to control the rotations (you can of course use whatever control scheme you want, e.g. mouse movements, swipes, etc.).

    Note that there are no approximations, no tolerances, no corrections, and no use of Euler angles in this code. It's completely deterministic and reliable, and is not susceptible to numerical error.

    Assuming this is in fact the behavior you're after, then I'd highly recommend using this method to implement the rotations.
     
    DRpatak and srmols like this.
  18. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    @Jesse It was more the implementation of using the mouse to drag the box to rotate, then release it and make it go to the next part.

    The code I wrote also is not susceptible to mathematical erroring. It uses a solid Slerp not an approximation.

    Oops, sorry, I see what Jesse saw... No, you dont need the two functions at the end. They were left over.. ;)
     
  19. KodaL

    KodaL

    Joined:
    Apr 4, 2008
    Posts:
    37
    Wow! Thanks for the 2 examples @Big and @Jesse! I've been going over them the past couple days and figuring out how they work. Not the easiest things for me to decipher, but definitely worth the time to study them! Even went over the unify wiki C# tutorials to better understand your script Jesse. It's alot easier to see what you were discussing earlier with me, with a clear example like that!

    Next step is converting it to using touch input and then maybe I can tackle creating a class out of it, and how to go about using it correctly. (Never used/implemented a class before)

    A question I have in the meantime: Is it a possibility (with some tweaking of course) to put one of these scripts onto an empty "player" gameobject, and then use that to control the rotation of the other cube object? I'm thinking if I use a raycast to get the cube object and then control it from the "player" gameobject...
     
  20. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    If you use UnityScript/JavaScript, then I think you probably have implemented a class before. I'm not sure of the details (I don't use UnityScript myself), but I think a UnityScript script is implicitly wrapped in a class that derives from (i.e. extends) MonoBehaviour. UnityScript hides this detail (and a lot of other details) from you (for better or for worse).

    Just about anything is possible.

    As for what you describe, yes, it's definitely possible. The first thing you'll want to do is identify which parts of the script I posted should be specific to each cube, and which parts can be shared between all cubes.

    Just as a hint, the 'orientations' and 'neighbor' arrays can be shared between all cubes (since that data never changes). Most or all of the rest of the data (the current orientation index, timing and state information, etc.) will differ for each cube.

    A logical way to proceed would probably be to have the 'player' script store the orientation and neighbor indices arrays, and then have a 'cube' script that handles the rotation and so on. When you raycast a cube, you can then grab a reference to the 'cube' script using GetComponent(), and then call a function that tells the cube it's time to rotate, and assigns it a new target orientation.

    Some data will have to be exchanged as part of this process (for example, in order to determine the cube's new orientation, you need the orientation array, the neighbor indices array, and the current orientation index of the cube). There are various ways the two classes (the 'player' class/script and the 'cube' class/script) can interact with one another for this purpose (e.g. accessors/properties, function arguments, etc.).

    Also, while I generally recommend C#, keep in mind that all of this can be done using UnityScript (and Boo, presumably) as well.
     
  21. KodaL

    KodaL

    Joined:
    Apr 4, 2008
    Posts:
    37
    Excellent!

    While I am still figuring out the original code and dissecting it, I ended up converting your (Jesse's) code over to JS then changing over to touch input (works ok but needs tweaking still), and now I am working on raycasting to control the cube.

    Instead of splitting it into 2 scripts (player and cube), I started by having it all self-contained in a single script that goes into the empty Player game object. So far it seems to be making the cube object rotate but I'm having issues with this line of code in the Start() function:
    Code (csharp):
    1.         transform.rotation = orientations[orientationIndex];
    The cube doesn't rotate correctly once this script is off of the actual game object. So I'm guessing this line of code is what set's the cube's orientation to begin with and allows the neighbor orientations to work correctly? This appears to be why I can't just use the 1 script on Player to control the cube.

    I will set forth now to break it down into 2 scripts and see if I can get it to work that way. Though I'm not sure how to access the 'orientations:Quaternion[]' array from the cube script, if it is in the player script.

    Or is this where a class would be better to use and then just at the top of my cube script put:
    Code (csharp):
    1. using CubeRotation;
    I think that's correct-ish.
     
  22. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    Right, that line sets the cube's initial orientation.

    It probably won't work particularly well to use a single 'player' script. One way or another, each cube needs to track its own state (its current orientation index and so on), and having separate 'player' and 'cube' scripts would probably be the easiest way to facilitate this.

    Generally speaking, you can always find a way to access data from one script or object from another script or object if needed.

    For general info on accessing other game objects in the scene, read this. You can use GetComponent() to acquire references to components in other game objects. To allow one script to make use of data from another script, you can make the data public, or expose it through accessors, or pass it in to the functions that need it as an argument (the last option is arguably preferable from a software-design standpoint).

    Hm, I'm not sure if that has any bearing on the problem.
     
  23. KodaL

    KodaL

    Joined:
    Apr 4, 2008
    Posts:
    37
    That makes sense. Was a bit late when I posted and GetComponent() was just escaping me for some reason.

    For the 'using CubeRotation;' part: I thought I read if you create a class and put it in the Plugins folder, that you can use anything in the script/class without explicitly having to use GetComponent() for it. So I thought I could just put the line of code 'transform.rotation = orientations[myVar]' and it would instinctively know that 'orientations' was in the script in the Plugins folder since I put 'using.....' in my cube script. But I'm not too familiar with classes/plugins so I was probably way off base in their use. Doh.

    - Not sure how to make the data public but I think I'd rather use the last option you mentioned anyways.

    - Exposing it through accessors would be using GetComponent() to acquire the script, correct?

    - And I'm not familiar with the last option "pass it in to the functions that need it as an argument"? If that's the preferred way of doing things, then I'd like to use this way so I get in the habit of doing things the most efficient way possible. Or is this way also using GetComponent() ?

    Do you happen to have a link or example of what you mean by this?

    Once again, thanks for all the help and assistance! It is very appreciated!
     
  24. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    In UnityScript, script/class variables are public unless specified otherwise, I believe. In C#, struct/class variables are private unless stated otherwise. To make a variable public in C#, you'd write:

    Code (csharp):
    1. public int myVariable;
    Those are two difference things. GetComponent() is an accessor of sorts itself of course, but what I'm talking about is this:

    Code (csharp):
    1. public int myVariable;
    As opposed to this:

    Code (csharp):
    1. int myVariable;
    2. int GetMyVariableValue() { return myVariable; }
    Basically, it's the difference between exposing the variable directly, and providing an interface through which the variable can be queried and/or modified.

    Note that in C#, accessors are typically implemented as properties.

    Here's an example function definition:

    Code (csharp):
    1. void MyFunction(int someDataINeed, string[] someOtherDataINeed) { ... }
    The idea is that the data the function needs to access in order to do what it needs to do is passed into the function via function arguments.

    Not off-hand. This is just basic programming stuff though (not specific to Unity), so I'd just look for references/books/tutorials on C# (if you're using C#) or on programming or software design in general.
     
    DRpatak likes this.
  25. gamerant

    gamerant

    Joined:
    Jun 27, 2013
    Posts:
    16
    Hi there.Jesse thank you for the script.It's great.
    I used it and had it altered so that the game object to which it is attached wouldn't rotate when the game starts unless i provide the input.However recently my laptop crashed and i lost all my work including the altered script.Now i just can't for the life of me figure out how to alter the script so it works like it did before.


    I know that the code responsible for making the object rotate by 90 degrees before any input is pressed is in the start function bellow,but i can't figure out how to alter this correctly so it doesn't do that.
    Do any of you guys have any suggestions how to go about this?
    Thanks in advance :p

    Code (csharp):
    1. void Start()
    2.  
    3.     {
    4.  
    5.         Quaternion[] rotations = new Quaternion[] {
    6.  
    7.             Quaternion.AngleAxis(-90, Vector3.up),
    8.  
    9.             Quaternion.AngleAxis(90, Vector3.up),
    10.  
    11.             Quaternion.AngleAxis(-90, Vector3.right),
    12.  
    13.             Quaternion.AngleAxis(90, Vector3.right)
    14.  
    15.         };
    16.  
    17.         for (int i = 0; i < 24; ++i) {
    18.  
    19.             for (int j = 0; j < 4; ++j) {
    20.  
    21.                 var currentOrientation =
    22.  
    23.                     rotations[j] * orientations[i];
    24.  
    25.                 float maxDot = 0f;
    26.  
    27.                 int neighborIndex = 0;
    28.  
    29.                 for (int k = 0; k < 24; ++k) {
    30.  
    31.                     float dot = Mathf.Abs(Quaternion.Dot(
    32.  
    33.                         currentOrientation, orientations[k]));
    34.  
    35.                     if (dot > maxDot) {
    36.  
    37.                         maxDot = dot;
    38.  
    39.                         neighborIndex = k;
    40.  
    41.                     }
    42.  
    43.                 }
    44.  
    45.                 neighborIndices[i, j] = neighborIndex;
    46.  
    47.             }
    48.  
    49.         }
    50.         transform.rotation = orientations[orientationIndex];
    51.  
    52.     }
     
  26. srmols

    srmols

    Joined:
    Nov 3, 2014
    Posts:
    10
    Six years later, your script still helps. Thank you very much, Jesse!
     
  27. DRpatak

    DRpatak

    Joined:
    Dec 14, 2014
    Posts:
    3
    Hello. I like to thank Jessie for opening my eyes. Not 4 days ago i was creating similar script doing exact same thing. As rotation was not big issue but rather direction uppon rotating cube in diferent directions. I was using rigidbody and adding force to cube but the principles are the same...

    I was thinking about how make script know wich axis is right axis to add force uppon. I taught as much to make a list or in your case array with predefined directions. But i decided not to follow my taughts as i would make a mess of things. Code entangled with ifs and elses and other nonsence i was thinking about...

    I actually did it by reseting rotation to zero when cube completely stopped. Then i would need to know only 4 directions xD... It was more like hack and slash really.
    Now i studied your code and implemented good sum of your code and tweaked it to my needs..
    You sir are brain savier as surely you saved mine..

    Thank you.