After following the CryDev documentation and creating my first entity I was lost… there were still many things that I needed to know before getting started with my grand plans. How to rotate entities for instance, or how to make different Lua scripts communicate together. Eventually I learned this stuff through trial and error and now I want to share it with you.

I am still a pretty novice Lua scripter and do not have much experience with the CryEditor. This is my very first tutorial and therefore it would be great to hear your thoughts on it! I’ll take any feedback to heart and adapt it in my future tutorials.

What are we going to create?We are going to create an entity that, when input with the name of another entity, rotates toward it smoothly in real-time. This can be useful for, for instance, moving a camera around the player or as part of a path following system.

If well received this tutorial will be the first part of a series that will eventually teach you how to create a Train that can follow tracks.

What will we learn during this tutorial?

- Basic Lua scripting for CryEngine- How to rotate entities- Parameters and arguments- Communicating between different Lua scripts- Some advanced Lua maths: atan2, modulo[%] and Lerp()

What do we need to know beforehand?Basic scripting knowledge will help a lot. I will, however, be as detailed as possible in this tutorial. We will start off with the following code:

First we are going to add a couple a variables: We will be needing a place to store the name of the entity we want to rotate towards, we want the unique ID of the named entity which we can use and we want a place where we can store the current angles (rotation) of MyEntity. Later on we will also need the position (pos) and speed at which it rotates (fRotSpeed).

entName = "", -- place this within the Properties tablefRotSpeed = 3, -- place this within the Properties tableentID = "", -- place this within the MyEntity tableangles = 0, -- place this within the MyEntity tablepos = 0, -- place this within the MyEntity table

As of now, these variables are empty, so let’s fill them the moment the game starts:

Just like with the model, we make sure an entity has been set before continuing. If this is the case the code looks for an entity in your editor project with the name of entName and stores its ID in entID. From now on we are able to access the functions and variables of that entity from this script. The next two lines are simple: They get the current angles + position from MyEntity and store them in angles and pos.

As we want to rotate MyEntity in real-time we will need to use a function that is continually called: OnUpdate(). OnUpdate()is called every frame. This function has a drawback though: Someone playing your project on 30 FPS will have half the number of calls than someone playing on 60 FPS resulting in everything moving half the pace. We can solve this by calculating the time between frames and using that to modify the rotation values. This time is frameTime and is automatically passed on to OnUpdate().

function MyEntity:FaceAt(ID, fT) Log("ID: " .. tostring(ID)); --ID needs to be converted to a string for the Log() functionend

In this example the contents of self.EntID and frameTime are passed from OnUpdate()to FaceAt() as ID and fT so that they can be used there.

The names of the parameters (ID & fT) can be anything. It is the position within the parenthesis that is of import. The first argument passed by the line: self:FaceAt(first , second); will be stored in the first parameter of the called function MyEntity:FaceAt(first , second).

Now to make this code work we must activate OnUpdate() as it is disabled by default:

Try it out for yourself! You should now be getting an output like ID: table: 000xxx0 continually being printed to the console. If this is not the case, scroll down a bit to find what your script should look like.

Assignment: If you got this working I would say it is time to do some thinking for yourself. It is a bit messy that OnUpdate() is always on, even when it doesn’t need to. CryTek disabled it by default for a reason right?

Make it so that in the editor we can enable and disable OnUpdate() by switching between self:Activate(1) and (0).

The second and final part of this tutorial will explain how we will turn MyEntity towards the specified entity.

The very first thing we will need to do is get the position of our entity and the one we want to rotate to. To do that we need to make a couple of variables that we will only need to use within FaceAt().

local a = self.pos; --self.pos is a vec 3 table, meaning it contains an x, y, and z variable local b = ID:GetPos(); --GetPos also returns a vec3

You can access all functions and non-local variables of another entity. You just need to replace self with an entity ID, in our case that is ID.

Now we have both locations we can use maths to calculate the rotation.

1. 2.

1. We want to calculate (a). (a) is the rotation of (A) when it is facing (B).2. From our younger years in high-school we recall that we need to use the arc tangent: (a) = atan(y/x)

We end up with: (a) = ~59°

However, x and y can at some point become negative and we know that -3/5 == 3/-5. This can become a problem as our result will be the same, even though the position of B is vastly different in these situations. This is why Lua has a function that keeps the x and the y separate and can thus calculate the coordinates correctly. atan2(y,x):

local newAngle = math.atan2 (b.y-a.y, b.x-a.x); -- calculates angle at which b is from a

Hopefully you found this part not too hard to follow, because now we are going to do something a bit more complicated. Instead of snapping the rotation to (B), we want to make it rotate towards (B) nice and smooth.

To do that we are going to linearly interpolate between the currentAngle of (A) to the newAngle (a) that we just calculated.

Our fRotspeed, multiplied by our frameTime is the value by which it interpolates. The higher fRotSpeed is, the faster MyEntity will turn.

But! What happens when our entity is at 10° and we want it to rotate to 350° ? The shortest way would be turning clockwise for 20°, pass through 360° and end up at 350°. Lerp, however, just interpolates one number towards the other linearly and cannot pass 360°. This means it would take the long way round. We could create some interesting if statements that compare rotations and make a different calculation depending on the results, but we can also create one funky formula that can solve this issue a bit more efficiently:

difference is the shortest angle between currentAngle (self.angles.z) and newAngle. So our example above would result in: -20°.

Modulo[%] works like this: 9 % 4 = 1. It checks how many times 4 fits in 9, which is 2, then it subtracts this sum (2*4= 8) resulting in 1. So to (overly) simplify: In this example it takes out all the 4’s and calculates what is left.

Keep the above diagram and example in mind as we will be using those values.

- Line 1 calculates the difference between the two angles and modulates it. This means we will end up with a value between the -359 and +359. (Remember: 360 will become 0, and 370 will become 10)

In our example difference = 340.

- Line 2 adds +360 and +180 (540). Then modulates by 360.

The +360 makes sure we cannot get any negative numbers. It moves our range of values from (-359 and +359) to (+1 and +719). (As we modulate this by 360 at the end: 719 == 359.) You can see this as transforming our range to (+0 to +359).

The +180 makes sure that when difference is higher than 180 (this is when we want our entity to turn the other way round, as it’s shorter) will end up being higher than 359 and thus, being modulated again.

After this modulation all differences with a value higher than 180 will range from (+1 to +180).When adding 180 to differences below 180, the results will range from (+180 to +360) and will not be modulated.

In our example difference becomes = 160

- Line 3 subtracts 180 from these results making every difference that is less than 180 a negative value, and every difference that is more than 180 will become less than 180.

Difference becomes = -20

That’s the answer we were looking for! Don’t worry if you don’t get it straight away! Try this formula with a couple of different values and eventually the penny will drop.The final thing we need to do is add our difference to our original angle:

Now when you go into the editor, reload your script and add the name of an entity in your scene to the properties of MyEntity and you will see MyEntity rotate towards the assigned entity. (Don’t forget to check Active too!)

Now move the target entity around and see how great it works! Congratulations! The hardest part is done.

WetCode wrote:This is great stuff keep it coming mate.The only thing that was "unclear" was the frameTime veriable.I assumed it was a global veriable, or was it. ? But again great Tutorial.Cheers

frameTime is a variable that contains the time it took the engine to render a frame. This time is always the first parameter of the OnUpdate() function. It's up to us to use this or not but it will always be sent, so yes, it is global.

CA3r8deP wrote:Hi, I see you only do Z axis rotation. If you manage to find a way to get it xyz it would so much be appreciated if you show me or us.thanks for this tutorial no matter.

The following code will make it rotate on the Y-axis as well. This should be sufficient in making the entity face the target entity, even if it's on a different height(Z).

Hi jethroj, As Tataru mentioned, I've also developed a drivable train system programmed with Lua, and I've been working with Tataru. The train system has many features, and it includes an editor that can be used to create and edit train tracks. One test track was more than 100 km long. If you'd like to discuss this and compare notes etc, why not pop around to my Flyable Aircraft thread?viewtopic.php?f=280&t=25876Despite its name, the Lua code is for ships, submarines, spacecraft - and trains. I'll be making a public release for the free SDK by the end of June.Regards,Chris