AGS Pointers for Dummies

From Adventure Game Studio
Revision as of 03:25, 22 March 2006 by 70.128.94.86 (talk)
Jump to navigation Jump to search

AGS Pointers for Dummies (A reference for the rest of us!)

So you're new to Adventure Game Studio (at least new to version 2.7 or later) and you're a bit confused about all this "pointer" business? Don't worry, you're not alone. A lot of people are confused about pointers. That's why I'm here to help.

What are pointers?

So what exactly are pointers, and how do I use them?

The basic idea of a pointer is that instead of creating a new variable in the memory, you are just going to point to one that is already stored there. This can have several uses, and in AGS even has some special ones.

AGS has certain managed types that you can not create an instance (variable declaration) of. All of the variables of managed types are managed by AGS. These include the types: Room, Game, Parser, File, InventoryItem, Overlay, DynamicSprite, GUI, Label, Button, Slider, TextBox, InvWindow, ListBox, Character, GUIControl, Hotspot, Region, Maths, DateTime, and Object.

Note that not all of the managed types are meant to have pointers to them. Room, Game, Parser, and Maths do not need pointers (you can't even assign them a value).

However, you can work with these managed types through pointers. You define a pointer by typing the variable type, then a space, an asterik (*), and finally the name of the pointer. So, to create a pointer named GUIPointer to point to the GUI object named gMygui, you could type:

 GUI *GUIPointer = gMygui;

Note that if the pointer is global (outside of all functions), then you cannot assign it an initial value.

Also note that if you are defining multiple pointers at once, you have to place an asterik before the name of each pointer.

Furthermore, you should take note that the asterik can actually be placed beside the variable type (i.e., GUI* GUIPointer instead of GUI *GUIPointer), but it will always be parsed as being attached to the variable (i.e., GUI *GUIPointer).

Of course this would be a rather pointless assignment, unless you just wanted a new alias for your GUI. A more useful assignment makes use of the function GUI.GetAtScreenXY:

 GUI *GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y);

As implied by the name of the pointer, this pointer will point to the GUI that the mouse is over. This brings to point an interesting question though. What if there is no GUI under the mouse? Well, in that case, GUIUnderMouse would be set to null, which means that it isn't pointing to anything.

If a pointer is null, then basically all you can do with the pointer is make it point to something (assign it a value as in the examples), and test it against other pointers (or variables) of the same type. We've already seen how to assign a value to a pointer, so let's see how we can compare two pointers. Let's take the following example:

 GUI *GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y);
 if (GUIUnderMouse != null) {
   if (GUIUnderMouse == gMygui) {
     Display("MYGUI is under the mouse!");
     }
   }

First we assign the pointer to hold the value of the GUI under the mouse as we did before. Then we test whether it is null with the statement "if (GUIUnderMouse != null)" which reads as "GUIUnderMouse is not equal to null." If GUIUnderMouse was equal to null, then it wouldn't be pointing to anything, so we don't want to work with it. Next we test if the GUI is MYGUI with the statement "if (GUIUnderMouse == gMygui)". If the GUI under the mouse was gMygui, then they will be equal, and the statement will pass as true and the statement will be displayed (avoid doing this repeatedly or else it could be a hassle to return back to the game).

Okay, so we can create, assign, and test pointers, but what do they DO?

Well, we've already discussed that they point to variables stored in the memory, but it's an interesting question as to how this can be useful. Let's take for example a built-in pointer, InventoryItem* Character.ActiveInventory. This is a pointer to an InventoryItem; the active inventory item for the Character who owns the property.

What it does is allows the user to operate on the active item without having to know it's integral value, or even what it is for that matter. For example, if you wanted to change the item's graphic, with an integral system (with no pointers) you would have to do something like this:

 inventory[character[GetPlayerCharacter()].activeinv].Graphic = 42;

However, in a system with pointers, you can type this instead:

 player.ActiveInventory.Graphic = 42;

So in addition to shortening the code, it also makes it easier to read. The player keyword is a pointer to the player character (Character*) and ActiveInventory is a pointer to the player's active inventory item (InventoryItem*).

Wait...what's with all this script o-name stuff?

If you're reading this, you probably didn't ask that, so I asked it for you, because it pertains to this...

Script o-names are essentially just pointers. So, if you create GUI #5 and name it INVENTORY, then AGS automatically assigns it the script o-name, gInventory. Basically gInventory will be defined internally as this:

 // pseudo-internal-AGS-code
 GUI *gInventory = gui[5];

It is a pointer to GUI #5, which is accessed globally by the gui array. Now for all I know the gui array could be just an array of pointers to something which is managed further inside the bowels of AGS, but it wouldn't really make a difference as they would still both be pointing to the same variable in the end.

Okay, did you just say something about an array of pointers?

Well, I'm not going to go into what an array is (look it up in the manual), but yes, you can create an array of pointers. You do this the same way you would create any other array. So, if you wanted to create your own array of pointers to the first five GUI objects, you could do something like this:

 GUI* MyGUIArray[5];
 
 // game_start
 int i = 0;
 while (i < 5) {
   MyGUIArray[i] = gui[i];
   i++;
   }

Note that if you have less than 5 GUIs in your game, this WILL crash it.

I forgot to mention, that you can even place a pointer inside of a struct. This can be another great use for pointers in AGS. You define it the same way you define any other variable in a struct:

 struct StructNameHere {
   vartype1 var1;
   vartype2 var2;
   TypeToPointTo *PointerToType;
   };

Then, you use it like a normal pointer (just as a member of a variable of the new type). For example, suppose you want to store some new information about a character, say RPG statistics. You could use a pointer like this:

 struct CharStats {
   int HP;
   int MP;
   int MaxHP;
   int MaxMP;
   Character* Char;
   };
 
 CharStats stEgo;
 
 // game_start
 stEgo.MaxHP = 100;
 stEgo.MaxMP = 50;
 stEgo.HP = 100;
 stEgo.MP = 50;
 stEgo.Char = cEgo;

If you know how to use structs, then you will already understand what the integer values are for (if you don't check the manual), but what about this Character* (pointer to Character)? By storing this information, you can easily use stEgo in place of cEgo. Where you would normally type something like cEgo.Animate, you would simply type stEgo.Char.Animate.

This is one of the most useful reasons for pointers as, to an extent, it can allow you to extend built-in types.

And just to bring it all together, yes, you can even combine arrays, pointers, and structs all in one:

 struct CharStats {
   int HP[50];
   int MP[50];
   int MaxHP[50];
   int MaxMP[50];
   Character* Char[50];
   };

Then, just by creating a variable of this type, you can create 50 new character-statistic variables at once!

Now then, I hope this helps everyone to understand pointers in AGS a little bit better.