Previous: Actions

Pointers

A pointer stores a reference to an object. Let's imagine the following situation: you want to program a pong clone in lite-C:

You want the left paddle to be controlled by the player and the right paddle to be controlled by the computer. The problem is... how can you tell in your code who is the player and who is the computer? The answer is simple: you create a pointer, which is just a reference to another object. That's exactly how the pong multiplayer sample in your samples folder moves its paddles.

A typical pointer definition looks like this:

ENTITY* the_player;

or

STRING* my_name;

or

PANEL* game_over;

or

BMAP* health_picture;

or even

var* my_number;

Ok, so a pointer definition looks similar to an object definition, but without the content of the object. We simply add an asterisk "*" to the object type and our pointer is ready for use. We can make the pointer "point" to any object, string, panel, etc and then we can deal with the pointer as if we would be dealing with the object, string, panel, etc itself!

I know you're feeling a little dizzy right now so let's fire up an example: start Lite-C, open the script file named script11 inside the workshop11 folder, and run it right away. You can see a house and two tiny characters in front of it.

Oh, I see! The twin wizard brothers are dressed in red and blue! They don't do anything special but we're going to change that. Press Tab on your keyboard and type the following line:

wizard.x = 300;

The red wizard has come much closer to the camera!

But why did the red wizard move? Why didn't the blue wizard move? Why didn't both wizards move at the same time? And why didn't we see the house moving at all?

I'm afraid I can't cope with that many questions at once, but I'll give you a phrase that answers them all: I have used a pointer for the red wizard. You see, we are living in a cruel world. These nasty computers don't care about us, humans. You and I know how that wizard model looks like, but the computer isn't interested in learning about that. If you tell a computer to move the red wizard model it will laugh at you; it doesn't know and it doesn't care who and where is the red wizard model. He doesn't know and he doesn't care to learn about the wizard's skin color. Therefore, we need to use a pointer for every entity that needs special treatment from your computer.

Let's look inside the script file named script11; its important part looks like this:

////////////////////////////////////////////////////////////////////
ENTITY* wizard;
////////////////////////////////////////////////////////////////////
function main()
{
  level_load ("work11.wmb");
}
   
action wizard_with_pointer()
{
  wizard = me;
  my.ambient = 100;
}
   
action wizard_simple()
{
  my.ambient = 100;
}   

Thank God! The script doesn't look complicated at all!

We set the screen resolution, we load a level named work11.wmb, and then we have two actions, which were assigned to the two wizards using GameStudio's level editor WED. Above all of them, we have defined a pointer named "wizard". Let's take a closer look at this line of code:

ENTITY* wizard;

We have defined a pointer that will be used by an entity (an entity pointer) and we have named it wizard. We can use any other name here; it doesn't matter if we use a pointer named robot_12 for a monkey. Now let's examine the action named wizard_with_pointer:

action wizard_with_pointer()
{
    wizard = my;
    my.ambient = 100;
}

You know that we have defined a pointer named wizard, right? The first line of code inside the action assigns the pointer to the entity that has the action attached to it. In other words, "wizard = my;" means I'm the wizard! I, the entity that has this action attached to it, will be known as "wizard" from now on! And guess what? The action named wizard_with_pointer is attached to the red wizard model, so the red wizard model becomes "wizard" and can be controlled using the dot method, just like we did at the console with our "wizard.x = 300;" line of code.

The second line of the action sets the ambient for the red wizard to 100, which makes it look brighter. Curious to see the difference? Take a look at the picture below:

The second action doesn't use a pointer so all it does is to increase the ambient for the model attached to it.

action wizard_simple()
{
    my.ambient = 100;
}

Well, you've guessed it again... this action is attached to the blue model. Oh, and I forgot to tell you that "my" is a pointer too! It's a predefined pointer so we don't have to write its definition first. Don't worry; we will talk about it close to the end of the workshop.

Ok, so how do we create pointers? There are two steps:

1) We define the pointer using a single, short line of code:

ENTITY* horse; // define an entity pointer

2) We tell the pointer who will be its owner by placing a line that contains its name in the action that interests us:

action players_horse()
{
    horse = my;
    ...
}

From now on you will be able to control the entity that has action players_horse attach to it using the dot method: object.property

Let's play a little more with our test level, shall we? Run the level again and type this line of code at the console:

wizard.z = 100;

The wizard has changed its height indeed! You can play with its x, y, z, pan, tilt, roll...

The good news is that you can change the properties of the red wizard from within any other action or function in your script. Copy and paste the following function at the top of your main function:

function move_up()
{
    wizard.z += 5; // add 5 quants to the height of the wizard each time when this function is called
}

Then, add the following line of code inside function main:

on_u = move_up;

The modified script file should look like this:

//////////////////////////////////////////////////
ENTITY* wizard;
//////////////////////////////////////////////////
function move_up()
{
  wizard.z += 5;
}

function main()
{
  level_load ("work11.wmb");
  on_u = move_up;
}

action wizard_with_pointer()
{
  wizard = me;
  my.ambient = 100;
}

action wizard_simple()
{
  my.ambient = 100;
}

You can see that the function named move_up increases the height (the z) of the wizard with 5 units; if its initial height was 15 quants, the function will set it to 20 quants and so on. Now this line of code looks weird:

on_u = move_up;

Well, on_u means "when the player presses the U key on the keyboard" and move_up is the name of the function, written without parenthesis, that will be executed every time we press the "U" key (it doesn't matter if Caps Lock is on or off). As a conclusion, every time we press the "U" key, function move_up will add 5 more quants to the height of the red wizard.

Some beginners use parenthesis for assigning a function to a key. Don't do it; writing "on_u = move_up();" will run the function right away instead of assigning it to the key, and produce an error message when the "U" key is pressed.

Back to our script: save it, run it and then press the "U" key several times to move the wizard upwards. It is clear that the function named move_up knows who "wizard" is.

A final note: the most important pointer used by the engine is "my". Let's get back to the action attached to the blue wizard model:

action wizard_simple()
{
    my.ambient = 100;
}

The line of code inside the action sets the ambient to 100 for any entity that has this action attached to it; "my.ambient = 100;" means "I, the entity that has this action attached to it, will set my ambient to 100". Attach this action to a sprite and the ambient for that sprite will be set to 100; attach it to a wmb entity and it will set its ambient to 100. Pretend that "my" is the universal pointer; every entity can use it without having to define it in advance, but it isn't known outside the action or function that contains it. If you try to type

my.z = 100;

at the console it won't do anything, because "my" isn't known outside its action or function.


Next: If - Else branching


Further reading: Gamestudio manual ► pointers