Game Development Community

Names vs. Variables

by Chad Maron · in Torque Game Builder · 05/29/2005 (3:48 pm) · 19 replies

Alright... another n00b question for everyone. Are there any big performance hits to the following?

Using variables:
$Game = new SimObject();

$Game.player = new fxStaticSprite2D() {// ... };
$Game.player.foo = "bar";
// ...

Using names:
new SimObject(Game);

new fxStaticSprite2D(player) {// ... };
Game.player = player;
Game.player.foo = "bar";
// ...

Is there a "proper" way to do this sort of thing? I'd like to have an object that holds most, if not all, of my game goods. Would it be best to create my own object and use it's namespace?

Thanks!
-Chad

#1
05/29/2005 (11:51 pm)
I personally like scriptobjects
$Player = new ScriptObject() {
Class = Player;
Health = 0;
};
$Player.obj = new fxStaticSprite2D()) { sceneGraph= $sceneGraph; };
I like it because it allows me to define in player functions

ie
function Player::setHealth(%this, %amt) {
%this.Health= %amt;
}
function Player::getHealth(%this) {
return %this.Health;
}

In this way it allows me to really control what functions it can call and I can then use it to further the character by adding other script objects to it and have it all (that is relivent to player) in one object. $Player.inventory would be a script object containing information about his / her inventory $Player.stats would contain information about the stats on player (ex RPG: STR, DEX, CON, INT)

Sure it is a lot of programming but I find that creating it in a scriptobject form its a lot easier to test objects and functions as you dont really have to worry about any outside interfirence as long as you always use the set functions you made for the object. If you remember this it also lower the amount of bugs. Or so I think.
#2
05/30/2005 (1:06 pm)
Chris,

That looks pretty cool... I think I might try that out. Thanks! :)

-Chad
#3
05/30/2005 (11:39 pm)
I find it really fun when you can define like an ItemDefault then use a ItemDefault function like create. Here is what I mean.
$DefaultPlant = new ScriptObject() { 
Class = DefaultPlant;
name = "wheat";
yield = 0;
};
function DefaultPlant::create(%this) {
%copyPlant = new ScriptObject() {
Class= Plant;
};
%copyPlant.name= %this.name;
%copyPlant.yield =%this.yield;
return %copyPlant;
}
function Plant::getName(%this) {
return %this.name;
}
With that I can create a bunch of definitions of default objects and do something like $DefaultPlant.create(); and it would create a Plant that I could actually modify. This way I always have a default as to how the object should be build and since I dont have any functions in DefaultPlant class that can change any data in the class I dont have to worry about the defaults changing. Since the new object created by the create function in DefaultPLant class is of the class Plant I can allow the changing of data in the class.

Quite useful if your working on a farmining game like I am.
#4
05/31/2005 (4:02 am)
@Chris: This may be of interest to you then...

@Chad: Though not performance related, this shows something that requires the use of ScriptObject names (or at least I can't get it to compile using variables, $ or % ;-)

When creating a new ScriptObject you can specify it to be a copy of another ScriptObject. To rework Chris' example a little:


function Plant::getName(%this)
{
	return %this.name;
}


new ScriptObject( DefaultPlant )
{
	class = Plant;
	// Could also specify 'superClass' here as well to create limited hierarchy

	name = "wheat";
	yield = 0;
};

// Demonstrate creating objects from a source object...
function DemoScriptObjectCopy()
{
	%newPlant = new ScriptObject( : DefaultPlant );

	echo( %newPlant.getName() );
}

I use this approach to create a 'Class' object that I can then use to make instances of that class (in the same way that DefaultPlant is used in this example). I find this simplifies the creation of objects and doesn't require a 'create' function to give all objects default values.

It is also possible to 'override' the default values in the source object, at point of creation e.g.:

%newPlant2 = new ScriptObject( : DefaultPlant )
	{
		name = "barley";
	};


AFAIK This is a feature of all objects in TorqueScript though only dynamic fields are copied if the source and copy are different object types. Personally I've only used it for ScriptObjects.

Bri

N.B. To use the 'named' object approach with copying the syntax is:

new ScriptObject( myNamedPlant : DefaultPlant )
   {
      name = "barley";
   };

edit: Changed "Class = Plant;" to "class = Plant;"
#5
05/31/2005 (5:24 am)
This is really useful stuff guys :) Any other hints on this would be great.

I think I'll have a go at re-writing my enemy creation functions to make use of this.
#6
05/31/2005 (10:11 am)
Script Objects and Sim Sets work well together... since the .save(path) function is built in, the nice thing of using a SimSet as the top level is when you run the .save() function it will save out all the object info of objects within it, for example

function setUpSimSetData()
{
   new SimSet( gameData );
   new SimSet( plantLife );
   new SimSet( playerInfo );
   
   gameData.add(plantLife);
   gameData.add(playerInfo);

   new ScriptObject( defaultPlant )
   {
      name = "wheat";   
      yield = 0;
   };

   new ScriptObject( player )
   {
      name = "Player";
      Health = 100;
      Power = 50;
   };

   plantLife.add(DefaultPlant);
   playerInfo.add(player);

   gameData.save("T2D/gameData.info");

}

when run, gameData.info looks like this

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

   new SimSet(plantLife) {

      new ScriptObject(defaultPlant) {
            name = "wheat";
            yield = "0";
      };
   };
   new SimSet(playerInfo) {

      new ScriptObject(player) {
            name = "Player";
            Health = "100";
            Power = "50";
      };
   };
};
//--- OBJECT WRITE END ---

on the other hand if we just used script objects like this

function setUpScriptObjectData()
{
   $gameData = new ScriptObject( gameData );
   $plantLife = new ScriptObject( plantLife );
   $playerInfo = new ScriptObject( playerInfo );
   
   gameData.plantInfo = $plantLife;
   gameData.playerData = $playerInfo;

   $defaultPlant = new ScriptObject( defaultPlant )
   {
      name = "wheat";   
      yield = 0;
   };

   $player = new ScriptObject( player )
   {
      name = "Player";
      Health = 100;
      Power = 50;
   };

   plantLife.defaultPlant = $DefaultPlant;
   playerInfo.info = player;

   gameData.save("T2D/gameData.info");

}

it just saves the references to the objects, this will happen when saving out any object reference, static sprites, etc

//--- OBJECT WRITE BEGIN ---
new ScriptObject(gameData) {
      plantInfo = "1571";
      playerData = "1572";
};
//--- OBJECT WRITE END ---

so for saving out data SimSets and ScriptObject work well together :)
#7
05/31/2005 (10:19 am)
Oh nice... that is very useful. :) You guys rock!
#8
06/01/2005 (2:03 am)
Wow thats awsomeI got to start putting that to use!
#9
06/01/2005 (10:58 am)
Another thing to remember is the built in save function will save out the initPersist fields... basically the properties that come up in the .dump() function, member or tagged... these do not include position, size, and imagemap for sprites ... in my Gui Creator I made accessor functions like this

function fxSceneObject2D::imageMap(%this, %image)
{
   %this.imageMap = %image;
   %this.setImageMap(%image);
}

function fxSceneObject2D::position(%this, %pos)
{
   %this.position = %pos;
   %this.setPosition(%pos);
}

function fxSceneObject2D::size(%this, %size)
{
   %this.size = %size;
   %this.setSize(%size);
}

that way if you can create a sprite like this

new fxStaticSprite2D(sprite) { sceneGraph = t2dSceneGraph; };
sprite.imageMap(tileMapImageMap);
sprite.position("0 0");
sprite.size("5 5");

then you attach it to a simset and save it out

new SimSet(gameData);
gameData.add(sprite);
gameData.save("T2D/gameData.cs");

to save it out, you get this

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

   new fxStaticSprite2D(sprite) {
      scenegraph = "t2dSceneGraph";
         imageMap = "tileMapImageMap";
         position = "0 0";
         size = "5 5";
   };
};
//--- OBJECT WRITE END ---

normally you would just get the scenegraph... then its not hard to make a load function to cycle through the simSet and set these properties, like this

function loadData(%file, %setName)
{
   %file = expandFileName(%file);
   if(exec(%file))
   {
      %count = %setName.getCount();
      for(%i=0;%i<%count;%i++)
      {
         %obj = %setName.getObject(%i);
         %obj.setImageMap(%obj.imageMap);
         %obj.setPosition(%obj.position);
         %obj.setSize(%obj.size);
      }
      echo("set loaded!");
   } else
   {
      echo("invalid file name");
   }
}

and it should load all sprites attached to simset with those accessor functions used instead of the normal ones.
#10
06/01/2005 (11:25 am)
new fxStaticSprite2D(sprite) { sceneGraph = t2dSceneGraph; };
sprite.imageMap(tileMapImageMap);
sprite.position("0 0");
sprite.size("5 5");

How would I access this in torque script? Just as "sprite" The problem with that is it would have to be a dynamic name. I suppose I could just eval a string to set it up. Thus giving it a unique name.

@Brian; Can you explain that myNamedPlant? I dont quite understand the new ScriptObject( myNamedPlant : DefaultPlant)
#11
06/01/2005 (11:29 am)
You could do

%num++
new fxStaticSprite2D(sprite @ %num) { sceneGraph = t2dSceneGraph; };

or tag on anything you want...

that example fully works using the functions in a .cs file and using those as 4 seperate commands in the console (tested it even)...


if you did
$sprite = new fxStaticSprite2D(sprite) { sceneGraph = t2dSceneGraph; };

you could refer to it as $sprit or sprite, the good thi ng about setting its name in the () is that is the part thats saved, the variable doesnt get saved... though you could make that load function load each sprite into an array.
#12
06/01/2005 (3:01 pm)
The problem with this is I am currently thinking about the multiplayer. When I load this its going to read it as
//--- OBJECT WRITE BEGIN ---
new SimSet(gameData) {

   new fxStaticSprite2D(sprite) {
      scenegraph = "t2dSceneGraph";
         imageMap = "tileMapImageMap";
         position = "0 0";
         size = "5 5";
   };
};
//--- OBJECT WRITE END ---
Its going to create it as sprite. While i know its in gameData the problem is what if I want to load a character. Lets say the character "Joe Shmo" I would have to create the script object based off name. Then prevent other people from using that name. If I use a number identifier then I get something like this. Player creates and is named Player01, the old Player01 joins the game and then the newer player01 is rewrited to the old Player01 since its being loaded. The only thing to come over this would be to create a numbering system in which it relates by name. A player joins and creates himself. Once he saves it assigns a unique number to the name. This number will always be with that name. So if Player01 Joins then no one will be able to take the Player01 ever.

I am not sure if this would be the best way to do this though. Any ideas?
#13
06/01/2005 (5:19 pm)
Quote:
@Brian; Can you explain that myNamedPlant? I dont quite understand the new ScriptObject( myNamedPlant : DefaultPlant)

I was demonstrating how to use the 'create as copy' approach with an object that is accessed by name rather than by reference.

The example I gave creates a new script object that has the name 'myNamedPlant' and is a copy of 'DefaultPlant'. This new objects methods/attributes can be accessed using e.g. myNamedPlant.getId();

FYI I got this idea from the Torque Game Engine docs (see The TorqueScript Language, Chapter 5, Objects)

Hope this helps
#14
06/01/2005 (8:49 pm)
Yeah I was just looking at the doc after seeing this new scriptobject stuff I did not know about. Thanks.
#15
06/01/2005 (10:01 pm)
Christopher:

Quote:
The only thing to come over this would be to create a numbering system in which it relates by name. A player joins and creates himself. Once he saves it assigns a unique number to the name. This number will always be with that name. So if Player01 Joins then no one will be able to take the Player01 ever.

You just described a GUID or Globaly Unique IDentifier.

Basically you would need to decide on a number (number is used loosely here as you could use a GUID that contained alpha characters as well) format, this would be the length of the GUID as well as what each digit value for every digit position in the GUID can be. You then need to come up with an algorithm to generate your GUID's as well as keep a record of every GUID ever created, and a cross reference of GUID to owner.
#16
06/02/2005 (8:39 pm)
This is a very helpful thread!
#17
06/03/2005 (2:24 am)
@Matt: I like the idea posted: Jun 01, 2005 18:58

Unless I've missed something, I suspect that this would be slightly more complicated if the sprites were moving. I'm thinking that a routine will need to be called, prior to saving, to update the dynamic fields (e.g. position, velocities etc...) with their current values as obtained from the sprite. This would probably be quite similar in form to the loadData() function.

I'm guessing that this is not a problem in your GUI creator as it's probably only the starting state of the sprite that you're interested in. In which case it's a very cool solution!
#18
06/03/2005 (2:32 am)
Hmm your right Brian! In my GUI creator the initial state was all that mattered, didn't think about if it was moving, your right... easiest way to do that would be to add all the objects to a SimSet (if you did it properly I guess they'd already be added)... then you can loop through it and set the properties on saving... like

%count = gameData.getCount();

for(%i=0;%i<%count;%i++)
{
   %obj = gameData.getObject(%i);
   %obj.position = %obj.getPosition();
   %obj.size = %obj.getSize();
   ... etc ...
   %obj.linearVelocityX = %obj.getLinearVelocityX();
   ... etc ...
}

SimSets and ScriptObjects can really make some great combinations... in fact was using them this past week to set up a Torque 2D chat using 'turn based networking,' can get some pretty cool setups.
#19
06/03/2005 (3:45 am)
Yeah, that's what I was thinking (too lazy to post an example, guess that's why you're "King Tut" )

-----------------------------------------
WARNING: Untested code follows!!!
-----------------------------------------

As an alternative approach to the original idea (it doesn't solve the problem I just posed), based on some 'in progress' stuff of mine and "off the top of my head" thinking:

If you were to wrap a t2d sprite in a ScriptObject that held the position, imageMap etc..
and let's also give it a 'class', see where we get...

e.g.
new ScriptObject( mySprite )
{
	class = "Sprite";

	t2dSprite = 0;		// This will hold a reference to the sprite

	position = "0 0";
	imageMap = "tileMapImageMap";
};

You could now use the onAdd() script callback to initialise the sprite from the properties in the ScriptObject e.g.

function Sprite::onAdd( %this )
{
	// Create a new t2d sprite and set its properties
	%this.t2dSprite = new fxStaticSprite2d() { scenegraph = t2dScenegraph; };
	%this.t2dSprite.setPosition( %this.position );
	%this.t2dSprite.setImageMap( %this.imageMap );
};

If we now add mySprite to a SimSet and save...
e.g.
new SimSet( gameData );
gameData.add( mySprite );
gameData.save("T2D/gameData.cs");

Then the loading function should reduce to this:
function loadData(%file, %setName)
{
   %file = expandFileName(%file);
   if(exec(%file))
   {
      echo("set loaded!");
   } else
   {
      echo("invalid file name");
   }
}
...because the ::onAdd() will be invoked for each object when the file is 'exec-ed'.

I can't test this at the moment... but is there anything obvious that would kill this?

The potential I see here is that the initialisation is now class specific.

Comments anyone?

I'll try and test this tonight and report back...


EDIT: Removed the duplicated 'new' in the line creating the sprite.
Original line read:
t2dSprite = new new fxStaticSprite2d() { scenegraph = t2dScenegraph; };
Corrected access to dynamic fields in onAdd() by adding '%this.'

I've now tested this and, with the above corrections, it works!