Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
115 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Introduction to GameMonkey Script Part 1
Language Introduction


Simulation of 'structs' and simple classes with Tables

The final use of the table structure is to simulate C/C++ structs and classes. If you recall what I mentioned before, the GM Script table object can store any type of data, including functions. Because of this, you can assign a scripted function to an index or key within a table. You should be aware that when declaring a function as a table member you should not put the semi-colon line terminator as you do when declaring a function on its own.

myStruct = table(
	SayHello = function() { print( "Hello, world!" ); }	
);
myStruct.SayHello();	// Call table-bound function

Example code: tables_6.gm

As you see in the example, you can access keyed table data using the period (dot) operator. This allows us to treat the table as a simple class structure, accessing the named elements in a familiar fashion.

myAlien = table(
	Name = "Alien",
	AttackPower = 20,
	Strength = 50,
	OnAttack = function( entity )
		{
			entity.Damage( this.AttackPower );
		}
	);

Example code: tables_7.gm

The slightly more complex example shows how simply a generic alien scripted object can be created using the basic GameMonkey Script types and how it is centred primarily around the use of the table object.

Unlike C++ classes, it is important to note that the GM Script table object has no constructor/destructor, cannot be inherited from and does not allow for custom operator overriding. However, you can achieve such behaviour through creating your own bound types (covered in the next instalment of this article). It should also be noted that GM tables have no concept of public, private and protected scoping as C++ presents for structs and classes. All table members are declared as being in the public scope and so can be accessed from anywhere. I will continue the scoping discussion in the next section.

Scoping

GameMonkey script has a range of scopes for variables (and hence functions). If you wish your functions or methods to be accessible from outside of the script (for example, to be read directly by the host application) you must declare them as being in the global scope. The global scope is accessible everywhere in the script; even within other functions. Without this declaration, the objects are implicitly within local scope, which means they're only accessible to within the current scope or lower.

// Create a variable in the global scope
global myvar = 100;

// parameter 'a_param' is in function local scope
myfunc = function( a_param )
{
	// variable myvar is in local scope
	myvar = a_param;
	print( myvar );
};

print( myvar );
myfunc( 50 );
print( myvar );

Example code: scoping_1.gm

Hold up a minute; you will notice that I've created 2 variables called myvar, one in the function and the other in global scope. If you run this script you will notice that the value of the global myvar is unchanged, even though you set the value of myvar in the function. The reason for this is simple; they exist in different scopes! GameMonkey allows you to set global variables from within functions by explicitly specifying the scope of the variable. In this case, I add the global keyword to the myvar declaration in myfunc.

// Create a variable in the global scope
global myvar = 100;

// parameter 'a_param' is in function local scope
myfunc = function( a_param )
{
	// Access variable myvar in global scope
	global myvar = a_param;
	print( myvar );
};

print( myvar );
myfunc( 50 );
print( myvar );

Example code: scoping_2.gm

Things can begin to become tricky, however, when using tables and the this operator. Whenever a variable is part of a table or user-defined object, it exists in the member scope of the parent object, or this. This concept will be familiar to you if you've done any work in C++, so I will not dwell on it. Let's have a look at the member scoping in use:

global mytable = table(
	myMember = 50,
	setMember = function( a_value )
	{
		myMember = a_value;
	}
);

print( mytable.myMember );
mytable.setMember( 100 );
print( mytable.myMember );

Example code: scoping_3.gm

The script above behaves similarly to the local scoping example; the myMember method isn't altered. However, when you include the member scoping keyword you will see a different result.

global mytable = table(
	myMember = 50,
	setMember = function( a_value )
	{
		member myMember = a_value;
	}
);

print( mytable.myMember );
mytable.setMember( 100 );
print( mytable.myMember );

Example code: scoping_4.gm

The this scoping is fairly complicated, but at the same time is very powerful. Using this scoping you can create generic delegates that can access the data of the object that is passed as this. Confused? Take a look at the following example:

myTable = table(
	myMember = 50
);

setMember = function( a_param )
{
	this.myMember = a_param;
};

print( myTable.myMember );
myTable:setMember( 100 );
print( myTable.myMember );

Example code: scoping_5.gm

In this example the function setMember is completely external to the myTable object but is able to access its data and methods. The reason it is able to do this is though use of passing the myTable object as this when calling the setMember function. The body of setMember explicitly states that it will alter the data belonging to this without actually belonging to this at compile time. This allows you to create very powerful scripted functions which can exist in the global scope and be called from objects as if they were a member of that object itself. An abbreviation for typing this is to simply type a single period '.'. For a more complex example of this in action, please refer to scoping_6.gm which is included with this article.

It should be noted that this scoping is different to member scoping, although you could mistake the two if you're accustomed to C++. This scoping refers to the scope of the object passed as this. Member scoping allows you to specify that a variable is a member of this, making it of use in situations where a global or local member may conflict with your attempts to access a member variable of an object.





Further Exploration


Contents
  Introduction
  Introduction to the Language
  Expression and Conditional Syntax
  The Table Type
  Simulation with Tables
  Further Exploration

  Source code
  Printable version
  Discuss this article

The Series
  Language Introduction
  Embedding GameMonkey