Using onUpdate or timers for behaviors
by Chris Beals · in Torque 2D Beginner · 05/14/2015 (2:58 pm) · 5 replies
Hello,
I have been scouring the forums for information trying to wrap my head around using T2D. I now am having the conundrum of building behaviors efficient and flexible. On my journey I have seen a good amount of behaviors for different enemy patterns and actions. Currently I am focusing on movement behaviors and combining them dynamically.
The question I have is whether it is common practice to use the onUpdate callback function of a behavior to actually do the calculations and movement? I have seen those methods, but it leads to the callback (and thus the code and movement associated with it) being called quite often.
As a result of trying to combine them, the onUpdate seems to have movements fighting and ending up very jagged.
For example if you have a chasing movement behavior and a sidestep(or zigzag)
When these are combined the result is a shaky movement. I am building these to be generic, but I want them to be able to still combine correctly of course. At first I thought adding a delay by using a schedule would help, but I tried and it wound up just about the same. Maybe I misused the schedule because I tried to combine it in the onUpdate callback though. I saw in this thread that a timer was used instead of onUpdate.
I was hoping to get some advice/insight for help because I am stumped even after combing into the source and forums.
I have been scouring the forums for information trying to wrap my head around using T2D. I now am having the conundrum of building behaviors efficient and flexible. On my journey I have seen a good amount of behaviors for different enemy patterns and actions. Currently I am focusing on movement behaviors and combining them dynamically.
The question I have is whether it is common practice to use the onUpdate callback function of a behavior to actually do the calculations and movement? I have seen those methods, but it leads to the callback (and thus the code and movement associated with it) being called quite often.
As a result of trying to combine them, the onUpdate seems to have movements fighting and ending up very jagged.
For example if you have a chasing movement behavior and a sidestep(or zigzag)
function ChaseBehavior::onUpdate(%this)
{
if (!isObject(%this.target))
return;
%targetRotation = Vector2AngleToPoint (%this.owner.getPosition(), %this.target.getPosition());
%xPercent = mCos(%targetRotation);
%yPercent = mSin(%targetRotation);
%this.owner.setLinearVelocityX(%xPercent * %this.owner.walkspeed);
%this.owner.setLinearVelocityY(%yPercent * %this.owner.walkspeed);
}function ZigZagBehavior::onUpdate(%this)
{
if (!isObject(%this.target))
return;
//if(!%this.zigZagCooledDown)
// return;
%targetRotation = Vector2AngleToPoint (%this.owner.getPosition(), %this.target.getPosition());
%leftRight = getRandom(10);
switch(%leftRight)
{
case 0:
%targetRotation += 90;
case 1:
%targetRotation -= 90;
default:
echo("default");
return;
}
%xPercent = mCos(%targetRotation);
%yPercent = mSin(%targetRotation);
%this.owner.setLinearVelocityX(%xPercent * %this.owner.walkspeed * 10);
%this.owner.setLinearVelocityY(%yPercent * %this.owner.walkspeed * 10);
}When these are combined the result is a shaky movement. I am building these to be generic, but I want them to be able to still combine correctly of course. At first I thought adding a delay by using a schedule would help, but I tried and it wound up just about the same. Maybe I misused the schedule because I tried to combine it in the onUpdate callback though. I saw in this thread that a timer was used instead of onUpdate.
I was hoping to get some advice/insight for help because I am stumped even after combing into the source and forums.
About the author
Recent CS graduate trying to get a foothold into the videogame industry. I currently am trying to implement interesting applications for a novel AI.
#2
Why exactly would having many onUpdates be bad? Are behaviors considered objects, as the createInstance would seem to show?
I read through the suggested repository on your github and the aiClientManager very easily implements what you were mentioning. Very nicely documented and clear, so thank you very much for that! You use actual players but I guess I could switch those for behaviors.
Since posting this, I did quickly put to use the behavior input/output functionality to test. I made it so that one behavior could output pause, do it's action, then output unpause. This would most likely expand into any behavior that needed to be focused would send a pause/unpause to all others on that enemy. It seems to work in theory, but I do worry about when it is expanded to more behaviors. Does this Output/Input system have a slow down quickly when overpopulated?
I definitely will spend some time looking through the repository now though. The script tick does throw me a little though. I see in the repository, you used just schedule() so what is the script tick for?
05/15/2015 (12:07 pm)
Richard, so the idea would be to schedule a self-defined update for the group then. Why exactly would having many onUpdates be bad? Are behaviors considered objects, as the createInstance would seem to show?
I read through the suggested repository on your github and the aiClientManager very easily implements what you were mentioning. Very nicely documented and clear, so thank you very much for that! You use actual players but I guess I could switch those for behaviors.
Since posting this, I did quickly put to use the behavior input/output functionality to test. I made it so that one behavior could output pause, do it's action, then output unpause. This would most likely expand into any behavior that needed to be focused would send a pause/unpause to all others on that enemy. It seems to work in theory, but I do worry about when it is expanded to more behaviors. Does this Output/Input system have a slow down quickly when overpopulated?
I definitely will spend some time looking through the repository now though. The script tick does throw me a little though. I see in the repository, you used just schedule() so what is the script tick for?
#3
Just drop about 200 of 'em in there and check it out. If that runs fine, add more.
And from that repo - don't do it for behavior instances, do it for the object "hosting" the behavior instances (the instance owner). That "Owner" object is the core of the game object. If you make an enemy, it's probably based on a Sprite object - that object gets the DealsDamage behavior and the ChasePlayer behavior, etc. That's what you schedule - it will take care of updating its own stuff; just use %object.CallOnBehaviors("onUpdate") and it will cause all behavior instances on that object to call their "onUpdate" method if they have one.
Again - if you're going to hit that onUpdate() make sure it's lean. It is script, so it is not as fast as engine code by a long shot and that shit adds up fast.
05/15/2015 (3:40 pm)
Yeah, BehaviorInstances are objects - and every one of them firing a callback into script can get expensive.Just drop about 200 of 'em in there and check it out. If that runs fine, add more.
And from that repo - don't do it for behavior instances, do it for the object "hosting" the behavior instances (the instance owner). That "Owner" object is the core of the game object. If you make an enemy, it's probably based on a Sprite object - that object gets the DealsDamage behavior and the ChasePlayer behavior, etc. That's what you schedule - it will take care of updating its own stuff; just use %object.CallOnBehaviors("onUpdate") and it will cause all behavior instances on that object to call their "onUpdate" method if they have one.
Again - if you're going to hit that onUpdate() make sure it's lean. It is script, so it is not as fast as engine code by a long shot and that shit adds up fast.
#4
Well the behaviors are actually what I am trying to control. The behaviors aren't interacting correctly, but I believe if I can have certain ones "turn off" when others are triggering it will work. The example is the sidestep and chase examples. I was thinking arranging the behaviors into those groups would achieve that, but maybe I didn't see the problem. It would also need those two groups for each enemy as enemy A sidestepping shouldn't pause the others from a chase.
I do have enemies that have the behaviors, but I am almost having the enemy themselves shift into different movement "modes" I guess. Such as chase after a target, then when the zigzag behavior says so it puts the focus on zigzagging instead.
05/15/2015 (4:48 pm)
I see, the constant callbacks would bog down the script. Good to know. what about the behavior connections? I assume if it was constant enough it would too.Well the behaviors are actually what I am trying to control. The behaviors aren't interacting correctly, but I believe if I can have certain ones "turn off" when others are triggering it will work. The example is the sidestep and chase examples. I was thinking arranging the behaviors into those groups would achieve that, but maybe I didn't see the problem. It would also need those two groups for each enemy as enemy A sidestepping shouldn't pause the others from a chase.
I do have enemies that have the behaviors, but I am almost having the enemy themselves shift into different movement "modes" I guess. Such as chase after a target, then when the zigzag behavior says so it puts the focus on zigzagging instead.
#5
I think maybe you need a "think" piece to handle telling the unit what to do - make the main object a member of an "aiUnit" script class and have that script class decide when the unit changes states. Then assign a behavior for each base state and connect it to all the other base state behaviors - when it raises its Entered event it tells the others to stop updating.
This is where a good little tool like yEd comes in handy - map out all of your connections and interactions between behaviors "on paper" so it's easier to follow. Like I said - in the Angry Birds clone template the projectile alone had 17 different behaviors on it, but since each was simple and several were state-related not all of them were doing anything at any given time - light and quick.
05/15/2015 (6:10 pm)
Well, that's what the connections are for - raise an event on one to tell the other to disable its updates etc. when you tell the enemy object itself what to do.I think maybe you need a "think" piece to handle telling the unit what to do - make the main object a member of an "aiUnit" script class and have that script class decide when the unit changes states. Then assign a behavior for each base state and connect it to all the other base state behaviors - when it raises its Entered event it tells the others to stop updating.
This is where a good little tool like yEd comes in handy - map out all of your connections and interactions between behaviors "on paper" so it's easier to follow. Like I said - in the Angry Birds clone template the projectile alone had 17 different behaviors on it, but since each was simple and several were state-related not all of them were doing anything at any given time - light and quick.
Torque Owner Richard Ranft
Roostertail Games
Better to have a SimGroup of objects and schedule an update for the whole group:
function updateGroup(%group) { for(%i = 0; %i < %group.getCount(); %i++) { // iterate the group, call each object's update() method %obj = %group.getObject(%i); %obj.update(); } // do it again in 250 ms schedule(250, 0, "updateGroup", %group); }You can even use this group to filter "out of zone" objects - just move them to a non-updated group. Since an object can only belong to one SimGroup (adding it to another automatically removes it from its current group) you don't have to worry about "did I remove that from there?" stuff.
You could also use a script tick object (can't remember what it's actually called) to update this at a regular (and less frequent than every engine tick) interval.
Check out the https://github.com/RichardRanft/AITutorial scripts, particularly scripts/server/aiClientManager.cs, aiEventManager.cs, aiManager.cs, and aiPlayer.cs - and the art/datablocks/AI folder. Nothing super advanced or groundbreaking, but purely script-driven team-based cooperative units that will seek health/ammo, have a "view cone", know how to aim a grenade launcher, and shift between different groups (like above) to adjust processing load based on the units' distance from player cameras. And, it can be adapted to T2D fairly easily - just change out AIPlayer with your unit's script class name and chase the breakage (shouldn't be too bad, mostly weapons and stuff). Since I didn't bother tying to the Recast/Detour nav system, there is one less thing to break!