Hi,
I am trying to code a model to "turn to face in the direction
of another model" but only on the "y" axis of it's rotation,
so it is facing in the direction of the player object in a game.
I have some idea about how this needs to be done,
but I still struggle with intuitively understanding how to
use trigonometric functions.
I tried this:
CurrentModel.Rotation.y =
atan2
(
(CurrentModel.Position.z+player.position.z),
(CurrentModel.Position.x-player.position.x)
)*PI/2;
which almost works for what I have in mind, however,
the offset is wrong, and I'm not sure why.
The other thing it needs, is some incline,
so it gradually turns to face, rather than,
immediately facing the other object.
Any thoughts?
Making a model rotate to face another.
Moderator: Moderators
Re: Making a model rotate to face another.
Hi jinxtengu,
So, in order to have a model face a target ( the "face" of the model being the positive Z-axis ) you can simply do this:
K
The first thing you need to realize is that atan2 returns a value between -PI and PI. Since ZGE uses units / cycles for rotations ( a value of 1 represents 360 degrees ), in order to convert the result of atan2 you need divide by PI*2 ( not multiply by half-PI as you're doing ). Furthermore, to get a result of 0 from atan2 the input needs to be atan2(0,1). Keep in mind that the function uses argument order atan2(y,x) instead of atan2(x,y).
So, in order to have a model face a target ( the "face" of the model being the positive Z-axis ) you can simply do this:
Code: Select all
<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" CameraPosition="0 16 16" CameraRotation="0.125 0 0" LightPosition="0 1 0" FileVersion="2">
<OnLoaded>
<SpawnModel Model="Box" SpawnStyle="1"/>
<SpawnModel Model="Eye" SpawnStyle="1"/>
</OnLoaded>
<Content>
<Model Name="Box">
<OnUpdate>
<ZExpression>
<Expression>
<![CDATA[// Animate box position
float a = sin(App.Time/4)*16;
Box.Position.X = sin(a)*8;
Box.Position.Z = cos(a)*8;]]>
</Expression>
</ZExpression>
</OnUpdate>
<OnRender>
<RenderMesh Mesh="BoxMesh"/>
</OnRender>
</Model>
<Mesh Name="BoxMesh">
<Producers>
<MeshBox/>
</Producers>
</Mesh>
<Model Name="Eye">
<OnUpdate>
<ZExpression>
<Expression>
<![CDATA[// Control x-position using mouse
Eye.Position.X = App.MousePosition.X*16;
// Calculate angle between box and eye.
Eye.Rotation.Y = atan2(Box.Position.X-Eye.Position.X,
Box.Position.Z-Eye.Position.Z)/PI*0.5;]]>
</Expression>
</ZExpression>
</OnUpdate>
<OnRender>
<RenderMesh Mesh="EyeMesh"/>
</OnRender>
</Model>
<Mesh Name="EyeMesh">
<Producers>
<MeshBox/>
<MeshExpression>
<Expression>
<![CDATA[//
if(V.Z > 0)
{
V.X = 0;
V.Y = 0;
}]]>
</Expression>
</MeshExpression>
</Producers>
</Mesh>
</Content>
</ZApplication>
This is pretty straight-forward, the only thing different from having a regular value "follow" another is that the target value "wraps around". So for example, you need to make sure that when current rotation is at 0.1 and target rotation is at 0.9 you go towards -0.1 instead of 0.9. What formula you want to use to follow the value completely depends on your preference & requirements .. but here's a super simple example.
Code: Select all
<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" CameraPosition="0 16 16" CameraRotation="0.125 0 0" LightPosition="0 1 0" FileVersion="2">
<OnLoaded>
<SpawnModel Model="Box" SpawnStyle="1"/>
<SpawnModel Model="Eye" SpawnStyle="1"/>
</OnLoaded>
<Content>
<Model Name="Box">
<OnUpdate>
<ZExpression>
<Expression>
<![CDATA[// Animate box position
float a = sin(App.Time/4)*16;
Box.Position.X = sin(a)*8;
Box.Position.Z = cos(a)*8;]]>
</Expression>
</ZExpression>
</OnUpdate>
<OnRender>
<RenderMesh Mesh="BoxMesh"/>
</OnRender>
</Model>
<Mesh Name="BoxMesh">
<Producers>
<MeshBox/>
</Producers>
</Mesh>
<Model Name="Eye">
<OnUpdate>
<ZExpression>
<Expression>
<![CDATA[// Control x-position using mouse
Eye.Position.X = App.MousePosition.X*16;
// Calculate target rotation
float a2 = atan2(Box.Position.X-Eye.Position.X,
Box.Position.Z-Eye.Position.Z)/PI*0.5;
// Get current rotation in cycle range
float a1 = frac(Eye.Rotation.Y);
float a = a2-a1;
if(a > 0.5)a--; else if(a < -0.5)a++;
// Follow target rotation over time
Eye.Rotation.Y = frac(Eye.Rotation.Y+a*4*App.DeltaTime);]]>
</Expression>
</ZExpression>
</OnUpdate>
<OnRender>
<RenderMesh Mesh="EyeMesh"/>
</OnRender>
</Model>
<Mesh Name="EyeMesh">
<Producers>
<MeshBox/>
<MeshExpression>
<Expression>
<![CDATA[//
if(V.Z > 0)
{
V.X = 0;
V.Y = 0;
}]]>
</Expression>
</MeshExpression>
</Producers>
</Mesh>
</Content>
</ZApplication>
Re: Making a model rotate to face another.
Thanks alot Kjell! This has helped me alot!
I've been reading over the code you posted so I can understand it.
Why is float integer of A2 divided by PI times 0.5?
Why not just divide it by PI, to get 360 degrees. Why is the 0.5 necessary on the end?
also here:
float a = a2-a1;
if(a > 0.5)a--; else if(a < -0.5)a++;
I don't really grasp what this does, even though you've labeled it as "Get current rotation in cycle range"
"a > 0.5" "a < -0.5"
Is this to ensure that there are limitations, in regard to the units as cycles for rotations?
Apologies for asking so many simple questions.
I've been reading over the code you posted so I can understand it.
Why is float integer of A2 divided by PI times 0.5?
Why not just divide it by PI, to get 360 degrees. Why is the 0.5 necessary on the end?
also here:
float a = a2-a1;
if(a > 0.5)a--; else if(a < -0.5)a++;
I don't really grasp what this does, even though you've labeled it as "Get current rotation in cycle range"
"a > 0.5" "a < -0.5"
Is this to ensure that there are limitations, in regard to the units as cycles for rotations?
Apologies for asking so many simple questions.
Re: Making a model rotate to face another.
Hi jinxtengu,
Think of it like a clock. If the current hour is 10 o'clock and the target hour is 2 o'clock you don't want to rotate down ( 10->9->8->7->6 etc. ) you want to rotate up ( 10->11->12->1->2 ). So in order to do that easily you'd adjust the target from 2 o'clock to 14 o'clock.
K
Because the atan2 result has a range of -PI to PI, once you divide it by PI the range becomes -1 to 1 .. which is a range of 2 ( 1 minus -1 is 2 ). So you've got to divide it by 2 ( or multiply by 0.5 ) to get a range of 1 ( from -0.5 to 0.5 ).
Yes, it's basically checking whether the target rotation is further away than half a cycle, in which case rotating the other direction is actually closer .. so you adjust the target by one cycle to facilitate that.
Think of it like a clock. If the current hour is 10 o'clock and the target hour is 2 o'clock you don't want to rotate down ( 10->9->8->7->6 etc. ) you want to rotate up ( 10->11->12->1->2 ). So in order to do that easily you'd adjust the target from 2 o'clock to 14 o'clock.
K
Re: Making a model rotate to face another.
Thanks for your explanations. This actually makes sense to me now.