Game Development Community

ScriptObject Instancing

by Drew -Gaiiden- Sikora · in Torque Game Builder · 07/28/2005 (1:41 am) · 22 replies

I just wanted to know if this was the best way to do this. It took quite a bit of effort to come up with this, I still haven't found the difinitive resource for ScriptObjects, I've just managed to accumulate knowledge from various posts around these forums.
new ScriptObject(Player)
{
   score = 0;
   hasSpecialBlock = false;
   isLoaded = false;
   ...
};

$playerOne = new ScriptObject() 
{
   type = $PLAYER_ONE;
   class = Player;
};

$playerTwo = new ScriptObject()
{
   type = $PLAYER_TWO;
   class = Player;
};

function Player::loadPlayer(%this)
{
   // stuff
}
Basically what I've done is the equivilant of (in C++)
class Player
{
   public:
      int score = 0;
      bool hasSpecialBlock = false;
      bool is Loaded = false;

      void loadPlayer();
};

Player* playerOne = new Player;
Player* playerTwo = new Player;
This is the only way I could think of doing it, though if there is a better way I'd like to know. If not then hopefully this'll be of help to others as lost as I was for a time :P
Page «Previous 1 2
#1
07/28/2005 (4:23 am)
As written, the script version is not quite the same as the c++ version because playerOne & playerTwo won't have any of the dynamic fields in the Player object. Given that the example values are all(effectively) zero this may work but if you want to have a variable/field, say: lives = 3; then this value probably won't be correct.

TorqueScript does provide a way round this though. You can create an object as a copy of a named object. So, with your example and adding the lives=3 you could do this

new ScriptObject(Player)
{
   score = 0;
   hasSpecialBlock = false;
   isLoaded = false;
   lives = 3;
};

$playerOne = new ScriptObject( :Player ) 
{
   type = $PLAYER_ONE;
   class = Player;
};

$playerTwo = new ScriptObject( :Player )
{
   type = $PLAYER_TWO;
   class = Player;
};

function Player::loadPlayer(%this)
{
	echo( "Score:" SPC %this.score );
	echo( "Lives:" SPC %this.lives );
}

// Demo the load player function
echo( "Demo script object copying..." );

$playerOne.loadPlayer();

Alternatively you could include the 'class = Player;' declaration in the class object but in this case you would have to rename it e.g.

new ScriptObject(PlayerClass)
{
   class = Player;

   score = 0;
   hasSpecialBlock = false;
   isLoaded = false;
   lives = 3;
};

$playerOne = new ScriptObject( :PlayerClass ) 
{
   type = $PLAYER_ONE;
};

function Player::loadPlayer(%this)
{
  echo( "Score:" SPC %this.score );
  echo( "Lives:" SPC %this.lives );
  echo( "Class:" SPC %this.class );
}

// Demo the load player function
echo( "Demo script object copying..." );

$playerOne.loadPlayer();

I've been using the latter approach on my project (YMMV) but you have to be wary If you are using the ::onAdd() callback. Where it is defined determines whether the 'class' object will call it or not.

As with a lot of programming there's often more than one 'right' way to do things...
#2
07/28/2005 (5:35 pm)
Thx for pointing that out - I hadn't realized that I couldn't access any of the member data. However I noticed that if I dump() the $playerOne object that the type field doesn't show up at all. I tried changing it to kind but that doesn't seem to work either. How come that's not working?
#3
07/28/2005 (7:06 pm)
Are you sure the globals $PLAYER_ONE, $PLAYER_TWO are defined?
#4
07/30/2005 (1:32 am)
Yea they were defined, however I realized that I missed a warning in the console saying that the cs file they were defined in was missing - I mispelled it :P I fixed that and now everything is fine - can't wait for that new console in the next release of T2D that has a drop down list of all errors and warnings.

thx guys
#5
07/30/2005 (7:26 pm)
K, new problem along the same vein.

I have in blocks.cs
new ScriptObject(Block)
{
   charge = $ACTIVE_CHARGE;
   blockSprite = new fxStaticSprite2D() { scenegraph = bbSceneGraph; };
};

and in board.cs I want to do
switch (getRandom(0, 2))
   {
      case $ATTACK_BLOCK_TYPE:

         $block = new ScriptObject(AttackBlock:Block) { class = Block; };

      case $DEFENSE_BLOCK_TYPE:

         $block = new ScriptObject(DefenseBlock:Block) { class = Block; };

      case $NEUTRAL_BLOCK_TYPE:

         $block = new ScriptObject(NeutralBlock:Block) { class = Block; };        
   }

however I get warnings saying that the parent class Block couldn't be found. I guess it may be because I have the Block scriptobject defined in another cs file, but I hope that's not the case cause I'd like to keep it there.

another question, semi-related - I don't want the $block to be global, %block would work fine but then it would go out of scope when the case statement ends. How can I pre-define %block so that it retains its scope outside the switch statement?
#6
07/31/2005 (12:27 am)
Quote:
I get warnings saying that the parent class Block couldn't be found. I guess it may be because I have the Block scriptobject defined in another cs file

This shouldn't be a problem as long as "blocks.cs" is exec-ed before "board.cs". I use objects defined in other files and with careful ordering of the files don't see this problem.

Quote:
I don't want the $block to be global, %block would work fine but then it would go out of scope when the case statement ends
AFAIK TorqueScript scoping rules are more like C than C++ so you can use %block and it will retain it's value outside of the switch statement and within the function in which it is defined. This test seems to demonstrate this:
function scopeTest( %index )
{
	switch( %index )
	{
		case 0:	%test = 10;
		case 1:	%test = 20;
		case 2:	%test = 30;
		default:	%test = 100;
	}
	echo( "Test in function:" SPC %test );
}

scopeTest( 1 );
echo( "Test outside function:" SPC %test );
#7
08/01/2005 (10:39 am)
This seems like a good thread for this question. I want to create a general function to create copies of my Scriptobjects. So far this is what I have:
%effect_name = %effect.name;
		echo("Effect Object Name:" @ %effect_name);
		eval("%temp_effect = new ScriptObject(dfsfd :" @ %effect_name @ ");");
In the above code %effect is a ScriptObject created like this:
$HP_Drain_Effect = new ScriptObject (HP_Drain_Effect : defaultEffect){
   class="effect";
   game_name="HP Drain";
};
I know that in order to copy it I would have to type:
%temp_effect = new ScriptObject(some_name : HP_Drain_Effect);
But the problem is that I need a function that can figure out the original ScriptObject's name and copy it.
That is why I tried the code at the top. But this does not seem to work.
Any ideas?
#8
08/01/2005 (11:37 am)
What you really need is the ScriptObject's ID, not it's name. When you type HP_Drain_Effect for instance, it is being resolved to the ID of HP_Drain_Effect. So, you should be able to do something like this:
// Grab the effect's ID
%effect = HP_Drain_Effect;
// Create the new object, inheriting from HP_Drain_Effect
%temp_effect = new ScriptObject(some_name : %effect);
Of course, that in itself would be pointless, but you could assign %effect to any script object that had already been created or even have a function return a ScriptObject ID, or pass an ID to your function as a paremeter.

And, despite this thread being about ScriptObjects, you are asking a new question, so you really should have just started a new thread.
#9
08/01/2005 (2:46 pm)
@Adam:
Actually you really do need to use the name of the object not its id. I tried both global and local variables and they result in syntax errors. Hence Travis' use of the eval().

@Travis:
Close... try this:
eval("%temp_effect = new ScriptObject(dfsfd :" @ %effect.getName() @ ");");
#10
08/01/2005 (3:11 pm)
Fyi, i encountered a bug (and reported it) where the use of "%variable" is NOT a good way to compare equality.

always, ALWAYS use %variable.getName() or %variable.getId(). This is because under certain conditions comparing the %variable itself will be TRUE, and other times FALSE.

i have seen this issue with the OnCollision callback in the basic tutorial.
#11
08/01/2005 (4:05 pm)
@Jason:
Any idea under what conditions this happens or perhaps example code of this happening?

Fortunately, a quick search through my code shows I'm only testing equality of objects in 2 places and in both I'm using getId(); (Phew!)
#12
08/01/2005 (4:45 pm)
@Brian: i should have elaborated.

Create1
$player = new SimObject(newPlayer);
Create2
new SimObject(newPlayer);
$player = newPlayer;

You would think that Create1 is the same as Create2, but it's not. For example, if you do CreateX and then check the object in OnCollision, you will run into a problem:

OnCollision
if(%srcObj==$player)
    ........

If you do Create1, things work as expected. If you do Create2, then that IF statement will always evaluate to FALSE.

So the way to "fix" this is to change the OnCollision IF to:
if(%srcObj.getId()==$player.getId())
    .....

That evaluates properly.
#13
08/01/2005 (5:39 pm)
Ahhh. I see what you mean but I would say that this is actually the assignment that is causing the effect you're seeing.
$player = newPlayer;

$player ends up as the string "newPlayer". There was a similar problem in an earlier post by Adam
// Grab the effect's ID
%effect = HP_Drain_Effect;

Hopefully someone with more knowledge of the inner workings of TorqueScript can enlighten us but it
seems to prefer strings internally. So you can do this "newPlayer".getId(); and it'll work.

Incidentally, FYI the 2 places where I used .getId() in equality tests were for object names acquired from gui controls so I've obviously had a similar problem!
#14
08/03/2005 (1:49 am)
I'm hijacking my thread back! ;)

So anyways Brian I checked out the order of my exec calls and I was calling the blocks.cs file first, so that wasn't the problem. I tried moving the Block definition to the board.cs file with the instances and that didn't work either. Then I thought maybe cause it wasn't in the function with the instance calls so I moved it there and that didn't work either. This was really pissing me off since I'm doing the exact same thing elsewhere in my game and it works perfectly (the Player stuff at the start of this thread). Finally I set it up exactly like I'm doing the Player and it reported that it couldn't find the Block parent at line 0 of the file, which led me to examine the parent Block definition.

It turns out that something in this call is screwing everything up
new ScriptObject(Block)
{
   charge = $ACTIVE_CHARGE;
   blockSprite = new fxStaticSprite2D() { scenegraph = bbSceneGraph; };  <--- BAD!!!???
};
if I comment out the creation of the sprite object then everything works perfectly again!

Anyone have any idea why this is?

Also, a better explaination of my scope problem. Here's more source
function loadClip(%player)
{
   // create the new block sprite
   switch (getRandom(0, 2))
   {
      case $ATTACK_BLOCK_TYPE:

         $block = new ScriptObject(AttackBlock:Block) { class = Block; };

      case $DEFENSE_BLOCK_TYPE:

         $block = new ScriptObject(DefenseBlock:Block) { class = Block; };

      case $NEUTRAL_BLOCK_TYPE:

         $block = new ScriptObject(NeutralBlock:Block) { class = Block; };        
   }
   $block.type = %blockType;

   ....
}
My problem is that I want $block to not be global but how/where do I define the variable that will later store the ScriptObject? I mean I don't know how to define a variable in TS without assigning it a value at definition.
#15
08/03/2005 (4:25 am)
To be honest, I don't know exactly why but, I have met this problem before.
Defining a new object inside an object definition only works for set/group types and then it's not possible to assign the id of the inner object to a field. The inner object just gets added to the outer set/group. GUI objects being a good example of this. I suspect that the problem is something to do with this. It's a pity it fails silently and only reports errors when the object is used!

Anyway, to get around your problem use the onAdd() callback e.g.
function Block::onAdd( %this )
{
	%this.blockSprite = new fxStaticSprite2D() { sceneGraph = bbSceneGraph; };
}

The 'onAdd()' callback is called whenever a ScriptObject (or ScriptGroup) is first created. There is also a companion callback 'onRemove()' called when the object is (about to be) deleted.

As for the scope problem, I'm still not clear on what it is you're trying to achieve. :-(
#16
08/03/2005 (8:24 am)
Brian is right... you cannot create a new object in the definition of a new object. Another way to get around it is this

new ScriptObject(Block){   
charge = $ACTIVE_CHARGE;     
};
Block.blockSprite = new fxStaticSprite2D() 
{ scenegraph = bbSceneGraph; };
#17
08/03/2005 (9:30 am)
Hmm, Matt has a point here...I may have missed the original intention :-(

My suggested solution would create unique blockSprite instances for each new Block that is created,
whereas Matt's version creates a single instance that will be shared between all Blocks.

...but only Drew knows which of these possible outcomes is the intended one ;-)
#18
08/03/2005 (9:38 am)
Very true, if you are trying to do seperate instances for each creation then Brian's method is much better (in this case it seems that might be the case)... if you are just doing a one shot thing then adding it to the object after you create it might be a good idea.
#19
08/03/2005 (12:03 pm)
Thx guys, and yea Brian got it right :) Tho I'll keep Matt's method in mind for later use if I need it.

Okay, the scope question :) If you look at the code I can't define the %block variable until I assign it a value, which happens within the case statement, which means the variable immediately goes out of scope at the end of the case. What I need is a way to define the local variable outside the switch block so that it retains its scope in the function but I don't know how to do this since AFAIK in TS you can't create a variable without assigning it a value. Therefore how to I create the variable outside the switch block so that I can assign it the value inside the case and not have it go out of scope?
#20
08/03/2005 (1:00 pm)
I think you missed the (2nd half of the ) post - Posted: Jul 31, 2005 08:27 ;-)

TS local variables only have function scope. There is no block scope in TS (AFAIK).
Any local variable created within a function is available in any following code until the end of that function irrespective of whether it is created within a code block or not.
Page «Previous 1 2