Smooth Rotation

So here I am again with another question, luckily it isn’t about a major glitch due to some typo like a couple other times I’ve posted, it’s a question about methodology. Right now in my 3rd person platformer setup the character rotates according to the camera’s direction and that works fine, but it isn’t smooth. The character immediately aligns to the proper orientation, he doesn’t smoothly rotate there. Is there a way to get him to rotate to the proper angle? I’ve tried using applyRotation and having a limit based on his orientation, but that just made him spin uncontrollably, it didn’t properly detect his orientation. If anyone can help me find a way to do it smoothly thatd be great!

Blend File: https://www.dropbox.com/s/htsfiqwpl8hrhfv/ambitious%20project.blend

I like the name of the blend… And you’ve done a great job.

Before I get onto what you asked about, I just thought that I’d mention that the function normalize in functions.py is redundant. If you want the normal vector, you can simply use vector.normalized if it is a mathutils.Vector.
Also probably good to note is that obj.orientation is deprecated, use o.worldOrientation instead

And for other people looking at the blend, the relevant code is lines 90-122 of the file control (which you should rename to control.py)

So as you know, you’re currently just using “o.orientation = ori” which of course sets it instantly. Using applyRotation is not what you want because it rotates it by that amount, rather than setting it to a direction.

There are a number of ways to solve this problem. One is to write a function which takes the intended angle and current angle and returns an angle half-way between. Then set the orientation to the new value.
What I would do though is stop using angles and start using vectors. Vectors are confusing until one day, they will click into place and then movement/direction becomes really simple.
So instead of using o.orientation, you use o.getAxisVect
and instead of setting o.orientation, you use o.alignAxisToVect, which, can take a delay value, and do the smooth movement.

However, that would require quite an overhaul of the scripts.

No amount of overhaul is too great! I want this game to be done right, and I was planning on overhauling everything to use a state system anyways, which will allow for more flexibility as I’m sure you know. I’ve been looking into vector math and vectors but as to actually using that math in blender I just have been a little lost on, anything you could point me to would help. Obviously I’m going to try blender’s api first, but if you know of anything else that’d really help. Thanks a bundle!

If you just want a quick-fix, replace the:

ori[2] = camRot

with:

ori[2] = ori[2]+(-ori[2]+camRot)/5

where 5 is the number of frames to smooth.

I’m just writing a post on vectors for you!

I will DEFINITELY read that post! I appreciate the quick fix, but I really want to do this game right. Having to learn vectors isn’t the kind of thing that’s going to stop me. Now, the end of the world, nuclear holocaust, getting shot, those are the kind of things that’ll end up stopping me.

Right, so vectors.

A vector can indicate many things, and pretty common is rotation. For a game object, we tend to assume that the y axis is forward. So it’s z rotation is 0. This is confusing, right? The way it faces depends on a vertical thing.

With vectors, we can ignore this. A simple definition of a rotation vector is to draw a line from the center of a graph to a 3D location. So to find out what way our player is facing, instead of saying his z rotation is 0, we can say that his y axis is pointing along the vector [0,1,0] (his local axis is matching the global axis)

In blender’s API, we can get the way an object is facing using obj.getAxisVect([0,1,0]) for the y axis.

And so how about setting vectors? For that we use obj.alignAxisToVect(vector, 1, 0.5) where the 1 is the y axis and the 0.5 is to rotate only half-way this frame.
In this case, we may want to get the vector of the camera, but we only want the Z rotation of the camera. Unfortunately this isn’t as simple as grabbing the last component of the vector, because this is how much vertical it is facing. Instead we want only the x and y components, or we set the z component to zero.

So here’s some quick code:


camera_ori = camera.getAxisVect([0,0,1]) #because the z axis of a camera is forwards
desired_ori = camera_ori
desired_ori[2] = 0

player.alignAxisToVect(-desired_ori, 1, 0.5)

And if we want to align to a different axis (say the player is pressing A) we can simply set the axis differently:


player.alignAxisToVect(-desired_ori, 1, 0,5) #facing away from us. Remember that camera faces -z axis!!!
player.alignAxisToVect(desired_ori, 1, 0,5) #facing us
player.alignAxisToVect(desired_ori, 0, 0,5) #sideways
player.alignAxisToVect(-desired_ori, 0, 0,5) #other sideways

One other thing that may happen is he falls over sideways. If this is the case, you may have to align his Z axis to the world Z axis:

player.alignAxisToVect([0,0,1], 2, 1)

— edit, having read your post —
About whether this is the ‘right way’ there is no right way. Do whatever works, and what you understand.
As the writer of one web comic puts it:

Ok thanks! I have a question though, is there an easy way to figure out what numbers I need to use in alignAxisToVect?

Oh also you got your negatives mixed up, the facing us equation faces forward and vice versa.

Ok thanks! I have a question though, is there an easy way to figure out what numbers I need to use in alignAxisToVect?

The first parameter is the vector to align to (ie [0,0,1]) for straight up.
The second parameter is the axis you wish to align (x = 0, y = 1, z = 2)
The third parameter is the percentage you wish to align it to (0 = won’t move, 1 = all in this frame, 0.5 = half the rotation this frame, 0.25 = a quarter this frame. Note that 0.5 will not take two frames, but will follow an exponential decay to face the right way.)

Oh also you got your negatives mixed up, the facing us equation faces forward and vice versa.

Quite right, camera faces -z not z. Fixing that now.

Thank you so much for this. Learning will only do me good, and that comic about the “right” way was spot on, when I said I wanted to do things the “right” way I meant I wanted it to feel good and not be lazy slapped in code. Since this is my first real project in python a lot of it could probably be refined and redone and I’m sure this is just one of the many things I can do over again.

Ok so when I change the value that controls how quickly the player rotates to that vector I run into a problem when it is less than 0.5. For some reason the player has some delay before turning, understandable, but then on top of that he sometimes doesn’t rotate at all, specifically when you rotate to the direction 180 degrees behind you whatever that may be. Are there some problems with alignAxisToVect and values less than 0.5 that I don’t know about? It works perfectly when it’s 0.5

Hmm. I’ve never had any problems with it. If you were exactly 180 degrees away, there may be a problem (which way do I go), but getting that exact should be impossible. (but then going from forwards to backwards is one of these cases I suppose).

alignAxisToVect() is pretty straight-forward and it should work with any value (0 doesn’t turn it :slight_smile: ). I don’t have time to dig into your .blend but:

  • check that you are indeed calling it when you expect the character to turn, ie there isn’t error or something that makes the script skip the part
  • check that you aren’t setting the character orientation in some other way in addition
  • check that the vector you are aligning to is correct. You can draw the vectors real time using render.drawLine


#bge.render.drawLine(start, end, [1,1,1])
#start and end are list of [x,y,z] world coordinates
#the last [1,1,1] is color in RGB

#for example draw the player x axis

player = bge.logic.getCurrentController().owner
vector = player.worldOrientation[0]
pos = player.worldPosition

start = pos
end = pos + vector

bge.render.drawLine(start, end, [1,1,1])

I’ve looked through it for anything like that and I can’t find any obvious problems. All is well as long as it is set to 0.5 or higher. I guess I’ll have to just keep messing around and try to figure something out. Thanks for the help!

Oh also how would I handle the diagonal movement? My best guess is using a DOT product of the y and x of the player’s vector, would that work?

Nope, you can just add them:

y = [0,1,0] (y movement)
x = [1,0,0] (x movement)
y+x = [1,1,0] (diagonal)

The dot product returns a scalar (non vector) value that is occasionally useful in evaluating how similar two vectors are.
The cross product is more useful, as it gives a vector perpendicular to both vectors. (but it isn’t applicable here)