This is what I'm using. This is in a schedule loop on an AIConnection who's control object is a Bot.
//Moving
%moveVec = vectorSub(%movePos, %pos);
%moveY = vectorComponent(%moveVec, %forVec);
%moveX = vectorComponent(%moveVec, vectorCross(%forVec, "0 0 1"));
if(vectorLen(%moveX SPC %moveY) > 1) {
%move = vectorNormalize(%moveX SPC %moveY);
%moveX = getWord(%move, 0);
%moveY = getWord(%move, 1);
}
%client.setMove("y", %moveY);
%client.setMove("x", %moveX);
//Looking
%lookVec = vectorNormalize(vectorSub(%lookPos, %eyePoint));
%yaw = mATan(getWord(%lookVec, 0), getWord(%lookVec, 1));
%pitch = mASin(getWord(%lookVec, 2));// - 0.15; // - 0.15 makes the bot look down slightly, to mimic players, however it makes it inaccurate.
%facingDir = mATan(getWord(%forVec, 0), getWord(%forVec, 1));
%yaw -= %facingDir;
%pitch -= mASin(getWord(%eyeVec, 2));
if(%yaw > $pi)
%yaw -= 2*$pi;
if(%yaw < -$pi)
%yaw += 2*$pi;
%yaw = mClampF(%yaw/5, -$pi/30, $pi/30); //These just limit the turn rate of the bot.
%pitch = mClampF(%pitch/5, -$pi/30, $pi/30);
%client.setMove("yaw", %yaw);
%client.setMove("pitch", -%pitch);
function vectorComponent(%vec1, %vec2) { //Returns the component/projection of vec1 in the direction of vec2
return vectorDot(%vec1, vectorNormalize(%vec2));
}
%movePos is the position to move to, %lookPos is the point to look at. Everything else should be self-explanatory.
Edit: Added in code for looking at a position.