Game Development Community

Global Variables, New Objects and Functions Problem

by John Cabral · in Torque Game Builder · 08/14/2005 (12:44 pm) · 7 replies

Hi,
I am working on my first project with T2D, basically my knock off of Tetris. And I've localized a problem, but I am not sure what I am doing wrong.

Right now, I'm just trying to display blocks to the screen and then move them around. I am using 4 global variables to hold the blocks that are currently in play. I had designed a couple of utility functions that would take one of the global variables as a parameter and then create instances of fxStaticSprite2D. So, the global would store the handle when it exits the function, so that the player can then control it.

The functions are creating the relevant images and displaying them to the screen; however, the global variables do not have them when they exit the function. I'm trying to figure out what is going on and how I can use functions to create new variables and to pass them to global variables.

Here's an illustration:

Assume that we have two instances of fxImageMapDatablock2D: redImageMap and blueImageMap.
And that we have a scene graph t2dScenegraph

In my main code I execute the function: testSquares()

Here is the relevant code:

function addObjectHandleToGlobal(%global)
//simple function to create a new object and pass its handle to a global variables.
{
	%global = new fxStaticSprite2D(){sceneGraph = t2dScenegraph;};
	%global.setSize("5 5");
	%global.setPosition("-5 -30");
	%global.setImageMap(blueImageMap);
}

function testSquares()
//basic function to test addObjectHandle versus directly creating an object.
{
	$block1 = new fxStaticSprite2D(){sceneGraph = t2dScenegraph;};
	$block1.setSize("5 5");
	$block1.setPosition("-10 -20");
	$block1.setImageMap(redImageMap);
	
	echo("block1's x position is: " SPC $block1.getPositionX());
	echo("block1's y position is: " SPC $block1.getPositionY());
	
	addObjectHandleToGlobal($block1);
	
	echo("block1's x position is: " SPC $block1.getPositionX());
	echo("block1's y position is: " SPC $block1.getPositionY());
	
}

When I run this, I have both a red and a blue square appear on the screen.

However, I get the following in the output log:

block1's x position is:  -10
block1's y position is:  -20
block1's x position is:  -10
block1's y position is:  -20

Since both objects show up on screen, the new object is being created within the function. However, why is its handle not stored in the global variable $block1 when the program exits addObjectHandleToGlobal()?


Thanks in advance. Torque2D is great. I wish I new enough to take full advantage of it.

#1
08/14/2005 (1:00 pm)
OK you have a couple of misconceptions that we can hopefully clear up:

1) When you store an object reference, what you are storing (either global or local), is the object's simulation ID. This is a guaranteed unique number that is associated with that object, but other than that it doesn't have any special protections or associations.

2) When you pass an object ID as a parameter to a function, you are in simply sending a string to be used locally. You really aren't passing by reference, at least not in the way that you might expect.

Ok, now, let's step through your code right quick, assuming that you are calling testSquares() as your entry point:

A) A new fxStaticSprite2D is created, and the object ID is stored in the global variable $block1.

B) You set it's size, position, and image, and then echo out the position values.

C) You make a function call (not associated with a namespace, therefore it is a function, not a method), sending the objectID of your $block1 as a parameter to the function addObjectHandleToGlobal. Note: Since this is a function, not a method, TorqueScript does not automatically pre-pend the object ID of the calling object (there is no calling object).
--The simulation does however send in the object id of $block1 since it's a param, and therefore as the script function is entered, the value of %global IS set to the value of $block1.

D) Now, here's where things break down from what you think you want: Since the very first thing you do is to create another new object and assign it's objectID to the local variable %global, you no longer have any association whatsoever with the object referenced by the objectID stored in $block1. You set your new values, and they all apply to the new object that you just created. However, you did NOT change the value of $block1, and you did not over-write the values in the initial object's space.

E) You return from the called function, but do not send a return value back. It is common in this type of situation to return an objectID of any object(s) you create within a function or method, or to assign them to global variables, because if you do not, you have effectively "orphaned" the object--you have no way of referencing the object once you've left scope, since you didn't "save" the objectID handle anywhere. It's still in the simulation, and there are ways of accessing it, but without knowing either an objects (unique) name, or the object's ID, there really isn't any way to access this specific object directly.

F) Since you've not actually changed the value of $block1, when you echo out again the values, you get exactly what you got before...the initial values you set before your function call.
#2
08/14/2005 (1:25 pm)
Thank you very much, Stephen.

My C++ teacher would surely have put his foot in my rump for forgetting the distinction between pass-by-value and pass-by-reference. I just fixed things and it's working as it should.

For other "junior programers"* out there, here's how to fix this:

function addObjectHandleToGlobal(%global)
{
	%global = new fxStaticSprite2D(){sceneGraph = t2dScenegraph;};
	%global.setSize("5 5");
	%global.setPosition("-5 -30");
	%global.setImageMap(blueImageMap);
}

function returnObjectHandle()
{
	%handle = new fxStaticSprite2D(){sceneGraph = t2dScenegraph;};
	%handle.setSize("5 5");
	%handle.setPosition("-5 -30");
	%handle.setImageMap(blueImageMap);
	return %handle;
}

function testSquares()
//basic function to check this code
{
	$block1 = new fxStaticSprite2D(){sceneGraph = t2dScenegraph;};
	$block1.setSize("5 5");
	$block1.setPosition("-10 -20");
	$block1.setImageMap(redImageMap);
	
	echo("block1's x position is: " SPC $block1.getPositionX());
	echo("block1's y position is: " SPC $block1.getPositionY());
	
        //misguided attempt at pass by reference.  -- can you say "memory leak"?
	addObjectHandleToGlobal($block1);
	
	echo("block1's x position is: " SPC $block1.getPositionX());
	echo("block1's y position is: " SPC $block1.getPositionY());
	
        //correct attempt at returning an objectID from a function
	$block1 = returnObjectHandle();
	echo("block1's x position is: " SPC $block1.getPositionX());
	echo("block1's y position is: " SPC $block1.getPositionY());
}


The new log

block1's x position is:  -10
block1's y position is:  -20
block1's x position is:  -10
block1's y position is:  -20
block1's x position is:  -5
block1's y position is:  -30


*aka, noob, newb, nube.
#3
08/14/2005 (1:41 pm)
Looking much better, except for one thing:

You still have a (semi) memory leak here, since you are in effect over-writing your initial $block1 value with the call to your function...the object still exists of course, but you've now lost your one real reference to it. And since both returnObjectHandle() and addObjectHandleToGlobal() create new objects, you actually have 3 objects now in your simulation at the end of this run, but are only storing the objectID of one for later use.

As an aside, one of the things I found very handy for myself while learning Torque was to define what my actual problem statement was, and then once I had an implementation I thought might work, go back and re-evaluate if I met my problem statement requirements. While it sounds very basic (and is of course "proper" software design technique), many times people think "this is too simple to be so formal", and blow it off.

The reason I bring this up is that it's a bit difficult to understand what you are actually trying to accomplish here...are you trying to come up with a generic "create object" function/method, or are you just experimenting with various techniques?
#4
08/14/2005 (5:12 pm)
Stephen,
Thanks for pointing out the memory leak.

Here's the proper code. The functions have been altered to focus on having internal global variables. At the end of the day, everything is deleted. So, this more properly handles the allocated memory.

//bad function - only to show you what not to do
function addObjectHandleToGlobal(%global)
{
	$block2 = new fxStaticSprite2D(){sceneGraph = t2dScenegraph;};
	$block2.setSize("5 5");
	$block2.setPosition("-5 -30");
	$block2.setImageMap(blueImageMap);
	%global = $block2;
}
//good function, used to allocate a new object and return it to a variable
function returnObjectHandle()
{
	%handle = new fxStaticSprite2D(){sceneGraph = t2dScenegraph;};
	%handle.setSize("5 5");
	%handle.setPosition("-5 -30");
	%handle.setImageMap(blueImageMap);
	return %handle;
}
//just a function that uses a global variable internally, just an illustation
function alterBlock1()
{
	$block1.setSize("5 5");
	$block1.setPosition("-15 -15");
}
	
function testSquares()
//basic function to check this code creates a block, allows you to move it around
{
        //vanilla creation of a new object and passing its handle to a global
	$block1 = new fxStaticSprite2D(){sceneGraph = t2dScenegraph;};
	$block1.setSize("5 5");
	$block1.setPosition("-10 -20");
	$block1.setImageMap(redImageMap);

	echo("After direct instantiation");
	echo("block1's x position is: " SPC $block1.getPositionX());
	echo("block1's y position is: " SPC $block1.getPositionY());

	$block1.safeDelete();
	
        //failed attempt to pass by reference, but with an internal global to keep track of the 
        //allocated object
	addObjectHandleToGlobal($block1);
	echo("After passing as value");
	echo("block1's x position is: " SPC $block1.getPositionX());
	echo("block1's y position is: " SPC $block1.getPositionY());
	
	echo("block2's x position is: " SPC $block2.getPositionX());
	echo("block2's y position is: " SPC $block2.getPositionY());
	
	$block2.safeDelete();

	echo("after returning the handle");

        //creating a new object inside the function and returning its objectID to  a global

	$block1 = returnObjectHandle();

	echo("block1's x position is: " SPC $block1.getPositionX());
	echo("block1's y position is: " SPC $block1.getPositionY());

        //using a function with a global variable on the inside to manipulate 	
	alterBlock1();
	echo("after the alter block");
	echo("block1's x position is: " SPC $block1.getPositionX());
	echo("block1's y position is: " SPC $block1.getPositionY());
	
	$block1.safeDelete();

Here's the log:

After direct instantiation
block1's x position is:  -10
block1's y position is:  -20
After passing as value
block1's x position is:  -10
block1's y position is:  -20
block2's x position is:  -5
block2's y position is:  -30
after returning the handle
block1's x position is:  -5
block1's y position is:  -30
after the alter block
block1's x position is:  -15
block1's y position is:  -15

Thank you for the advice, Stephen. I won't waste forum space with a plan for a Tetris game. The functions I posted here were distilled down to a minimal amount that would (a) do something on the screen and (b) exhibit the behavior I couldn't figure out. So, they aren't intended to actually do something useful within a game.

However, I am in a situation where I'm learning to code on my own. My dream is to develop AI in C++ to work with the Torque2D engine, so I'm going to have to get a lot better as a software engineer. Are there resources you could point so that I can better refine my programming skills?

Again, many thanks for the clear and complete explanation.
#5
08/15/2005 (12:29 am)
Just a quick skim, but make sure you use $ to denote globals, and % to denote local variables.
#6
08/15/2005 (8:29 am)
I'd have to agree with Stephen, I'm a bit unsure with what your trying to accomplish, some details would probably helps us help you :)

a note

function addObjectHandleToGlobal(%global)
{   
   $block2 = new fxStaticSprite2D(){sceneGraph = t2dScenegraph;};   
   $block2.setSize("5 5");   
   $block2.setPosition("-5 -30");   
   $block2.setImageMap(blueImageMap);   
   %global = $block2;
}

In this function you throw a new fxStaticSprite2D into the global (denoted by the "$" in front of it) variable $block2... you then assign the object ID of $block2 to the local variable (denoted by the "%" in front of it) %global. Now the %global variable is really local so it will be destroyed at the end of the function... however $block2 is still a global variable and is then created and held.
#7
08/16/2005 (5:53 pm)
Sorry that this is causing confusion. The last post was meant to be illustrative of different permutations of global and local variables in functions so that newbs like me can see what will work and what won't work.

%global is supposed to be a local variable. The name is just meant to signify (try to pass a global variable in here and do some manipulations on it). I was trying to be all clever and say, "Hey, I have this variables that have a similar manipulation. So, I'll just pass in a global variable and use the simpler code. " That, of course, didn't work. The introduction of the global variable ($block2) in the later permutation was an attempt to make a whack function less whack by having a global variable receive the newly allocated object so that (1) you could show that it still has it after the function exits and (2) prevent a memory leak within addObjectHandleToGlobal().

So, these functions are just illustrative. They are just playing around with global and local variables in Torque script. They aren't meant to do anything useful.