AGS Pointers for Dummies: Difference between revisions

From Adventure Game Studio | Wiki
Jump to navigation Jump to search
*>Monkey 05 06
No edit summary
Line 1: Line 1:
=AGS Pointers for Dummies (A reference for the rest of us!)=
=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? Or maybe you've read the manual, but you just don't get it? Don't worry, you're not alone. A lot of people are confused about pointers. That's why I'm here to help.
If you're reading this, then you've come looking for answers. ''What are pointers? How do I use them? What makes them better than just using integers?'' These questions, amongst others shall be answered. You're not alone in the confusion caused by pointers. Many people who are new to scripting, or haven't ever used a language that supported pointers don't understand them right away. That's why I'm here to help.


==What Are Pointers?==
==What Are Pointers?==
''So what exactly are pointers?''
''So what exactly are pointers?''


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.
To understand what a pointer is, we must first understand the more basic idea of variables. If you already understand variables, you can skip over this section.
 
===Variables===
A variable, in the context of scripting, is a way to represent a value. In AGS, there are five different [http://www.bigbluecup.com/manual/Datatypes.htm data types] which can be used to represent a variable. These types include '''char''', '''short''', '''int''', '''String'''<sup>1</sup>, and '''float'''<sup>2</sup>. A '''char'''-type variable is one that can hold only a single character (i.e., 'A', 'B', etc.) or a number within the range 0 to 255. A '''short'''-type variable can store integers within the range -32768 to 32767. An '''int'''-type variable can store integer values within the range -2147483648 to 2147483647. A '''String'''-type variable can hold a string of characters (i.e., "this is some text!") of ''virtually'' infinite length. A '''float'''-type variable can store floating-point decimals within the range -2147483648.0 to 2147483647.0, and has precision up to approximately 6 decimal places, though this will vary based on the actual number.
 
For information on defining and assigning values to variables read the entry in the manual [http://www.bigbluecup.com/manual/Datatypes.htm here].
 
'''<sup>1</sup>The String type is only defined as of AGS v2.71 and higher. Older versions use the now deprecated string type.'''
 
'''<sup>2</sup>Floating-point decimals won't always evaluate as you might expect when doing certain mathematical operations (this is due to their precision levels). See the manual entry on [http://www.bigbluecup.com/manual/Datatypes.htm data types] for more information.'''
 
===Pointers===
Okay, so now that we understand what a variable is, we can begin to understand what a pointer does. The basic idea of a pointer is that instead of creating a ''new'' variable, we are simply going to ''point'' to a variable that is already stored in the memory. This can have several uses in scripting, and AGS even has some special ones.


==Defining A Pointer==
==Defining A Pointer==
''And, how do I use them?''
''And, how do I use them?''


In AGS, you can only create pointers to certain types of variables. These are called ''managed'' types.
In AGS, you can only create pointers to certain data types. These are called the ''managed'' types.


===Managed Types===
===Managed Types===
Line 19: Line 31:


===Working With Managed Types===
===Working With Managed Types===
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:
You can work with these managed types through pointers.  You define a pointer by typing the name of the managed data type, then a space, an asterik (*)<sup>3</sup>, and finally the name of the pointer. So, if we want to create a GUI pointer (this is expressed as GUI*) called GUIPointer, we could type the following:


   GUI *GUIPointer = gMygui;
   GUI *GUIPointer;
 
This creates a pointer that can ''point'' to any GUI stored in the memory. However, until it is assigned a value, it is an empty, or ''null'' pointer. We'll first discuss how to assign pointers a value, then we'll discuss null pointers.


'''''Note that if the pointer is global (outside of all functions), then you cannot assign it an initial value.'''''
===Assigning A Pointer A Value===


'''''Also note that if you are defining multiple pointers at once, you have to place an asterik before the name of each pointer.'''''
To make it point to a GUI, you assign it the value of the GUI you want it to point to (with the assignment operator, '='). So to make GUIPointer point at the GUI named MYGUI, you would type:


'''''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).'''''
  GUIPointer = gMygui;


====A More Useful Assignment====
As long as the pointer isn't global (i.e., the pointer is defined inside of a function), then you can also assign it an inital value when you create it, like this:
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);
   GUI *GUIPointer = gMygui;


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.
Global pointers can't have an initial value assigned though, so this will only work if you define the pointer inside of a function. When defining more than one pointer of the same type at once, it is necessary to use an asterik for every pointer. So, if you want MyGUIPointer to point to MYGUI, and OtherGUIPointer to point to OTHERGUI, you can do this:


==Null Pointers==
  GUI *MyGUIPOINTER = gMygui, *OtherGUIPointer = gOthergui;
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. You can test to see if a variable is null with the '==' operator.


Suppose, for example, we have a GUI pointer (GUI*) called GUIUnderMouse which we will use to point to the GUI under the mouse. We can check if there is a GUI under the mouse (after assignment) with the following line:
If you forget an asterik then it will try to create a new instance (create a new variable) of the type GUI. AGS doesn't allow the user to create new instances of managed types, so this would crash your game. So, it's always important to remember your asteriks.


  GUI* GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y) // assignment
'''<sup>3</sup>The asterik doesn't necessarily have to be attached to the name of the pointer, such as "GUI *MyGUIPointer", it can also be attached to the data type itself, such as "GUI* MyGUIPointer". However, it will still be compiled as if it is attached to the name of the pointer, not the data type, so if you define multiple pointers at once, you will still need an asterik for each pointer.'''
  if (GUIUnderMouse == null) {} // no GUI under mouse


You can also test if a pointer is ''not'' equal to null with the '!=' operator:
===A More Useful Assignment===
This type of assignment is rather pointless however, unless you just want a new alias for your GUIs. A more useful assignment makes use of the function GUI.GetAtScreenXY. This function returns a GUI* to the GUI at the specifed coordinates. So, if you wanted to see what GUI the mouse was over, you could do this:


   if (GUIUnderMouse != null) {} // there is a GUI under the mouse
   GUI *GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y);


==Comparing Pointers==
===Testing A Pointer's Value===
We've seen how to assign values to pointers, and how to test whether or not they are null, so let's take a look at comparing them to other pointers and variables of the same type:
If you want to see what a pointer is actually pointing to, you can use the boolean operators == (checks equivalency) and != (checks inequality). So, to see if GUIUnderMouse is MYGUI or not, you could do this:


  GUI *GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y);
   if (GUIUnderMouse == gMygui)
   if (GUIUnderMouse == gMygui) {
     Display("MYGUI is under the mouse!");
     Display("MYGUI is under the mouse!");
    }
   else if (GUIUnderMouse != gMygui)
   else if (GUIUnderMouse != gMygui) {
     Display("MYGUI is not under the mouse!");
     Display("MYGUI is not 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 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).
===Null Pointers===
If a pointer isn't pointing to anything, it is known as a null pointer. It will actually hold the value '''null''' (which is equivalent to 0). Operations on null pointers will cause the game to crash, so you should always be sure that your pointer is non-null before using it.
 
You check if a pointer is null or not the same way you would normally check a pointer's value:
 
if (GUIPointer == null) { /* the pointer is null */ }
else { /* the pointer is non-null */ }


==What Pointers ''Do''==
==What Pointers ''Do''==
''Okay, so we can create, assign, and test pointers, but what do they DO?''
''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. Pointers can provide major advantages over systems which lack them, in several areas:
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. Pointers can provide major advantages over systems which lack them, in several areas:

Revision as of 22:38, 6 October 2006

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

If you're reading this, then you've come looking for answers. What are pointers? How do I use them? What makes them better than just using integers? These questions, amongst others shall be answered. You're not alone in the confusion caused by pointers. Many people who are new to scripting, or haven't ever used a language that supported pointers don't understand them right away. That's why I'm here to help.

What Are Pointers?

So what exactly are pointers?

To understand what a pointer is, we must first understand the more basic idea of variables. If you already understand variables, you can skip over this section.

Variables

A variable, in the context of scripting, is a way to represent a value. In AGS, there are five different data types which can be used to represent a variable. These types include char, short, int, String1, and float2. A char-type variable is one that can hold only a single character (i.e., 'A', 'B', etc.) or a number within the range 0 to 255. A short-type variable can store integers within the range -32768 to 32767. An int-type variable can store integer values within the range -2147483648 to 2147483647. A String-type variable can hold a string of characters (i.e., "this is some text!") of virtually infinite length. A float-type variable can store floating-point decimals within the range -2147483648.0 to 2147483647.0, and has precision up to approximately 6 decimal places, though this will vary based on the actual number.

For information on defining and assigning values to variables read the entry in the manual here.

1The String type is only defined as of AGS v2.71 and higher. Older versions use the now deprecated string type.

2Floating-point decimals won't always evaluate as you might expect when doing certain mathematical operations (this is due to their precision levels). See the manual entry on data types for more information.

Pointers

Okay, so now that we understand what a variable is, we can begin to understand what a pointer does. The basic idea of a pointer is that instead of creating a new variable, we are simply going to point to a variable that is already stored in the memory. This can have several uses in scripting, and AGS even has some special ones.

Defining A Pointer

And, how do I use them?

In AGS, you can only create pointers to certain data types. These are called the managed types.

Managed Types

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).

Working With Managed Types

You can work with these managed types through pointers. You define a pointer by typing the name of the managed data type, then a space, an asterik (*)3, and finally the name of the pointer. So, if we want to create a GUI pointer (this is expressed as GUI*) called GUIPointer, we could type the following:

 GUI *GUIPointer;

This creates a pointer that can point to any GUI stored in the memory. However, until it is assigned a value, it is an empty, or null pointer. We'll first discuss how to assign pointers a value, then we'll discuss null pointers.

Assigning A Pointer A Value

To make it point to a GUI, you assign it the value of the GUI you want it to point to (with the assignment operator, '='). So to make GUIPointer point at the GUI named MYGUI, you would type:

 GUIPointer = gMygui;

As long as the pointer isn't global (i.e., the pointer is defined inside of a function), then you can also assign it an inital value when you create it, like this:

 GUI *GUIPointer = gMygui;

Global pointers can't have an initial value assigned though, so this will only work if you define the pointer inside of a function. When defining more than one pointer of the same type at once, it is necessary to use an asterik for every pointer. So, if you want MyGUIPointer to point to MYGUI, and OtherGUIPointer to point to OTHERGUI, you can do this:

 GUI *MyGUIPOINTER = gMygui, *OtherGUIPointer = gOthergui;

If you forget an asterik then it will try to create a new instance (create a new variable) of the type GUI. AGS doesn't allow the user to create new instances of managed types, so this would crash your game. So, it's always important to remember your asteriks.

3The asterik doesn't necessarily have to be attached to the name of the pointer, such as "GUI *MyGUIPointer", it can also be attached to the data type itself, such as "GUI* MyGUIPointer". However, it will still be compiled as if it is attached to the name of the pointer, not the data type, so if you define multiple pointers at once, you will still need an asterik for each pointer.

A More Useful Assignment

This type of assignment is rather pointless however, unless you just want a new alias for your GUIs. A more useful assignment makes use of the function GUI.GetAtScreenXY. This function returns a GUI* to the GUI at the specifed coordinates. So, if you wanted to see what GUI the mouse was over, you could do this:

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

Testing A Pointer's Value

If you want to see what a pointer is actually pointing to, you can use the boolean operators == (checks equivalency) and != (checks inequality). So, to see if GUIUnderMouse is MYGUI or not, you could do this:

 if (GUIUnderMouse == gMygui)
   Display("MYGUI is under the mouse!");
 else if (GUIUnderMouse != gMygui)
   Display("MYGUI is not under the mouse!");

Null Pointers

If a pointer isn't pointing to anything, it is known as a null pointer. It will actually hold the value null (which is equivalent to 0). Operations on null pointers will cause the game to crash, so you should always be sure that your pointer is non-null before using it.

You check if a pointer is null or not the same way you would normally check a pointer's value:

if (GUIPointer == null) { /* the pointer is null */ } else { /* the pointer is non-null */ }

What Pointers Do

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. Pointers can provide major advantages over systems which lack them, in several areas:

Pointer System Versus Integral System

In a system with no pointers, you are likely to use a lot of integral references in referring to variables stored in the system. It's obvious how this could become difficult to work with, having to remember what number this character was, or what number that hotspot was, or what this integer flag you stored was. So let's compare some of the advantages:

Character* and InventoryItem*

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. 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*).

File*

Another example can be seen if we look at the File type.

In a system without pointers, you might have to do something like this to work with a(n external) file:

 int handle = FileOpen ("temp.tmp", FILE_WRITE);
 if (handle == 0) Display("Error opening file.");
 else {
   FileWrite (handle, "test string");
   FileClose (handle);
   }

This is difficult to work with because you have to create a new integer handle the file, use it while working with the file, and then make sure to close the file using it's handle.

In a system with pointers, you can do this instead:

 File *output = File.Open("temp.tmp", eFileWrite);
 if (output == null)
   Display("Error opening file.");
 else {
   output.WriteString("test string");
   output.Close();
   }

You still have to create a variable (or rather a pointer) to handle the file, but it becomes much more clear what it is for (as opposed to integers which are likely much more common in your scripts).

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.