If your game shows more than a static screen, it must include code that moves the entities. You can use three methods if you want to move an entity (model, sprite or map entity):
- Change its position directly;
- Use the c_move instruction to move the entity with collision detection;
- Use the physics engine to move the entity by forces and the law of gravity.
Physics will be dealt with in another workshop. Let's concentrate on the first two methods.
1. Changing the position of an entity
I'd like to start talking about this method by mentioning that it is really simple, but it should be used only in some rare cases. We learned about the x, y, z position of an entity in the 8th workshop and now you should know how to change it in WED. Start WED and open the level inside the \workshop18 folder.
That's a simple level! It's nothing more than a narrow hallway, with few small lights and a model entity that looks like a sphere. Build the level and run it using the script18 file:
I can see a part of the sphere model (at the bottom of the screen) and that narrow hallway. Let's press the "S" (Start) key right away!
The ball has started to move! It changes its ambient depending on the level of light in the level, which looks nice...
What is going on there? The ball has penetrated the wall! Well, life isn't always fair... let's see the content of the script18 file:
Take a look at function main:
function main()
{
fps_max = 30;
level_load (work18_wmb);
wait (3);
camera.x = -100; // set a static position for the camera
camera.y = 0;
camera.z = 70;
}
The first line of code limits the frame rate to 30 frames per second (30 fps). If you have a decent pc, it can display much more than 30 fps; that line of code ensures us that this script will run at the same speed on a PC that has a CPU of 500 MHz or 10 GHz.
The next few lines of code instruct the engine to load the level and then to wait for 3 frames; don't forget to add "wait (3);" after a level_load instruction, in order to give the engine enough time to load the level before moving on. Finally, we set the camera position (x, y, z) to a convenient position; we could have set the pan, tilt, roll angles too, but the initial camera.pan points along the x axis, and that's exactly what I was needing.
Time to check the code for the action attached to the sphere model:
action my_ball
{
while (key_s == off)
{
wait (1); // wait until the player presses the "S" key
}
You've learned how to work with the keyboard! The "while" loop above will wait until the player presses the "S" key on the keyboard. In this case my "while" loop definition from workshop10:
while (some condition is true)
{
// repeat the instructions that are placed inside the curly brackets
}
would be translated like this:
while (key_s isn't pressed)
{
wait (1); // repeat this instruction
}
As a conclusion, the first while loop will repeat the "wait (1);" instruction until the player presses the "S" key on the keyboard. Let's take a look at the second loop inside action my_ball:
while (1)
{
my.x += 5; // move the ball entity along the
x axis
wait (1);
}
}
This while loop changes the position of the ball entity (the sphere model) by adding 5 quants to its x coordinate every frame. You can try other values instead of "5" in order to increase or decrease the movement speed.
The first entity movement method is really simple: you change one or more entity coordinates (x, y, z) in a while loop and that's it!
Let's pretend that our ball is a mighty chopper that takes off at the end of the level; add a line of code to the script18 file:
Save the script and run it; you will see the same image.
Press "S" to start moving the ball. It continues to move along the x axis, but this time it increases its height (z) too!
The only bad news is that the ball will penetrate the ceiling:
Here's the main (and only) disadvantage when you change the coordinates of the entities directly: the entities will not care about the level geometry at all. The entities can penetrate each other, can pass through walls, floors, ceilings, doors, etc. Nothing can stop them! That's a useful method if you plan to give one of your monsters the ability to pass through walls, but it's a useless "feature" if you are creating a normal game (but what's "normal" these days?).
Important tip: Moving an entity by changing its x, y, z coordinates is really simple, but it shouldn't be used by the beginners. You have to know exactly what you are doing!
2. Using the c_move instruction
This c_move instruction moves an entity and performs collision detection during the movement. This means that our sphere will stop when it collides with the walls, with the ceiling, with other entities and so on, which is a good thing. Start WED, load the work18 file (if you haven't done that already) and then run the level using the script18_2 file:
Nothing new so far, so let's press the "S" key now!
The ball is moving on the x and z axis, just like before. I think that it is going to hit the ceiling sooner this time...
Guess what? The entity has hit the ceiling, but it hasn't penetrated it; more than that, it has continued to glide along the ceiling until it has stopped at the end of the hallway! Is this true entity movement or what?
If you think that the code that does all these exciting things is complicated, you are wrong. Here's the modified script18_2 file that moves the ball using c_move:
Don't forget to choose the script18_2 file in SED's Options -> Configuration -> CSC / WDL file to run; otherwise, you will continue to run the old script18 file.
A single line of code has changed - the line that uses the new c_move instruction:
c_move (me, nullvector, vector(5, 0, 2), GLIDE);
The general form of c_move is listed below:
c_move (entity, relative_speed, absolute_speed, movement_type)
I know that the line above looks pretty complicated but I promise to explain every term:
- c_move is the name of the instruction that can move any entity and performs collision detection during the movement. Every time you need to move an entity that can collide with the level geometry or with other entities you have to use the c_move instruction. Need movement code for the player, a monster, any vehicle, a ball, and so on? Then you'd better use c_move!
- entity is the name of the entity that must be moved. I have used the predefined me pointer, because I want to move the ball - that's the entity that has the action named move_ball attached to it. You can use any other entity pointer that was defined before using the c_move line of code.
- relative_speed is the speed that will move the entity in the direction (angles) pointed by the entity; I have used nullvector for relative_speed in my example from the script18_2 file. What's with this nullvector you ask? It's nothing more than a fancy name for a predefined vector (a set of three variables) that has all its components set to zero. Every time we use nullvector, the engine will understand that we are feeding it with a series of zeros. You can use nullvector or vector(0, 0, 0) here because the engine will understand the same thing: it must ignore the relative speed of the entity.
- absolute_speed is the speed that will move the entity in an absolute direction, without using the current direction (angles) pointed by the entity. I have used vector(5, 0, 2) here, which means that the ball will move with a speed of 5 quants / frame along the x axis, 0 quants / frame along the y axis and 2 quants / frame along the z axis. As you can see, vector (5, 0, 2) creates a temporary vector without a name, but this vector acts like 3 vars that have different x, y and z values.
- movement_type sets the type of the movement that will be performed for as long as the c_move line of code is executed. I have used GLIDE in my example, but there are many other possibilities listed in the reference manual. Let me describe a few of them for you right now:
a) GLIDE - the entity will glide along the walls and other entities if it collides with them;
b) IGNORE_PASSABLE - ignores (passes through) all the passable blocks and entities if they have their PASSABLE flag set;
c) IGNORE_MODELS - ignores (passes through) all the models, even if they aren't PASSABLE.
You can combine these options by adding them; GLIDE + IGNORE_PASSABLE will instruct the entity to glide along the walls and entities, passing through the blocks and entities that have their PASSABLE flag set.
I have fed your brain with a lot of information, so it is quite normal for you to feel a little confused. The scary part is the one that talks about relative_speed and absolute_speed, right? Let's pretend that we want to create a racing game:
Now take a good look at the blue car. If I would write an action that looks like this:
action blue_car_1
{
while (1)
{
c_move (me, nullvector, vector(3, 0, 0), GLIDE);
wait (1);
}
}
the blue car would move just like in the picture below:
Now why did that happen? Well, we are only using absolute_speed, and vector(3, 0, 0) tells the car to move along the x axis, because only the x component of the vector (3) was set to a non-zero value. Guess what? The x axis points towards the right side of the screen, so that's where this blue car is heading.
If we would use an action that looks like this:
action blue_car_2
{
while (1)
{
c_move (my, vector(3, 0, 0), nullvector, glide);
wait (1);
}
}
the blue car would move just like in the picture below:
This time the car moves using its relative_speed, so it will move in the direction pointed by its pan angle. You will want to use relative_speed most of the time in your projects. Need to create the code for a monster that chases the player? Rotate the monster towards the player (a few simple lines of code will do that) and c_move it using its relative_speed!
But why did Conitec invent both relative_speed and absolute_speed? Can't we use "some_speed" to rule them all?
Well, we could do that, but our code would look way too complicated sometimes. Let's imagine that you are creating a shooter game, and one of its episodes takes place in Siberia, which is a cold, snowy place. The level would be pretty easy to beat, but there's this cold wind blowing from north that pushes the player away. How would you code something like that? Something that pushes you away from your goal when you are standing still, or resists you when you are moving towards your goal (the north pole)? How would you say: "there's this force coming towards the player from the northern area of the map, and I'm the one that is going to write the code for it"? You've got it, absolute_speed will help. Here's the line of code that moves an entity towards the direction pointed by its pan angle and applies a negative force (just like a wind) coming from north (as seen in WED's top view):
c_move (my, vector(3, 0, 0), vector(0, -1, 0), glide);
This entity (be it the player, a vehicle, etc) will move with 3 quants / frame in the direction given by its own angles, while suffering the effects of a negative speed (-1 quant / frame) on the y axis. The result is a combination of the two speeds (relative_speed and absolute_speed). You will want to use this combination whenever you want to add external forces (like gravity, drift and so on). How can you keep the player on the ground? Use a vector that has a negative z component for absolute_speed:
c_move (my, vector(5, 0, 0), vector(0, 0, -2), glide);
I think that I have given you enough information about c_move, so it's time for your homework. I have created a brand new project for you, so you'd better spend some time with it! Start WED and open the homework18 level.
It's just a simple box with a car model inside it, so run the level using the home18 script file:
That tiny car can be moved using both relative_speed and absolute_speed. Press the "A" and "S" keys to change the pan angle of the car and then press the "Space" key to see it moving in the direction of its pan angle (relative_speed). Press the "Up", "Down", "Left" or "Right" arrow keys to move the car using absolute_speed (absolute world coordinates).
Now here's how the home18 script looks like:
I know that this script looks pretty big, but it contains simple instruction and many comments. I have set good initial position and angles for the camera in main and the rest of the lines move the car using absolute_speed (lines 29 to 44) and relative_speed (lines 46 to 57). If you don't like my camera position you can press the "Zero" key on your keyboard to free the camera and move it where you want it.
Your homework is to run this level over and over until you understand how c_move works. I encourage you to change all the values used for the vectors on their x, y and z components. What happens if you replace the last c_move instruction with the one below?
c_move (my, vector(4, 0, 4), nullvector, glide);
Maybe the car will start to fly, because the z component of its relative_speed is positive, but why don't you try that yourself? Play with this script until you are sure that you've mastered the c_move instruction; you will need to use this instruction in every game!
Now I'm going to take a break but I expect to find you here when I get back...