Game Development Community

Arrays

by Ryan Ackley · in Torque Game Builder · 09/06/2005 (8:58 pm) · 17 replies

Say I setup an object, say a static sprite like so:
%card = new fxStaticSprite2D() { scenegraph = t2dSceneGraph;  };
and i setup the image map, etc as well.

now, say i take that card and add it to a deck (just an array):
$deck[0]=%card;

Now say, after shuffling, I deal that card onto a hand (just another array of cards)
$grid[0]=$deck[0];

Are they all pointing at the same object? Like, say if i delete %card (assuming they are all within the same scope) does the objects in the first cell of each of the arrays cease to be (At least, does TorqueScript do this by reference or instance? Am i making a copy of the object, or is each copy just a reference to the same object?)

I did a quick search, but did not return much, though, maybe my query phrasing wasn't any good.

edit:
It seems if I do:
$grid[0]=$deck[0];
$grid[1]=$deck[0];
$grid[0].setposition("-32 -20");
$grid[1].setposition("-20 -20");

The card that was in $deck[0] is only drawn once on screen (or, at least, is drawn once in one position, then its position gets changed in the very next line of code), so my guess is that it would be a reference, not an instance.

#1
09/06/2005 (10:28 pm)
Try doing this from the console and you will see:
echo($deck[0]);
echo($grid[0]);

It is a string containing an integer handle #. Here is a snipped from the TGE torquescript documentation

Quote:Handles and Names

Every object in the game is identified and tracked by two parameters:

* Handle- Every object is assigned a unique numeric ID upon creation. This is generally referred to as the object's handle.
* Name- Additionally, all objects may have a name.

In most cases, Handles and names may be used interchangeably to refer to the same object, but a word of caution is in order. Handles are always unique, whereas multiple objects may have the same name. If you have multiple objects with the same name, referencing that name will find one and only one of the objects.
#2
09/07/2005 (7:21 pm)
Yeah, thats basically what I found out. Does any other OO programmer find the way torquescript does this sort of thing a bit of a PITA? Here is a bit of code:
%card = new fxStaticSprite2D() { scenegraph = t2dSceneGraph;  };
%card.setImageMap( card1ImageMap );
%card.setSize( "10 8" );
$deck[0]=%card;


%card = new fxStaticSprite2D() { scenegraph = t2dSceneGraph;  };
%card.setImageMap( card2ImageMap );
%card.setSize( "10 8" );
$deck[1]=%card;

$grid[0]=$deck[0];
$grid[1]=$deck[0];

Basically, both grid[0] and deck[0] point to the same object. Say i wanted to keep grid[0] and delete deck[0]? I find it odd that i couldnt copy an object from an array, do some manipulations, and then delete it without messing up the original.

So, maybe I'll pose this a different way: Im writing a card game (a single player version of set, if you must know). Basically, what i am doing is setting up all images, then creating staticsprites from those images, adding each one to an array, then removing them as I deal the cards out. I find it hard to remove a card from the deck and put it in the grid if i cant delete the object! It is kind of frustrating when then the basic gameplay is done and i am a few bugs short of finishing a game (which i have put about 6 hours of actual programming into, in this RAD ascpect, t2d rocks!)
#3
09/07/2005 (9:36 pm)
Ryan, I certainly don't have any control over the design of torquescript; but I don't understand how you would have torquescript behave differently.

Give a counter-example in a different language? Java, C++?
#4
09/07/2005 (9:37 pm)
Add the below code to 'engine/console/simBase.cc' some place.

What this code will do is create a new object of the same type as the object
you want to copy. It will then copy all of the fields from that object to the
new object. Now you want to be careful with this because the new object
isn't going to be an exact duplicate. In most cases this will work fine, but
what it won't do is copy any internal states the object may have that isn't
exposed to script via a field.

This uses a similar method to the datablock "copy constructor" ie:
datablock aDatablock(bar : foo)
{
   ...
};

So any way, here is the code:
ConsoleMethod(SimObject, createCopy, S32, 2, 2, "obj.createCopy()")
{
   const char* className = object->getClassName();
   ConsoleObject *co = ConsoleObject::create(className);
   SimObject *copy = dynamic_cast<SimObject*>(co);

   // make sure we have something
   if(!copy)
   {
      Con::errorf("SimObject::createCopy - error creating copy, '%s'!", className);
      if(co)
         delete co;

      return NULL;
   }

   copy->registerObject();
   copy->assignFieldsFrom(object);

   return copy->getId();
}

To use it in script, example:
%someObject = new SimGroup() {
};

%copyObject = %someObject.createCopy();

Now if you wanted to get knee deep in engine changes what you could do is
add copy constructors to all SimObject inherited classes and then change the
above code to reflect the addition of the copy constructor so that you can copy
any internal values in addition to the script fields. Which you can imagine is not
a very fun task.
#5
09/07/2005 (9:37 pm)
@Ryan: And you can delete the object, you just have to purge it's ID # out of the array also.

If you are finishing a game after 6 hours of programming; wow- you are a wizard even if T2D gave you a running start.
#6
09/07/2005 (9:40 pm)
Basically the above is useless if you want to do anything more then just copy fields.
#7
09/08/2005 (7:05 am)
@alex, In c++, say you had an object (doesnt realy matter what it is, say a string) and an array:
mString="blah";
mArray[0]=mString;
mString="blah2";
cout<<mString;
cout<<mArray[0];

(the code isn't syntatically correct, but you get the idea). When you print each out, they are different. In Torquescript, if i change mString, both mString and mArray[0] get changed. It is at least odd in my mind

also, alex, It is a card game, I already knew how to play, etc. I wrote most of it in one sitting, untill i hit this problem with the way I keep track of data in the game. All i really need to do is clean up the deck operations and finish the GUI, and its done.

OK, maybe ill try something else:
I need to manage a deck of 81 cards. I start with an array of them, which is fine. Basically, what i was doing before was having another array which would represent the cards on the table (dont really need to do that, I can just get the objects when clicked on). Once I am done with them, I'll delete them, which would Also remove them from the deck array.

Ok, one more question: if i have 3 objects in an array, and delete the second one, I still have an issue with that second cell being empty, right? Is there an easy way to trim that, or do i need to write a nice deletecell funtion in torque script?

Thanks robert, thats kind of what i was thinking of doing, but my implementation was based on bad assumptions on my part, i think i just figured a way to accomplish what i want. Now if i wasnt stuck at work for the next 12 hours :(
#8
09/08/2005 (9:13 am)
You could use a SimSet


new SimSet(deck);

then just add the objects to it

deck.add(card1);
deck.add(card2);
deck.add(card3);

then when you need to remove just do

deck.remove(card2);

and using deck.getCount();
and deck.getObject(%num);

you can manage it
#9
09/08/2005 (10:15 am)
Matt, once again you have saved the day. That is totally awesome, thanks so much.

That is indeed one hell of a lot easier than what i was planning on doing :)

Now, if i could leave work before 10PM :)
#10
09/08/2005 (9:05 pm)
Quote:When you print each out, they are different. In Torquescript, if i change mString, both mString and mArray[0] get changed. It is at least odd in my mind

@Ryan- In torquescript they are not both changed. Here is your code example in torquescript

$mString="blah";
	$mArray[0]= $mString;
	$mString="blah2";
	echo( $mString );
	echo( $mArray[0] );

It outputs

blah2
blah

Anyways, here are some other resources for getting better arrays and hash/map type of objects torquescript

A powerful array class designed for use in TGE script allowing array merging, cropping, sorting and many other features.
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=4711

STL wrappers
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=8557
#11
09/08/2005 (9:22 pm)
Alex, after some investigation, you are indeed right. What i was doing before was very flawed, and thus lead to my confusion. However, I still like the simset, from my initial investigation it seems to do exactly what I need.

Thanks for the links, I am going to need a tried and true array for another project, so they are a good read anyhow.

Oh, and I am going to back up my 6ish hour game claims, its just that Ive worked two 14 hour days since i started this project, not leaving much time for personal projects :) (much less a few hours to clean up my code and post in the show off forums)

Thanks again alex. It is nice to know that there are people here who are willing to offer help till you figure it out :)
#12
09/08/2005 (9:31 pm)
Glad to help :-) T2D rocks
#13
09/09/2005 (9:34 am)
The combination of SimSets and ScriptObject with attached varaibles and arrays can create some powerful data storage :)
#14
09/09/2005 (9:37 pm)
Matthew: Is there anywhere to go to learn about ScriptObject?

Some guy was going to write up about them and other stuff, but I don't think he ever did (maybe he's still working on it)
#15
09/09/2005 (10:17 pm)
Heres some basic info on SimSets and ScriptObject :) (copied from an older post of mine)




There are also SimGroups... which are pretty much the same except for two factors
- When deleted it deletes everything within it (can imagine some very usefull aspects for this)
- Can only be in one SimGroup at a time


there are also ScriptObjects, like

new ScriptObject(testObject);

then you can define parameters on it

testObject.data[0] = "blah0";
testObject.data[1] = "blah1";
testObject.data[2] = "blah2";
testObject.value = 100;

etc...

then you can store these in SimSets/SimGroups since they can only store objects...

all objects have a built in save function as well, so if you create the testObject like I did and then created a simset like this

new SimSet(gameData);

then added the ScriptObject to it

gameData.add( testObject );

you then could type this

gameData.save("T2D/client/saves/save.cs");

... open up that file with a text editor and you get this

//--- OBJECT WRITE BEGIN ---
new SimSet(gameData) {

   new ScriptObject(testObject) {
         value = "100";
         data1 = "blah1";
         data2 = "blah2";
         data0 = "blah0";
   };
};
//--- OBJECT WRITE END ---

Now ScriptObject can be copied... you can do this

new ScriptObject(test1:testObject);
new ScriptObject(test2:testObject);
new ScriptObject(test3:testObject);

now if we add them too

gameData.add(test1);
gameData.add(test2);
gameData.add(test3);

and re-save it out, we get this

//--- OBJECT WRITE BEGIN ---
new SimSet(gameData) {

   new ScriptObject(testObject) {
         value = "100";
         data1 = "blah1";
         data2 = "blah2";
         data0 = "blah0";
   };
   new ScriptObject(test1) {
         value = "100";
         data1 = "blah1";
         data2 = "blah2";
         data0 = "blah0";
   };
   new ScriptObject(test2) {
         value = "100";
         data1 = "blah1";
         data2 = "blah2";
         data0 = "blah0";
   };
   new ScriptObject(test3) {
         value = "100";
         data1 = "blah1";
         data2 = "blah2";
         data0 = "blah0";
   };
};
//--- OBJECT WRITE END ---

Now this data can be reloaded when the game loads again with this command
exec("T2D/client/saves/save.cs");

edit: reformatted board code blocks :)
#16
09/09/2005 (10:18 pm)
Script objects also have three levels of inheritence... say we created a base class like this
and add some starting values to it

function createEntityClass()
{
   new ScriptObject(Entity){
      health = 100;
      mana = 250;
      strength = 10;
   };
}


now lets make our own onAdd function attached to this "Entity" object which will get called everytime an entity is created...

function Entity::onAdd(%this)
{
   echo("New Entity added -" SPC %this.getName());
}

then we can create a child class of it, like this

function createOrcClass()
{
   new ScriptObject(Orc) {
      class = Entity;   
   };
   Orc.health = Orc.class.health + 50;
   Orc.strength = Orc.class.strength + 5;
}

(I did the health and strength that way so it would build off of its class)

run the createEntityClass(); function in the console and you should see this
Quote:
New Entity added - Entity

then you should see this in the console when createOrc(); function is run

Quote:
New Entity added - Orc

now you can create a function like this

function createOrc(%name)
{
   new ScriptObject(%name){
      class = Orc;
      superClass = Entity;
   };
}

then just call it like this

createOrc(Bob);
and if you want to overide the entity onAdd you can do so by adding one to Orc

function Orc::onAdd(%this)
{
   echo("New Orc of added -" SPC %this.getName());
}

and it will fire when you create an orc

now to test this add

function Entity::yell(%this)
{
   echo("This yelled -" SPC %this.getName());
}

then whatever orcs you created with "createOrc" call ".yell();" on them and you should see the console react properly
#17
09/10/2005 (3:47 am)
Good stuff Matthew, you are a good man :)