rotate around X and Y to face a point
note: entity must be facing north in Max by default when imported
this one rotates directly to a target location
function RotateXYToPoint(e,x,y,z)
local function GetXYRotationToPoint(e,x,y,z)
local x1,y1,z1 = x,y,z
local x2,y2,z2 = GetEntityPosAng(e)
local tay = math.atan2( x2 - x1, z2 - z1 ) * ( 180.0 / math.pi )
local nx = math.abs(x2 - x1)
local nz = math.abs(z2 - z1)
local tax = 0
if nx > nz then
else
tax = math.atan2( x2 - x1, y2 - y1 ) * ( 180.0 / math.pi )
end
local dx, dz = x2 - x1, z2 - z1
local dist = math.sqrt(dx*dx+dz*dz)
tax = math.atan2(dist , y2 - y1 ) * ( 180.0 / math.pi )
--no idea why we take away 90 degrees here but it seems to be needed to face the z direction rather than up
tax = tax - 90
return tax,tay,0
end
local tax,tay, taz = GetXYRotationToPoint(e,x,y,z)
CollisionOff(e)
SetRotation(e,tax,tay,taz)
end
or this one will just return the angles required so you can use them later
function GetXYRotationToPoint(e,x,y,z)
local x1,y1,z1 = x,y,z
local x2,y2,z2 = GetEntityPosAng(e)
local tay = math.atan2( x2 - x1, z2 - z1 ) * ( 180.0 / math.pi )
local nx = math.abs(x2 - x1)
local nz = math.abs(z2 - z1)
local tax = 0
if nx > nz then
else
tax = math.atan2( x2 - x1, y2 - y1 ) * ( 180.0 / math.pi )
end
local dx, dz = x2 - x1, z2 - z1
local dist = math.sqrt(dx*dx+dz*dz)
tax = math.atan2(dist , y2 - y1 ) * ( 180.0 / math.pi )
--no idea why we take away 90 degrees here but it seems to be needed to face the z direction rather than up
tax = tax - 90
return tax,tay,0
end
bonus is this get point on edge of a sphere (or hemisphere) function i used to generate my target positions
you pass in the distance from the center you want the position to be and an optional true or false for if you want it to be a hemisphere or not (i.e. if you set second param to false it will also return negative y values)
the returned values can be added to an origin point (i.e. the center) to get positions on the edge of a sphere around it
function GetRandomPointOnSphereEdge(distance, uponly)
local uponly = uponly or true
local cos = math.cos
local sin = math.sin
local angle = math.rad(math.random(1,360))
local x = distance * cos(angle) * sin(angle)
local y = distance * sin(angle) * sin(angle)
if not uponly and math.random(1,2) == 1 then y = y * -1 end
local z = distance * cos(angle)
return x,y,z
end
an example usage can be seen here to rotate this gun to face the targets that randomly position themselves around it in a hemisphere
note: this is using amen's quatlib functions to do the transition rotations
to do the slow rotation it looks like this (note: just a snippet, this won't work as is)
-- get our position and angle
local ex,ey,ez,eax,eay,eaz = GetEntityPosAng(e)
-- get the position of our target
local tx,ty,tz = GetEntityPosAng(sg.target)
-- aim below target because our gun is angled up
-- we're going to do some checking here to see how close the target is
-- and we reduce how far below the object we are aiming the closer we are
local dist = math.sqrt(sqrd3(ex,ey,ez,tx,ty,tz))
if dist > 500 then dist = 500 end
dist = dist / 500
ty = ty - 245 * dist
-- work out our desired angle
local tax,tay,taz = GetXYRotationToPoint(e,tx,ty,tz)
local rad = math.rad
-- convert the angles to radians so they work with the quatlib
eax,eay,eaz = rad(eax),rad(eay),rad(eaz)
tax,tay,taz = rad(tax),rad(tay),rad(taz)
-- this is the code that'll do the transition over time via quaternions
local q1 = Q.FromEuler(eax,eay,eaz)
local q2 = Q.FromEuler(tax,tay,taz)
local q3 = Q.SLerp(q1,q2,GetElapsedTime() * sg.rotatespeed)
-- convert back euler angles
local fax,fay,faz = Q.ToEuler(q3)
local deg = math.deg
-- convert back to degrees for GG
fax,fay,faz = deg(fax),deg(fay),deg(faz)
SetRotation(e,fax,fay,faz)
note2: sqrd3() is just a basic distance check (i.e. amen's sqrd() function with y added in)
function sqrd3(x1, y1, z1, x2, y2, z2)
local dx,dy,dz = x1 - x2, y1 - y2, z1 - z2
return dx*dx+dy*dy+dz*dz
end