iTickable ScriptObject for 1.4
by Josh Moore · 01/19/2006 (10:35 pm) · 17 comments
I made this handy little ScriptObject varient the other day and thought I'd share it with everyone else. This thing is seriously handy and can be used for all kinds of stuff(like..umm.. maybe an AI brain object with dynamic think ticking). The version posted here supports turning ticking on/off and changing the tick rate.
Here's the class:
I added it to console/simObject.cc
How to use it:
Here's the class:
I added it to console/simObject.cc
#include "core/iTickable.h"
// TickableScriptObject - JM
class TickableScriptObject : public SimObject, public virtual ITickable
{
typedef SimObject Parent;
StringTableEntry mClassName;
StringTableEntry mSuperClassName;
S32 mTickRate;
U32 mCurrTick;
protected:
virtual void interpolateTick( F32 delta ) {};
virtual void processTick();
virtual void advanceTime( F32 timeDelta ) {};
public:
TickableScriptObject();
!TickableScriptObject();
bool onAdd();
void onRemove();
void setTickRate(S32 newRate);
DECLARE_CONOBJECT(TickableScriptObject);
static void initPersistFields();
};
IMPLEMENT_CONOBJECT(TickableScriptObject);
void TickableScriptObject::initPersistFields()
{
addGroup("Classes", "Script objects have the ability to inherit and have class information.");
addField("class", TypeString, Offset(mClassName, TickableScriptObject), "Class of object.");
// Don't expose to script ?
addField("superClass", TypeString, Offset(mSuperClassName, TickableScriptObject), "Superclass of object.");
endGroup("Classes");
addGroup("iTick");
addField("tickRate", TypeS32, Offset(mTickRate, TickableScriptObject), "Tick rate of the Script Object.");
endGroup("iTick");
}
TickableScriptObject::TickableScriptObject()
{
mClassName = "";
mSuperClassName = "";
mTickRate = 2000;
mCurrTick = 0;
}
TickableScriptObject::~TickableScriptObject()
{
}
bool TickableScriptObject::onAdd()
{
if (!Parent::onAdd())
return false;
// it's possible that all the namespace links can fail, if
// multiple objects are named the same thing with different script
// hierarchies.
// linkNamespaces will now return false and echo an error message
// rather than asserting.
// superClassName -> TickableScriptObject
StringTableEntry parent = StringTable->insert("TickableScriptObject");
if(mSuperClassName[0])
{
if(Con::linkNamespaces(parent, mSuperClassName))
parent = mSuperClassName;
}
// className -> superClassName
if (mClassName[0])
{
if(Con::linkNamespaces(parent, mClassName))
parent = mClassName;
}
// objectName -> className
StringTableEntry objectName = getName();
if (objectName && objectName[0])
{
if(Con::linkNamespaces(parent, objectName))
parent = objectName;
}
// Store our namespace
mNameSpace = Con::lookupNamespace(parent);
// Tick by default
setProcessTicks(true);
// Call onAdd in script!
Con::executef(this, 2, "onAdd", Con::getIntArg(getId()));
return true;
}
void TickableScriptObject::onRemove()
{
// Call onRemove in script!
Con::executef(this, 2, "onRemove", Con::getIntArg(getId()));
Parent::onRemove();
}
void TickableScriptObject::processTick()
{
// Script callback
if(mCurrTick >= mTickRate) {
Con::executef(this, 1, "onTick");
mCurrTick = 0;
}
else
mCurrTick += 32;
}
void TickableScriptObject::setTickRate(S32 newRate)
{
mTickRate = newRate;
}
ConsoleMethod(TickableScriptObject, setTick, void, 3, 3, "bool tick")
{
object->setProcessTicks(dAtob(argv[2]));
}
ConsoleMethod(TickableScriptObject, setTickRate, void, 3, 3, "S32 tick rate")
{
object->setTickRate(dAtoi(argv[2]));
}
//---How to use it:
new TickableScriptObject(iTick) {
tickRate = 1000; // You can set the tick rate on creation or via the console methods
};
iTick.setTick(true);// Note: the object ticks by default
iTick.setTickRate(5000);// In milliseconds
function iTick::onTick(%this)
{
// Do cool stuff...
}About the author
#3
01/23/2006 (9:15 pm)
Any chance of getting a lower level description of what the heck this is for so the slow ones in back *points at himself* can understand it?
#4
You set up the time period between ticks, and then put stuff in the OnTick, so each time it ticks, torque does something. Of course you're not limited to just one clock either.
You can pretty much use it where schedule() gets goofy and clunky - like AI where you want the bot to stop and think about what he's doing every so many seconds.
Or you could use it to spiff up, well... a clock like http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=9502
01/24/2006 (3:36 pm)
Basically its a clock, that goes tick, tick, tick... You set up the time period between ticks, and then put stuff in the OnTick, so each time it ticks, torque does something. Of course you're not limited to just one clock either.
You can pretty much use it where schedule() gets goofy and clunky - like AI where you want the bot to stop and think about what he's doing every so many seconds.
Or you could use it to spiff up, well... a clock like http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=9502
#5
the iTickable thing looks pretty straightforward.
01/26/2006 (12:00 pm)
Any recommendations for bringing this back into TGE 1.3 ?the iTickable thing looks pretty straightforward.
#6
01/28/2006 (5:12 am)
Nice little addition, not thrown it in yet, but already I'm coming up with multiple uses. Another brilliant feature for us all.
#7
need to be ticked constanly then dont.
could be really handy for things like tick objects at a higher rate when they are closer to you, and at a slower rate if they are further away.
also good for ai, if an ai doesnt need to make a decision every few milliseconds then delay it for as long as possible, maybe some decisions only need to be made every few seconds or once a minute.
cuts down on processing time, especially if you have a lot of thinking objects.
nice work.
02/02/2006 (12:17 pm)
classic resource for optimizations, the fastest code is the code you dont run. if something doesntneed to be ticked constanly then dont.
could be really handy for things like tick objects at a higher rate when they are closer to you, and at a slower rate if they are further away.
also good for ai, if an ai doesnt need to make a decision every few milliseconds then delay it for as long as possible, maybe some decisions only need to be made every few seconds or once a minute.
cuts down on processing time, especially if you have a lot of thinking objects.
nice work.
#8
02/09/2006 (7:07 pm)
That is a neat idea, but you left out a whole lot.
#9
06/24/2006 (11:45 am)
nice!! very handy!!
#10
08/02/2006 (6:54 am)
Where do I find this simObject.cc file to put this code on? It isn't in the "console" directory, and isn't in the whole "engine" directory as well (as I searched for)... and I'm very impressed about it, because I've just downloaded the TGE 1.4 installation last week and doesn't changed anything up to now... really strange.
#11
08/02/2006 (8:52 am)
@fabio - you're right, i don't see a simObject.cc either. Josh probably meant engine/console/simBase.cc. that's where simObject is implemented.
#12
Does anyone knows what is that? Or where to put this code? Sorry for those stupid questions.
08/02/2006 (9:23 am)
@Orion - Thanks for the comment, but where in the file to put the code? (note that I'm no programmer) I've put at the end of the code, and I've got those errors:C:\GDev\TGE\engine\console\simBase.cc(1762) : error C2059: syntax error : '!' C:\GDev\TGE\engine\console\simBase.cc(1762) : error C2238: unexpected token(s) preceding ';' C:\GDev\TGE\engine\console\simBase.cc(1797) : error C2084: function '__thiscall TickableScriptObject::~TickableScriptObject(void)' already has a body
Does anyone knows what is that? Or where to put this code? Sorry for those stupid questions.
#13
08/02/2006 (9:58 am)
Hey Fabio - some things in life just need a programmer. it sounds like a simple syntax error, but it could be anything.
#14
just an update for those like Fabio who had trouble putting this in:
it goes in console/scriptObject.cc NOT simObject or simBase
put it at the bottom, but put the #include "core/iTickable.h" at the top
also, in the class definition, there is a typo on the destructor: !TickableScriptObject(); should read ~TickableScriptObject();
09/24/2007 (12:55 pm)
great resource, went into 1.5.2 just finejust an update for those like Fabio who had trouble putting this in:
it goes in console/scriptObject.cc NOT simObject or simBase
put it at the bottom, but put the #include "core/iTickable.h" at the top
also, in the class definition, there is a typo on the destructor: !TickableScriptObject(); should read ~TickableScriptObject();
#15
09/27/2007 (8:47 pm)
This looks extremely useful and perhaps something to help an issue I've been struggling with. However, I'm still fairly new to Torque and the formatting and use of its scripting. I'm trying to get this to work with the player, so I dropped the code above in the "How to use it" section into my player.cs file and used and echo command in the onTick "do cool stuff" function to see if it was in fact ticking... and I got nothing. Now, my spidey sense has been telling me from the start that it takes a little more something to get it working then what's listed above, I just don't know what. Does it need to be called within the player datablock? Within the onAdd function? Can anyone show me an example of this code being used in a within the script? Thanks!
#16
instead you could do:
notice, also that the AIManager.think() is removed... this is because the "think" function of AIManager is just a function that re-schedules itself to keep running every 500ms (tickrate). The function for AIManager.think is located in AIPlayer.cs and looks like this:
instead, now that it's a tickable script object you would replace that with something like this:
you would remove the need to reschedule the "think" function because the object is already ticking (if you wanted it to be exactly the same change the tick rate of the tickable script object)
also, you could stick additional things in there, like the echo statement, and because the object has been instantiated in the startGame, it should do those things every tick
09/28/2007 (8:45 am)
Well, it's not a player object, it's a separate script object. The player (because it's derived from GameBase) is already "ticking", although I don't think there is an onTick() callback setup for the player. You could for instance use it to replace the AIManager. In game.cs (stock TGE 1.5.2) for instance, during the startGame() function is where the AIManager is created: // Start the AIManager
new ScriptObject(AIManager) {};
MissionCleanup.add(AIManager);
AIManager.think();instead you could do:
new TickableScriptObject(AIManager) {};
MissionCleanup.add(AIManager);notice, also that the AIManager.think() is removed... this is because the "think" function of AIManager is just a function that re-schedules itself to keep running every 500ms (tickrate). The function for AIManager.think is located in AIPlayer.cs and looks like this:
function AIManager::think(%this)
{
// We could hook into the player's onDestroyed state instead of
// having to "think", but thinking allows us to consider other
// things...
if (!isObject(%this.player))
%this.player = %this.spawn();
%this.schedule(500,think);
}instead, now that it's a tickable script object you would replace that with something like this:
function AIManager::onTick(%this)
{
if (!isObject(%this.player))
%this.player = %this.spawn();
}you would remove the need to reschedule the "think" function because the object is already ticking (if you wanted it to be exactly the same change the tick rate of the tickable script object)
also, you could stick additional things in there, like the echo statement, and because the object has been instantiated in the startGame, it should do those things every tick
#17
I was interested in accessing this function with the player object because I have an issue with which I'm trying to time out the player's "walk" cycle. Technically, my player bounces and so I've been looking to develop some sort of counter that would not stop the player's movement until the animation cycle has completed, even though the key has been let up. I've started the following thread with my question about that: http://www.garagegames.com/mg/forums/result.thread.php?qt=67542
Greg
09/28/2007 (6:05 pm)
Thanks, Ben. That information was very useful and helps my understanding of Torque Script a little better.I was interested in accessing this function with the player object because I have an issue with which I'm trying to time out the player's "walk" cycle. Technically, my player bounces and so I've been looking to develop some sort of counter that would not stop the player's movement until the animation cycle has completed, even though the key has been let up. I've started the following thread with my question about that: http://www.garagegames.com/mg/forums/result.thread.php?qt=67542
Greg

Torque Owner Stefan Lundmark
Thanks alot!