AGS Pointers for Dummies: Difference between revisions

From Adventure Game Studio | Wiki
Jump to navigation Jump to search
No edit summary
Line 1: Line 1:
=AGS Pointers for Dummies (A reference for the rest of us!)=
<center>'''<font size="4">AGS Pointers for Dummies</font>'''</center>
 
<center>'''''<font size="3">A reference for the rest of us!</font>'''''</center>


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.
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?''


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.
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===
==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.
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''<sup>3</sup> 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].
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.'''
==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.
 
<sup>1</sup>'''<font size="1">The String type is only defined as of AGS v2.71 and higher. Older versions use the now deprecated string type.</font>'''


'''<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.'''
<sup>2</sup>'''<font size="1">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.</font>'''


===Pointers===
<sup>3</sup>'''<font size="1">The length for Strings is limited by your computer's physical memory. A String will take up 4 bytes of memory, plus 1 byte for each character it contains.</font>'''
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 data types. These are called the ''managed'' types.
In AGS, you can only create pointers to certain data types. These are called the ''managed'' types.


===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:  [http://www.bigbluecup.com/manual/Room%20_%20Screen%20functions.htm Room], [http://www.bigbluecup.com/manual/Game%20_%20Global%20functions.htm Game], [http://www.bigbluecup.com/manual/Parser%20functions.htm Parser], [http://www.bigbluecup.com/manual/File%20functions%20and%20properties.htm File], [http://www.bigbluecup.com/manual/Inventory%20item%20functions%20and%20properties.htm InventoryItem], [http://www.bigbluecup.com/manual/Overlay%20functions%20and%20properties.htm Overlay], [http://www.bigbluecup.com/manual/DynamicSprite%20functions%20and%20properties.htm DynamicSprite], [http://www.bigbluecup.com/manual/GUIFuncsAndProps.htm GUI], [http://www.bigbluecup.com/manual/GUI%20Label%20functions%20and%20properties.htm Label], [http://www.bigbluecup.com/manual/GUI%20Button%20functions%20and%20properties.htm Button], [http://www.bigbluecup.com/manual/GUI%20Slider%20properties.htm Slider], [http://www.bigbluecup.com/manual/GUI%20Text%20Box%20functions%20and%20properties.htm TextBox], [http://www.bigbluecup.com/manual/GUIInvFuncs.htm InvWindow], [http://www.bigbluecup.com/manual/GUI%20List%20Box%20functions%20and%20properties.htm ListBox], [http://www.bigbluecup.com/manual/Character%20functions%20and%20properties.htm Character], [http://www.bigbluecup.com/manual/GUI%20control%20functions%20and%20properties.htm GUIControl], [http://www.bigbluecup.com/manual/Hotspot%20functions%20and%20properties.htm Hotspot], [http://www.bigbluecup.com/manual/Region%20functions%20and%20properties.htm Region], [http://www.bigbluecup.com/manual/Maths%20functions%20and%20properties.htm Maths], [http://www.bigbluecup.com/manual/DateTime%20functions%20and%20properties.htm DateTime], and [http://www.bigbluecup.com/manual/Object%20functions%20and%20properties.htm Object].
AGS has certain ''managed'' types that you can not create an instance (variable declaration) of, but you ''can'' create pointers to<sup>4</sup>.  All of the variables of managed types are ''managed'' by AGS.  These include the types:  [http://www.bigbluecup.com/manual/Room%20_%20Screen%20functions.htm Room], [http://www.bigbluecup.com/manual/Game%20_%20Global%20functions.htm Game], [http://www.bigbluecup.com/manual/Parser%20functions.htm Parser], [http://www.bigbluecup.com/manual/File%20functions%20and%20properties.htm File], [http://www.bigbluecup.com/manual/Inventory%20item%20functions%20and%20properties.htm InventoryItem], [http://www.bigbluecup.com/manual/Overlay%20functions%20and%20properties.htm Overlay], [http://www.bigbluecup.com/manual/DynamicSprite%20functions%20and%20properties.htm DynamicSprite], [http://www.bigbluecup.com/manual/GUIFuncsAndProps.htm GUI], [http://www.bigbluecup.com/manual/GUI%20Label%20functions%20and%20properties.htm Label], [http://www.bigbluecup.com/manual/GUI%20Button%20functions%20and%20properties.htm Button], [http://www.bigbluecup.com/manual/GUI%20Slider%20properties.htm Slider], [http://www.bigbluecup.com/manual/GUI%20Text%20Box%20functions%20and%20properties.htm TextBox], [http://www.bigbluecup.com/manual/GUIInvFuncs.htm InvWindow], [http://www.bigbluecup.com/manual/GUI%20List%20Box%20functions%20and%20properties.htm ListBox], [http://www.bigbluecup.com/manual/Character%20functions%20and%20properties.htm Character], [http://www.bigbluecup.com/manual/GUI%20control%20functions%20and%20properties.htm GUIControl], [http://www.bigbluecup.com/manual/Hotspot%20functions%20and%20properties.htm Hotspot], [http://www.bigbluecup.com/manual/Region%20functions%20and%20properties.htm Region], [http://www.bigbluecup.com/manual/Maths%20functions%20and%20properties.htm Maths], [http://www.bigbluecup.com/manual/DateTime%20functions%20and%20properties.htm DateTime], and [http://www.bigbluecup.com/manual/Object%20functions%20and%20properties.htm 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 (*)<sup>5</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:
===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 (*)<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;
   GUI *GUIPointer;
Line 37: Line 39:
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.
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===
==Array of Pointers==
 
It should be noted here that when defining pointers, you can also create an [http://www.bigbluecup.com/manual/Arrays.htm array] of pointers. When you create an array you are simply defining a set of variables (or in this case, pointers) which all have the same name. You access each one individually using an index between brackets ([ and ]).
 
Defining an array of pointers works the same way as defining any other array does, so to define an array of GUI*s called myguis to hold 5 GUI*s, you would type:
 
  GUI *myguis[5];
 
With arrays you can't assign initial values, and the valid indices are from 0 to the size of the array minus one (in this case, 0 to 4). You treat an array of pointers just like you would ordinary pointers.
 
<sup>4</sup>'''<font size="1">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).</font>'''
 
<sup>5</sup>'''<font size="1">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.</font>'''
 
=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:
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:
Line 53: Line 69:
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.
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.


'''<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.'''
==A More Useful Assignment==
 
===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:
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);
   GUI *GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y);


===Testing A Pointer's Value===
=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 you want to see what a pointer is actually pointing to, you can use the boolean operators == (checks if two things are equal) and != (checks if two things are not equal). So, to see if GUIUnderMouse is MYGUI or not, you could do this:


   if (GUIUnderMouse == gMygui)
   if (GUIUnderMouse == gMygui)
Line 68: Line 82:
     Display("MYGUI is not under the mouse!");
     Display("MYGUI is not under the mouse!");


===Null Pointers===
=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.
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:
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 */ }
  if (GUIPointer == null) { /* the pointer is null */ }
else { /* the pointer is non-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 pointers, assign them a value, and test their value, but what do pointers '''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:
We've discussed already that pointers ''point'' to variables stored in the memory to prevent having to reproduce the data, but we haven't actually discussed in depth how this can be used to our advantage.


===Pointer System Versus Integral System===
==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*====
We've seen how a GUI* (remember that this is a GUI-pointer or pointer-to-GUI) can help us find out what GUI is on the screen at certain coordinates, but we could do this with an integral system, such as:
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:
  int gui = GetGUIAt(mouse.x, mouse.y);
  if (gui == gui[MYGUI]) Display("MYGUI is under the mouse!");


  inventory[character[GetPlayerCharacter()].activeinv].Graphic = 42;
So if we could already do this, why change to a pointer system and cause all the confusion? But let's not get ahead of ourselves. Pointers aren't designed with the sole purpose of causing confusion. And they are actually quite useful once you understand them.


However, in a system with pointers, you can type this instead:
===The String Type===


The String type isn't one of AGS's managed types, nor can you create a pointer to it. So why then am I bringing it up? The fact is, the String type is actually internally defined as a pointer, which is how it is able to have it's virtually infinite maximum length.
Prior to AGS 2.71, AGS used the now deprecated string type. The string type was internally defined as an [http://bigbluecup.com/manual/Arrays.htm array] of 200 characters ('''char'''s). This meant that strings had a maximum length of 200 characters themselves.
With the introduction of AGS 2.71 came the new String type which removed that limit. And how did it do it? It used a pointer. Not an AGS-style pointer, but a pointer nonetheless. In programming languages such as C and C++, a pointer-to-char (char*)<sup>6</sup> creates a special type of pointer. Instead of just pointing to one single variable, a char* can point to a virtually infinite number of chars in the form of what is known as a string-literal (such as "this is some text").
<sup>6</sup>'''<font size="1">In AGS you can't create a char*, as char isn't one of AGS's managed types. This type of pointer is used in scripting languages like C and C++. For storing string-literals AGS uses the String type (or the string type for AGS versions prior to 2.71).</font>'''
===Script O-Names===
Script o-names are another example of a pointer system versus an integral one. Basically the way a script o-name is defined is like this:
  // pseudo-AGS-internal-code
  GUI *gMygui = gui[MYGUI];
For all we know the gui array itself could be an array of pointers to something stored deeper within the bowels of AGS, but it's not really important as in the end they would still both point to the same GUI, and this is just an example anyway.
Using an integral system you would have to acess the gui array any time you wanted to perform any operations on the GUI<sup>7</sup>. So, if we wanted to move MYGUI to (30, 120), in an integral system we could do this:
  gui[MYGUI].SetPosition(30, 120);
In a pointer system we would do this:
  gMygui.SetPosition(30, 120);
So it makes our code a bit shorter then, but it's essentially the same. All-in-all not a particularlly convincing example. So let's take a look at another built-in pointer: '''player'''.
<sup>7</sup>'''<font size="1">I have taken the liberty here of envisioning an integral system set up much as AGS 2.7+ is set up, only since it is an integral system it uses integers instead of pointers. In this example AGS structs still have member functions, and all other non-pointer-related functionality of AGS is the same.</font>'''
===Player Keyword===
The player keyword provides a much simpler method for performing operations directly on the character. In an integral system we could use something like this:
  character[GetPlayerCharacter()].Move(20, 100);
With the player keyword we now simply type:
  player.Move(20, 100);
This also provides advantages when working with the player's active inventory item.
===Player.ActiveInventory===
In an integral system to access the player character's active inventory, you would have to do something like this:
  character[GetPlayerCharacter()].activeinv
In a pointer system you do this:
  player.ActiveInventory
But what about when we actually want to '''do''' something with that? Say, for example, changing it's graphic to slot 42:
  // integral system
  inventory[character[GetPlayerCharacter()].activeinv].graphic = 42;
  // pointer system
   player.ActiveInventory.Graphic = 42;
   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*).
Again, it makes the code shorter, but the second snippet is also easier to read, and more obvious what you're trying to do.


====File*====
===File*===
Another example can be seen if we look at the [http://www.bigbluecup.com/manual/File%20functions%20and%20properties.htm File] type.
Another example can be seen if we look at the [http://www.bigbluecup.com/manual/File%20functions%20and%20properties.htm File] type.


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


   int handle = FileOpen ("temp.tmp", FILE_WRITE);
   int handle = FileOpen("temp.tmp", FILE_WRITE);
   if (handle == 0) Display("Error opening file.");
   if (handle == 0) Display("Error opening file.");
   else {
   else {
     FileWrite (handle, "test string");
     FileWrite(handle, "test string");
     FileClose (handle);
     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.
You have to store the file's handle when you open it, and you later have to be sure to close that file using the same handle. In-between this time you have to be sure that the value of the handle doesn't change or get lost.


In a system with pointers, you can do this instead:
In a system with pointers, you can do this instead:
Line 121: Line 190:
     }
     }


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).
You create a File* which points to the opened file. You still have to close the file using the File*, but it's simpler since you are using a specifically created File* instead of just a generic int variable.


''Wait...what's with all this script o-name stuff?''
==Extending The Built-In (Managed) Types==


If you're reading this, you probably didn't ask that, so I asked it for you, because it pertains to this...
Now that we've seen what pointers are, how they are used, how they relate to AGS, and some basic uses of them, let's take a look at a different kind of usage. In AGS we can create our own custom-defined datatypes using [http://www.bigbluecup.com/manual/structs.htm structs].


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:
You define a struct like this:


   // pseudo-internal-AGS-code
   struct MyStruct {
  GUI *gInventory = gui[5];
    int IntMember;
    String StringMember;
    import int do_something();
    };


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.
That would create a new datatype called MyStruct which would have two data members and one member function. You could then create a variable of this type, and do all sorts of fun things with it. Though it's uses don't end there.


''Okay, did you just say something about an array of pointers?''
You can also make a pointer a member of a struct<sup>8</sup>, which provides some interesting possibilities. With a pointer as a member, you can essentially extend built-in datatypes (i.e., the managed types).


Well, I'm not going to go into what an array is ([http://www.bigbluecup.com/manual/Arrays.htm look it up] in [http://www.bigbluecup.com/manual/index.htm 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:
===Extending the Character Type===


  GUI* MyGUIArray[5];
We can extend the built-in Character type using a Character* as a member of one of our structs. So, let's look at how we can do this:
 
  // 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.'''''
   struct CharStats {
 
     int Health;
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:
     int Mana;
 
     Character* Char;
   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:
We've given this new datatype CharStats three members: Health, Mana, and Char. So how does this help us to extend the built-in datatypes then? By assigning a value to Char, we can access all the properties of that Character through our new datatype. First we have to assign the pointer a value, so let's look at that:


   struct CharStats {
   // global script
    int HP;
  CharStats stEgo; // cEgo with Health and Mana properties
    int MP;
   export stEgo; // this makes stEgo global to all scripts, requires an import in the header
    int MaxHP;
    int MaxMP;
    Character* Char;
    };
    
  CharStats stEgo;
    
    
   // game_start
   // game_start function
   stEgo.MaxHP = 100;
   stEgo.Health = 100; // set Ego's Health
   stEgo.MaxMP = 50;
   stEgo.Mana = 80; // set Ego's Mana
  stEgo.HP = 100;
   stEgo.Char = cEgo; // set Ego's Char
  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 [http://www.bigbluecup.com/manual/struct.htm 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 now for the remainder of your game you can use stEgo.Char any place you would normally use cEgo. This way you can put all of your properties and functions for working with Ego into one convenient place!


And just to bring it all together, yes, you can even combine arrays, pointers, and structs all in one:
You can extend any of the managed types that you can create pointers to in this manner.


  struct CharStats {
<sup>8</sup>'''<font size="1">Structs can only have pointers as members in AGS 2.71 and later.</font>'''
    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!
=Closing=


Now then, I hope this helps everyone to understand pointers in AGS a little bit better.
So you came to me with questions, and I hope I've answered some of them at least. In any case I hope I answered the ones you had about pointers and their usage in AGS. If you have any questions or comments you can PM me on the AGS forums, or email me at [mailto:monkey.05.06@gmail.com monkey.05.06@gmail.com] any time. Thanks for reading my article, and I hope you've enjoyed it as much as I enjoyed writing it.


monkey_05_06


[[Category:Intermediate Tutorials]]
[[Category:Intermediate Tutorials]]

Revision as of 22:41, 11 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 virtually3 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.

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.

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.

3The length for Strings is limited by your computer's physical memory. A String will take up 4 bytes of memory, plus 1 byte for each character it contains.

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, but you can create pointers to4. 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.

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 (*)5, 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.

Array of Pointers

It should be noted here that when defining pointers, you can also create an array of pointers. When you create an array you are simply defining a set of variables (or in this case, pointers) which all have the same name. You access each one individually using an index between brackets ([ and ]).

Defining an array of pointers works the same way as defining any other array does, so to define an array of GUI*s called myguis to hold 5 GUI*s, you would type:

 GUI *myguis[5];

With arrays you can't assign initial values, and the valid indices are from 0 to the size of the array minus one (in this case, 0 to 4). You treat an array of pointers just like you would ordinary pointers.

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

5The 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.

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.

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 if two things are equal) and != (checks if two things are not equal). 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 pointers, assign them a value, and test their value, but what do pointers do?

We've discussed already that pointers point to variables stored in the memory to prevent having to reproduce the data, but we haven't actually discussed in depth how this can be used to our advantage.

Pointer System Versus Integral System

We've seen how a GUI* (remember that this is a GUI-pointer or pointer-to-GUI) can help us find out what GUI is on the screen at certain coordinates, but we could do this with an integral system, such as:

 int gui = GetGUIAt(mouse.x, mouse.y);
 if (gui == gui[MYGUI]) Display("MYGUI is under the mouse!");

So if we could already do this, why change to a pointer system and cause all the confusion? But let's not get ahead of ourselves. Pointers aren't designed with the sole purpose of causing confusion. And they are actually quite useful once you understand them.

The String Type

The String type isn't one of AGS's managed types, nor can you create a pointer to it. So why then am I bringing it up? The fact is, the String type is actually internally defined as a pointer, which is how it is able to have it's virtually infinite maximum length.

Prior to AGS 2.71, AGS used the now deprecated string type. The string type was internally defined as an array of 200 characters (chars). This meant that strings had a maximum length of 200 characters themselves.

With the introduction of AGS 2.71 came the new String type which removed that limit. And how did it do it? It used a pointer. Not an AGS-style pointer, but a pointer nonetheless. In programming languages such as C and C++, a pointer-to-char (char*)6 creates a special type of pointer. Instead of just pointing to one single variable, a char* can point to a virtually infinite number of chars in the form of what is known as a string-literal (such as "this is some text").

6In AGS you can't create a char*, as char isn't one of AGS's managed types. This type of pointer is used in scripting languages like C and C++. For storing string-literals AGS uses the String type (or the string type for AGS versions prior to 2.71).

Script O-Names

Script o-names are another example of a pointer system versus an integral one. Basically the way a script o-name is defined is like this:

 // pseudo-AGS-internal-code
 GUI *gMygui = gui[MYGUI];

For all we know the gui array itself could be an array of pointers to something stored deeper within the bowels of AGS, but it's not really important as in the end they would still both point to the same GUI, and this is just an example anyway.

Using an integral system you would have to acess the gui array any time you wanted to perform any operations on the GUI7. So, if we wanted to move MYGUI to (30, 120), in an integral system we could do this:

 gui[MYGUI].SetPosition(30, 120);

In a pointer system we would do this:

 gMygui.SetPosition(30, 120);

So it makes our code a bit shorter then, but it's essentially the same. All-in-all not a particularlly convincing example. So let's take a look at another built-in pointer: player.

7I have taken the liberty here of envisioning an integral system set up much as AGS 2.7+ is set up, only since it is an integral system it uses integers instead of pointers. In this example AGS structs still have member functions, and all other non-pointer-related functionality of AGS is the same.

Player Keyword

The player keyword provides a much simpler method for performing operations directly on the character. In an integral system we could use something like this:

 character[GetPlayerCharacter()].Move(20, 100);

With the player keyword we now simply type:

 player.Move(20, 100);

This also provides advantages when working with the player's active inventory item.

Player.ActiveInventory

In an integral system to access the player character's active inventory, you would have to do something like this:

 character[GetPlayerCharacter()].activeinv

In a pointer system you do this:

 player.ActiveInventory

But what about when we actually want to do something with that? Say, for example, changing it's graphic to slot 42:

 // integral system
 inventory[character[GetPlayerCharacter()].activeinv].graphic = 42;
 // pointer system
 player.ActiveInventory.Graphic = 42;

Again, it makes the code shorter, but the second snippet is also easier to read, and more obvious what you're trying to do.

File*

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

In an integral system, you would access an external file like this:

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

You have to store the file's handle when you open it, and you later have to be sure to close that file using the same handle. In-between this time you have to be sure that the value of the handle doesn't change or get lost.

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 create a File* which points to the opened file. You still have to close the file using the File*, but it's simpler since you are using a specifically created File* instead of just a generic int variable.

Extending The Built-In (Managed) Types

Now that we've seen what pointers are, how they are used, how they relate to AGS, and some basic uses of them, let's take a look at a different kind of usage. In AGS we can create our own custom-defined datatypes using structs.

You define a struct like this:

 struct MyStruct {
   int IntMember;
   String StringMember;
   import int do_something();
   };

That would create a new datatype called MyStruct which would have two data members and one member function. You could then create a variable of this type, and do all sorts of fun things with it. Though it's uses don't end there.

You can also make a pointer a member of a struct8, which provides some interesting possibilities. With a pointer as a member, you can essentially extend built-in datatypes (i.e., the managed types).

Extending the Character Type

We can extend the built-in Character type using a Character* as a member of one of our structs. So, let's look at how we can do this:

 struct CharStats {
   int Health;
   int Mana;
   Character* Char;
   };

We've given this new datatype CharStats three members: Health, Mana, and Char. So how does this help us to extend the built-in datatypes then? By assigning a value to Char, we can access all the properties of that Character through our new datatype. First we have to assign the pointer a value, so let's look at that:

 // global script
 CharStats stEgo; // cEgo with Health and Mana properties
 export stEgo; // this makes stEgo global to all scripts, requires an import in the header
 
 // game_start function
 stEgo.Health = 100; // set Ego's Health
 stEgo.Mana = 80; // set Ego's Mana
 stEgo.Char = cEgo; // set Ego's Char

And now for the remainder of your game you can use stEgo.Char any place you would normally use cEgo. This way you can put all of your properties and functions for working with Ego into one convenient place!

You can extend any of the managed types that you can create pointers to in this manner.

8Structs can only have pointers as members in AGS 2.71 and later.

Closing

So you came to me with questions, and I hope I've answered some of them at least. In any case I hope I answered the ones you had about pointers and their usage in AGS. If you have any questions or comments you can PM me on the AGS forums, or email me at monkey.05.06@gmail.com any time. Thanks for reading my article, and I hope you've enjoyed it as much as I enjoyed writing it.

monkey_05_06