Making a model rotate to face another.

All topics about ZGameEditor goes here.

Moderator: Moderators

Post Reply
jinxtengu
Posts: 122
Joined: Wed Oct 14, 2009 2:05 pm
Contact:

Making a model rotate to face another.

Post by jinxtengu »

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?
User avatar
Kjell
Posts: 1883
Joined: Sat Feb 23, 2008 11:15 pm

Re: Making a model rotate to face another.

Post by Kjell »

Hi jinxtengu,
jinxtengu wrote: Sun May 07, 2023 7:08 amI have some idea about how this needs to be done,
but I still struggle with intuitively understanding how to
use trigonometric functions.
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>
jinxtengu wrote: Sun May 07, 2023 7:08 amThe other thing it needs, is some incline, so it gradually turns to face, rather than,
immediately facing the other object.
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>
K
jinxtengu
Posts: 122
Joined: Wed Oct 14, 2009 2:05 pm
Contact:

Re: Making a model rotate to face another.

Post by jinxtengu »

Thanks alot Kjell! This has helped me alot! :D

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.
User avatar
Kjell
Posts: 1883
Joined: Sat Feb 23, 2008 11:15 pm

Re: Making a model rotate to face another.

Post by Kjell »

Hi jinxtengu,
jinxtengu wrote: Mon May 08, 2023 7:34 amWhy 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?
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 ).
jinxtengu wrote: Mon May 08, 2023 7:34 amI don't really grasp what this does, even though you've labeled it as "Get current rotation in cycle range. Is this to ensure that there are limitations, in regard to the units as cycles for rotations?
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.

Image

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
jinxtengu
Posts: 122
Joined: Wed Oct 14, 2009 2:05 pm
Contact:

Re: Making a model rotate to face another.

Post by jinxtengu »

Thanks for your explanations. This actually makes sense to me now. :)
Post Reply