Game Development Community

Character class methods

by Daniel Stirk · in Torque Game Builder · 07/01/2005 (7:30 pm) · 15 replies

I'm trying to find a way to create a "class," so to speak, which I can easily call functions from. However, I can't figure out how to successfully add methods to objects.

Here's what I'm trying to do....

Right now, I'm using an fxAnimatedSprite2D and using the playAnimation to play certain animations. However, when I have about 20 different animations per character, the code looks kinda messy.

What I'd like to do, is create an object of some kind that would be an fxAnimatedSprite2D and would have cusomt methods such as walkSouth and walkNorth. If it could be a class, datablock, or object, it could be named something like Character. Creating new characters, would be just like creating sprites.

That way, I can just call something like %characterOne.walkSouth() which would set the velocity of the character and automatically play the animation for me.

The problem I have is since I don't have TDE or anything else, I can't get teh script documentation on how to do something like this.

How would I create a Character class that would be and fxAnimatedSprite2D with custom functions?

#1
07/01/2005 (8:47 pm)
When creating a scriptobject be sure to assign it a classname. Here's an early version of the code I used to set up my player class
$player =  new scriptobject( )		// Create a new object of the class playersprite
	{
		class = playersprite;
		state = 5;
		forceX = 0;
		forceY = 0;
	};

You can then define new functions for the class using

function myClassName::myFunction()
{
}

You can also put your sprite inside said scriptobject.

Or at least that's how I've done it for my little unfinished project.
#2
07/01/2005 (8:55 pm)
So, make myself a new scriptobject and make it hold a sprite? That would work. Thanks.

Do script objects have a constructor method of any kind that will automatically run when I create it? That would let me have it automatically set itself up without the need for me t opass it stuff in a separate function.

One more question, if I declare a variable like $CharacterSprite within this sprite object, will it be only accessable through this object? I would need to create a local variable inside this object, but I think $ is global, and % is local to that function. Any ideas?
#3
07/01/2005 (9:14 pm)
@Daniel

Scriptobjects have 'onAdd' and 'onRemove' methods you can implement that will get called whenever the object is instantiated. Example on a scriptobject i have named 'Bubble' :

function Bubble::onAdd(%this) {
        %this.classname = "Bubble";       
}


For my implementation, i use the same approach to tying a scriptobject to a sprite, but i actually assign an attribute named 'class' to the sprite which holds the handle/reference to the scriptobject.

For example:

function Bubble::createBubble(%this) {
        %bubble = new fxStaticSprite2D() { scenegraph = t2dSceneGraph; };
        %bubble.setSize(  ($globals.bubbleSize / 40) SPC ($globals.bubbleSize / 40) );
        %bubble.setPosition( $globals.brushOne.getPosition()   );
        
        %imgmap = %this.getImageMap();
        
        %bubble.setImageMap( %imgmap );
        %bubble.setWorldLimit( "BOUNCE", $globals.bubbleWorldLimits );
        %bubble.class = %this;
        %this.parent = %bubble;
}

Then, whenever an onCollision or other callback event occurs, i can quickly get a handle to the tied scriptobject by doing:

function fxSceneObject2D::onCollision( %srcObj, %dstObj, %srcRef..........){         

        %srcScriptObject = %srcObj.class;  // easy access to parent scriptobject


        // scriptobject callback extension:
        if (isObject(%srcObj.class) ) {
                %srcObj.class.onCollision(%dstObj, %srcRef................);
        }     
}


function Bubble::onCollision (%this, %dstObj, %srcRef..................) {
         // check if colliding object is bubble
        if (isObject(%dstObj.class) ) {
                if (%dstObj.class.classname $= "Bubble" && %dstObj.class.lockedInPosition == false) {
                                   ......................................
                }
        }                
}
#4
07/01/2005 (9:34 pm)
Hmmm... I should probably go with OO code, or add this class into the source. If I make i a scriptobject, getting information to and from teh sprite is gonna be a pain.

Only thing that would save me here would be if I could somehow inherit the attributes of a sprite and add my own functions to that. Is the source commonly changed amoung programmers in teh T2D engine?
#5
07/01/2005 (9:47 pm)
It's not 'too' much of a pain once you get used to it. You can also have 3 levels (IIRC) of inheritance in scriptobjects. So, you could have something like character->humanCharacter->etc......

You can also extend a t2d class like fxAnimatedSprite2D by adding additional functions via torquescript. For a good example on this, check Matt's implementation of the 'moveTo' method here:
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7576

Since i don't know your exact dilemma, i can't be much more specific.
#6
07/01/2005 (10:04 pm)
Okay, so I can still add functionality to a sprite. That helps a bit.

I was basically trying to make a new class that would inherit from fxAnimatedSprite2D so that I could have methods that would play animations and move the character in a certain direction. I'm trying to do an RPG, so lots of characters and anmations.

If I could make a scriptobject or class that was a sprite, it would save me time in sending informaiton to and from the sprite, such as collision data and everything. Unless, of course, that script objects can have collision.
#7
07/01/2005 (10:36 pm)
@Daniel

If you check my code above, you can see how i was able to transform fxSceneObject2D's 'onCollision' into a call
on my scriptobject, Bubble::onCollision. This is by using 'reference' attributes on my scriptobject (can be easily set in the onAdd method) so that onCollision can use a generic introspection to determine what type of class it is dealing with and calling its respective callback. Using this methodology, you can implement any callback logic within the scriptobject. In this regard it is close to inheriting from the base t2d c++ object.

One solution i see is that you could define a 'character' scriptobject which has methods such as walkSouth(), etc..... However, in these methods, specifics in regards to actual animation handles, etc can be offloaded to the child class.

Example pseudocode.....with Character being the parent class of OrcChar

function Character::walkSouth(%this) {

        // generic setup code
        // ......
        // end generic setup code

        %animation = %this.getAnimation();   

}

function OrcChar::getAnimation(%this) {
         // orc specific setup goes here

}


Crude example, but i think it can be close to what you're looking for.
#8
07/02/2005 (4:54 am)
Okay, yeah, that looks good. I'll just have to be carefuill and keep track of my sprites and who controls them. I'll have to look farther into how collision works to see if there would be any other problems I might run into. THanks guys, you've been a help. I'll try this tonight.
#9
07/02/2005 (7:24 am)
After you work with it for awhile, it becomes second nature. It seems a little kludgy at first, but it can get the job done.
#10
07/03/2005 (11:05 am)
What's the difference between:
function Bubble::onAdd(%this) {        %this.classname = "Bubble";       }
this?
$testObj = new scriptObject (){
       class = Bubble;
};
and this?
$testObj = new scriptObject ("Bubble");
I know that they are different, but why? I'm getting confused with the distinction and purpose between "class" field, "classname" and the "object name"

What I *thought* was that "class" was for inheritence, "name" was for namespace function assignment (to get something like class methods), but now what's "classname" for?
#11
07/03/2005 (1:18 pm)
Especially, now that I try it...
echo($aBubbleIMade.getClassName() );
will report "ScriptObject"

I guess maybe the "official" className property is internal to the C++ class, and above you were using "%this.className" as a dynamic field? That has nothing to do with the "real" classname?
#12
07/03/2005 (2:23 pm)
@Phil....

Exactly. The reason i use 'classname' is to allow a torquescript accessible property of the classname for reference. Then, i can do a %object.classname and easily tell what class i'm dealing with. I code this assignment in the 'onAdd' callback method so that each object of that type will be assigned the attribute.

You can also override the c++ getClassName function and add your own function like:

function Bubble::getClassName(%this) {
     return "Bubble";
}


Then, the call will return the desired output. I like my method a bit more since it is quicker to implement. YMMV.



As to the initializer questions.......

$testObj = new scriptObject () {       
class = Bubble;
};

$testObj = new scriptObject ("Bubble");

The difference between these 2 initializers is that the 1st option doesn't define a default namespace in the parentheses. It does define a class attribute so the object will look in that namespace for all function calls. The 2nd initializer takes advantage of the default namespace and uses it.

Torquescript limits you to 3 namespaces for inheritance. The 1st option is essentially 'wasting' a level. This may or may not be an issue depending if you are even implementing inheritance.

The code block i posted in this thread.....
www.garagegames.com/mg/forums/result.thread.php?qt=31715
.........is a complete model and explanation of using all 3 namespaces of inheritance.
#13
07/03/2005 (4:16 pm)
Ok, thanks Ty. I think it makes sense now.

So, just to clarify, your use of classname is essentially creating a new field in the scriptObject, then you're overriding the default getClassName() C++ function to return that field.

But, there's nothing inherently special about this new classname field is there? It could just as easily be called "puddingName"?

I just want to be sure, so I don't get confused.
#14
07/03/2005 (9:11 pm)
@Phil

Yes, the name is arbitrary. You can make it whatever you'd like. As for overriding the default getClassName() method, that is an optional solution to the problem and not essential to the classname field.
#15
07/03/2005 (9:47 pm)
I found this post pretty useful when starting out.