1

I know there's a million questions on rotating, I've tried and read many things but I can't seem to solve this particular problem.

The only way I can think to explain it properly is with some poorly drawn diagrams so here goes!

I have a parent object, and a child object (here it's represented as a die where opposite faces add up to 7), the axes in this diagram are the parents X, Y, and Z directions

Starting point:

Starting point

I want the child object to have two methods that I'll call RotateZ and RotateX. RotateZ and RotateX should rotate the child dice around the parents Z and X axes in 90 degree steps.

RotateX:

Starting pointRotateX

RotateZ:

Starting pointRotateZ

The code I currently have is this

void RotateZ(int value) {
    transform.rotation *= Quaternion.AngleAxis(90f * value, pivot.transform.forward);
}

void RotateX(int value) {
    transform.rotation *= Quaternion.AngleAxis(90f * value, pivot.transform.right);
}

I should note pivot is the parent object

This appears to work great if the child starts at (0, 0, 0) rotation, repeatedly calling RotateZ or RotateX with (0, 0, 0) starting point works great. This problem comes if for example I call RotateX then RotateZ.

After the initial RotateX call it will look like this, giving the child a rotation of (90, 0, 0).

Starting point RotateX

Then after a RotateZ the desired result is this:

After RotateX enter image description here

But instead I end up with this:

After RotateX After RotateXZ

I'm really at a loss as to what I'm doing wrong. I have a feeling I've hit the limit of what I can do with Euler angles and I need to learn quaternions to prevent this problem. What rotation function do I need to get the desired result?

GP89
  • 6,600
  • 4
  • 36
  • 64
  • You have a spectacular problem. **Never use Quaternions for any reason.** It is very confusing they are mentioned in the Unity manual; they are only for specific internal use totally irrelevant to any normal game programming. In any event, all you are looking for is **RotateAround**. Enjoy – Fattie Jun 10 '16 at 20:31
  • Note too that you may well simply want to set the .eulerAngles. – Fattie Jun 10 '16 at 20:32
  • @JoeBlow I was using euler angles originally and had the exact same issue. I know there's limitations with euler angles where they wont do the job which is why quaternions exist. I thought this might be one of those cases which is why I tried switching the implementation. I know they're more confusing and less easier to use but if run into gimbal lock you dont have a choice but to use them, I don't think your sentiment on never using them is correct. – GP89 Jun 10 '16 at 21:01
  • Did you check out **RotateAround**. it's super-useful – Fattie Jun 10 '16 at 21:13
  • @JoeBlow It looks good, but I'm not sure if it would work. I need to rotate it around the parent's axis rather than the world coordinate axis – GP89 Jun 10 '16 at 23:57
  • 1
    that's easy: I'll explain in an answer. – Fattie Jun 10 '16 at 23:58
  • 1
    i'll also give you an absolutely critical tip. – Fattie Jun 10 '16 at 23:59

1 Answers1

2

It looks like what you need is the really awesome call

RotateAround

it's one of the most important in Unity. RotateAround doco

Don't forget RotateAround rotates around a vector - ANY vector.

Huge tip...

A ubiquitous technique in video game engineering is: you use markers" - meaning just an empty game object - which you attach to objects or to which you attach your objects.

Very often, you temporarily attach an object to a marker, so as to move it, and then remove it from the marker again.

Here's how to do it in code. Make a marker "controlRotator"...

  1. make controlRotator a child of your controlObject.

  2. make it perfectly straight locally: ie, simply exactly the same orientation as the parent, controlObject, ie, local rotation = identity

  3. next, cleverly set the WORLD position of controlObject to exactly the WORLD position of smallCube...

  4. your smallCube, make a note of it's parent. take smallCube off that parent.

  5. next, temporarily make smallCube a child of controlRotator

... and now you can ...

  1. rotate the controlRotator any way you like!

You simply rotate controlRotator, not the small cube.

Note that you are now doing exactly what you asked to do, you're rotating smallCube using the axis of the controlObject. Clever right?

  1. finally, put smallCube back on to the parent you saved in step 4.

This is exactly how you do it. In practice this is so commonplace that you'll have an extension or function to do it.

Note that it is fine to make the GameObject controlRotator on the fly as needed, and get rid of it after the twist. (It's only a mathematical abstraction.)

Further tip...

This operation should work "on" controlObject. Make a script called

TwistSomethingOnMyAxis.cs

and put that ON the CONTROLOBJECT.

It would have one call

 public void twistThisThing(Transform twistIt) {}

What you do is, when you want to twist SMALL CUBE, you actually call to the TwistSomethingOnMyAxis ON THE CONTROL OBJECT. You see? So like

TwistSomethingOnMyAxis:twistor = 
  controlObject.GetComponent<TwistSomethingOnMyAxis>();
twistor.twistThisThing( smallCube , .. 90 degrees on Y or whatever .. );

I hope that makes sense! Enjoy!

Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • Thanks `RotateAround` did the job! I've read through your tip and I think I'm starting to understand the steps and how they would work. I'll give it a try! Sounds like I've learnt a very useful trick! Thanks! For the further tip, would it work better as a static method in a static helper class rather than in components that can be attached. – GP89 Jun 11 '16 at 02:24
  • Also the further tip is good, my instinct would be to put that type of logic in a static method in a static helper class, or extend the method onto a type, but in Unity I really need to think more in components so it can be attached to any type, and anything can be given the behaviour of Twist – GP89 Jun 11 '16 at 02:27
  • Hi GP, glad it solved the problem. You know, defintely *don't* use statics (as a rule) in Unity, set that aside. It just doesn't work with an ECS system. One thing, if you're an experienced programmer fooling with Unity for the first time, **extensions** (categories) are central to the Unity Experience :) http://stackoverflow.com/a/35629303/294884 use extensions everywhere and always. – Fattie Jun 11 '16 at 12:27
  • You really hit the nail on the head about components, that is very instictive. Here's an essay on that: http://stackoverflow.com/a/37243035/294884 And while we're at it here's a good general rant! http://stackoverflow.com/a/37391504/294884 – Fattie Jun 11 '16 at 12:30
  • Yea only time I think statics would be useful or the right choice are for the model parts of the game for classes that aren't MonoBehaviour's and can be unit tested easily. Thanks for the reading I'll definitely check over those! What is ECS? Don't think I've seen that term before – GP89 Jun 11 '16 at 13:58
  • I used to use inheritance a lot when I was starting OO programming years ago, but as I went on I used it less and less in favour of components. Then moving to c# a few years ago with interfaces I find myself never really using inheritance at all, even before starting unity a couple of months ago. – GP89 Jun 11 '16 at 14:29
  • Read through those links, it's really encouraging I'm already using a lot of extensions and components and no inheritance already so sounds like I'm not doing too badly. One thing that I got caught out on that I didn't see mentioned there was I was making everything a GameObject, and when I came to write unit tests (i should write them first, I know! :P) I found that you can't really unit test MonoBehaviours. Since then I've began writing as much logic and decoupling as much as possible into plain c# objects that I can cover with unit tests – GP89 Jun 11 '16 at 14:57
  • for example this game I have game objects which have positions and rotations etc. and rules about where they can go etc. I wrote extension methods like `Describe` which returns a descriptor that defines all the characteristics of the game object that i'm interested in. Then the logic that defines how and where they can move, the model with state, can be fed these descriptors. This way I can create descriptors in tests, and one of these plain c# class models and pass them in and test for the expected output/behaviour. and in the game I can just call `Describe` to get the input for the model. – GP89 Jun 11 '16 at 15:01