Ok Teacher's back.
So two additions in this version.
1) A little bit of code you'll find in an awful lot of my scripts which makes the code FPS agnostic:
local controlEnt = nil
local lastTime = 0
local tDiff = 1
....
if controlEnt == nil then controlEnt = e end
local timeNow = g_Time
if controlEnt == e then
if lastTime ~= 0 then
tDiff = ( timeNow - lastTime ) / 20
end
lastTime = timeNow
end
What this does is basically measure how long between frames and converts it to a fraction relative to a 'standard' frame time, I chose 20 ms as that is 50 FPS which is a pretty good value. Then this fraction can be used as a modifier in all movement related code to ensure that regardless of FPS movement distances will be the same.
The controlEnt bit just ensures that you only run the code once per frame.
2) Quaternion stuff (AKA scary voodoo):
local quat = Q.FromEuler( rad( xa ), rad( ya ), rad( za ) )
local rotquat = Q.FromEuler( 0, rad( rotSpeed * tDiff ), 0 )
xa, ya, za = Q.ToEuler( Q.Mul( quat, rotquat ) )
CollisionOff( e )
SetRotation( e, deg( xa ), deg( ya ), deg( za ) )
…
Ok so not really that scary, just don't try to understand how it works (unless you regularly converse with imaginary 4th dimensional friends who can calculate the square root of -1 in their heads that is) just treat it like a magical solution to a difficult problem.
So what does it mean?
Euler angles are evil, manipulating them is just plain impossible so instead we convert them to a quaternion, basically just a magical thing that represents the angular component of the entity.
Once in this magical form we can do stuff like multiply quats together (actually it's nothing like any multiplying you may of come across, trust me I wrote the code!) which results in a new quat that now represents the angular component of the first quat rotated by the second one (the order is important!).
In this case we first convert the current euler angles of the entity to a quat, then we create a second quat representing a rotation around the Y axis (the entities Y axis, not the world axis), 'multiply' them together to get a quat representing the resulting 'real world' angular components, convert them back to Euler angles and Bobby's your gender neutral relative.
Note that GG does stuff in degrees whereas all normal 3D manipulation is done in radians, so make sure to convert to radians right at the start, do all your math and finally convert back to degrees before calling any GG positional functions.
Been there, done that, got all the T-Shirts!