Game Development Community

dev|Pro Game Development Curriculum

2D Tuesdays: Behaviors and BlazeJam

by Michael Perry · 07/03/2012 (4:05 pm) · 17 comments

2D Tuesdays: Fancy HUD


static.garagegames.com/static/pg/blogs/michael-perry/TorqueLogoSmall.png

Kickoff

Greetings everyone. It's 2D Tuesday! Each week someone on the 2D team will post a blog or resource related to Torque 2D and/or iTorque 2D. The majority of the posts will come from myself, but don't be shocked if someone else takes over for me from time to time.

Last week, I posted a simple tutorial showing how to use multiple T2D scene files to represent a fancy HUD. At the end of the post, I decided to let all of you vote on the content of this week's entry. It was close, the majority of you wanted to know more about behaviors, how they should be used, and what R&D changes are being introduced to make them more useful. Before we get to that subject, I actually have two things to talk about. First, here's the "let's cover our bacon section":


static.garagegames.com/static/pg/blogs/michael-perry/TorqueLogoSmall.png

FULL DISLOSURE

Do not take the content from these blogs as law. I'm never going to say "this will be in x.x version" or "this is ready to be used right now". There's going to be a lot of R&D discussion. I might even post a discussion I had with Melv that ended up being scrapped. It might be useful for you to understand how we communicate internally, or amusing to see the mad scientists we really are. I will not post timelines. I will not post release dates. I will not commit the team to something we cannot deliver on.


static.torquepowered.com/static/pg/blogs/michael-perry/separatorz.jpg

static.garagegames.com/static/pg/blogs/michael-perry/TorqueLogoSmall.png

BlazeJam 2012

static.garagegames.com/static/upload/emp-44571/blazejam.jpg
I'll be participating in BlazeJam 2012 on July 7th and 8th (this weekend). This is a 48 hour game jam and auction to raise funds for the victims of the Waldo Canyon Fire. All donations will go to the American Red Cross and Care and Share. People who donate will get all the games created for the event. Long time GG member and former Torque contractor Dave Calabrese leading the effort. I also have family in Colorado, so this hits close to home. All are welcome to join in the event. You can join the jammers on site or work remotely like I will. All disciplines are welcome and any donations would be appreciated.

BONUS: I only planned on submitting a closed-source game. However, if you participate and/or donate during BlazeJam, I will give you access to my code. Maybe its the R&D T2D stuff, maybe its a completely separate project. If you can prove you helped out in any way possible, you will get treats.


static.torquepowered.com/static/pg/blogs/michael-perry/separatorz.jpg

static.garagegames.com/static/pg/blogs/michael-perry/TorqueLogoSmall.png

Applying Logic

A couple months ago I gave a presentation to the entire engineering staff at GarageGames. The topic was on Torque technology, specifically our T2D engine. We have hired several programmers who had never used Torque, so I thought it was a good idea to go over some of the basics and core concepts. A specific section of my presentation was on applying logic. The following are the most common ways to apply game logic:

Name: Unique object
Source class: All objects of a source type
Script Class: All objects of a script type
Datablock: All objects that use a config
Behavior: Reusable logic for scene object

I followed that up with one big slide: RTFM. That link is a handy reference, as it is a brief and official overview of behaviors.

I've noticed a few older users of T2D and iT2D popping up recently to respond to my blogs and posts about the R&D. Considering behaviors were introduced in TGB v1.5, I'm quite surprised by how many of the older users do not use behavior or even consider them. They mainly stick with datablocks and script classes. Of course, those are perfectly valid routes to go down for applying logic. Newer users tend to try to do everything in behaviors, which is great until they need extremely complex and unique functionality. This is almost the exact opposite of what a behavior is supposed to do. That brings me to the next point.

static.garagegames.com/static/pg/blogs/michael-perry/TorqueLogoSmall.png

Good Behavior

I try to keep my description of a behavior as simple as possible, because it is too easy to abuse the concept or code yourself into a corner. In my opinion, the following list defines a behavior:

  • A behavior contains logic outside of what a base class should do. A base class typically refers to something like a t2dStaticSprite, t2dSceneObject, etc.
  • The logic is reusable, meaning you can apply a behavior to multiple objects
  • A behavior template usually exposes "tweakable" fields an artist, designer, or novice developer can use to customize an object's behavior instance
  • A behavior should be relatively encapsulated. If you have written a monolithic behavior that can perform five different roles, you might not have written a great behavior.

If you keep that list in mind when trying to decide if you should use a class, datablock, or behavior, you should find your answers pretty quickly. Here are some examples of when I would NOT use a behavior:

  • A global system with a single purpose. Examples include a sound manager, level manager, project manager, etc.
  • A broad logic concept, like a "Player" or "Enemy". When you think of your game's "Player", what could that possibly mean? Going one step further, could your Player behavior be used in completely different game? Probably not.
  • Anything that extends or duplicates a base class's functionality.

I could keep making lists, but I should get to some hard details about using behaviors.

static.garagegames.com/static/pg/blogs/michael-perry/TorqueLogoSmall.png

The Big Three

The behavior system is rather robust, but I can break it down into three simple pieces:

1. The owner (any t2dSceneObject)
2. The behavior template (BehaviorTemplate)
3. The behavior instance (BehaviorInstance)

Primarily, the BehaviorTemplate is used to expose behavior information to the editor. However, it's also important when you need to create a BehaviorInstance. The BehaviorInstance is self-descriptive. It is an instance of that behavior in memory. It has its own unique ID, it tracks its own properties, and will react to function calls on itself and its owner. That last piece is what you are used to working with. The owner is the object that will use the behavior. When you add a behavior to your sprite in a scene, it becomes the owner. Keep these three pieces in mind as we go over examples:

static.garagegames.com/static/pg/blogs/michael-perry/TorqueLogoSmall.png

Examples

Let's start by looking at the smallest behavior example you can find:

TemplateBehavior.cs
// Create this behavior only if it does not already exist
if (!isObject(TemplateBehavior))
{
   // Create this behavior from the blank BehaviorTemplate
   // Name it TemplateBehavior
   %template = new BehaviorTemplate(TemplateBehavior);
   
   // friendlyName will be what is displayed in the editor
   // behaviorType organize this behavior in the editor
   // description briefly explains what this behavior does
   %template.friendlyName = "Template";
   %template.behaviorType = "Game";
   %template.description  = "Disables an object unless it is a clone";
}

// Perform the following logic on the owner when the level is loaded
function TemplateBehavior::onLevelLoaded(%this, %scenegraph)
{
   // Disable the original owner of this behavior, since we will be
   // using it for duplication purposes only
   %this.owner.enabled = false;
}

The above behavior is very useful for objects you plan on cloning frequently. Think of any object you might spawn, such as enemies, projectiles, and power-ups. So why do you need a behavior for this? Well, if you are writing other behaviors that reference the object for cloning, you do not want the original object getting in the way. The limitation of the behavior system is that it can only utilize an object if it exists in the scene. Because of that, you can apply the TemplateBehavior to any objects you plan on duplicating to easily disable it when a level starts.

Alright, let's look at another behavior that actually exposes fields to the editor so a designer or artist can tweak it:

TouchDrag.cs
if (!isObject(TouchDrag))
{
   %template = new BehaviorTemplate(TouchDrag);
   
   %template.friendlyName = "Touch Drag";
   %template.behaviorType = "Movement Styles";
   %template.description  = "Allows the user to drag the object using the touch screen";
   
   %template.addBehaviorField(DragInX, "Allow dragging along the X-axis", bool, true);
   %template.addBehaviorField(DragInY, "Allow dragging along the Y-axis", bool, true);
}

function TouchDrag::onBehaviorAdd(%this)
{
   %this.touchID = "";
   %this.owner.setUseMouseEvents(true);
}

function TouchDrag::onTouchDown(%this, %touchID, %worldPos)
{
   // If we are already dragging, do nothing
   // Otherwise, enable the drag variable
   if (%this.touchID $="")
      %this.touchID = %touchID;
}

function TouchDrag::onTouchUp(%this, %touchID, %worldPos)
{
   if (%this.touchID $= %touchID)
      %this.touchID = "";
}

function TouchDrag::onTouchDragged(%this, %touchID, %worldPos)
{
   if (%touchID !$= %this.touchID)
      return;
      
   if (%this.DragInX)
      %this.owner.setPositionX(%worldPos.x);
      
   if (%this.DragInY)
      %this.owner.setPositionY(%worldPos.Y);
}

Now, if you are a T2D user, you will not have touch functionality. However, this is exactly the same as ::onMouseEvent callbacks, with slightly different parameters. So what does the above behavior achieve? Well, now you, your artist, or your designer can make any object they see in the level editor draggable. Going one step further, they can determine if an object is draggable in a specific axis. This kind of control is at their fingertips, without ever touching code.

There are certainly more complex behaviors that can be written, but I do not want to go overboard with the examples in this blog. I'll provide one more that has more dynamic logic.

ParallaxObjectBehavior.cs
if (!isObject(ParallaxObjectBehavior))
{
    %template = new BehaviorTemplate(ParallaxObjectBehavior);
   
    %template.friendlyName = "Parallax Object";
    %template.behaviorType = "Camera";
    %template.description  = "Changes object position based on camera movement.";

    %template.addBehaviorField(horizontalScrollSpeed, "Percentage of horizontal scroll speed", float, 1);
    %template.addBehaviorField(verticalScrollSpeed, "Percentage of vertical scroll speed", float, 1);
    %template.addBehaviorField(tileable, "Toggles whether the image will tile when stretched", bool, false);    
}

function ParallaxObjectBehavior::onBehaviorAdd(%this)
{
    if (!$LevelEditorActive)    
        %this.owner.setUpdateCallback(true);
}

function ParallaxObjectBehavior::onLevelLoaded(%this, %scenegraph)
{
	%currentPosition = sceneWindow2d.getCurrentCameraPosition();
	%this.oldPositionX = getWord(%currentPosition, 0);
	%this.oldPositionY = getWord(%currentPosition, 1);
}

function ParallaxObjectBehavior::onUpdate(%this)
{
    %currentPosition = scenewindow2d.getCurrentCameraPosition();
	
	%this.currentPositionX = getword(%currentPosition, 0);
	%this.currentPositionY = getword(%currentPosition, 1);
	
	%deltaX = %this.currentPositionX - %this.oldPositionX;
	%deltaY = %this.currentPositionY - %this.oldPositionY;
	
	%horizontalScrollRate = %deltaX * %this.horizontalScrollSpeed;
	%verticalScrollRate = %deltaY * %this.verticalScrollSpeed;
	
	if (%horizontalScrollRate <= 0 && %verticalScrollRate <= 0)
	    return;
	
	%this.oldPositionX = %this.currentPositionX;
	%this.oldPositionY = %this.currentPositionY;
	
	%this.owner.setScroll(%horizontalScrollRate, %verticalScrollRate);
}

The above is a work in progress behavior. The final version will be attached to t2dScrollerObjects in a scene. I haven't finished it yet, as evident by the lack of comments, tileable not being used, and the math being slightly off. It will eventually control the scroll rate of a t2dScroller, based on the camera speed. This will allow another team member to add multiple backgrounds to a level and control how fast they scroll when a camera is moving.

Not only that, but. . . wait. What the what? This blog has gotten huge. Alright, I did not think I would write this much about the current behavior system. I'm afraid I'm gonna have to split this into two parts. Can't eat too much into actual development time.

static.garagegames.com/static/pg/blogs/michael-perry/TorqueLogoSmall.png

Next Time

I'm sure a lot of naysayers are thinking "yeah yeah, I know all this. I still don't use them for ____ reason". Ok, next week's content might change your mind. Behavior lovers are also going to get a kick out of next week's blog. Here is what I didn't cover this week, but will most definitely tackle in next week's 2D Tuesday:

  • Love triangle between behaviors, datablocks, and classes
  • Advanced behavior usage outside of the editors
  • New behavior features in R&D (signals and more)
  • More sample behaviors

After that, I promise I will get into how we analyzed our editor, identified weak points, swooned over its strong points, pondered its existence, then gutted it like a fish. Have a great week. Be sure to let me know if you are participating in BlazeJam. Maybe we can team up.

#1
07/03/2012 (4:44 pm)
Neat blog again. Maybe I'll be awake around BlazeJam time too :)

Suggestion for a third part: Making the C++ equivalent of a behaviour.
#2
07/03/2012 (5:25 pm)
Cool suggestion. I can fit that into part two.
#3
07/03/2012 (8:02 pm)
Interesting post. I'm one of the older TGB users who has yet to delve into behaviors, but this does illustrate how they could be useful.

I agree with Ronny, it would be good to see how we can optimize iT2D projects by moving behaviors into C++ for extra performance.

Even better than that would be to see iT2D in a state where it can release iPad games. That might not make for an interesting blog post but I think it might make some iT2D users quite happy.
#4
07/04/2012 (6:46 am)
Good intro to behaviors for new people. I use behaviors a LOT, though some of mine have grown rather large, I prefer to tweak certain things inside the editor.
#5
07/04/2012 (7:40 am)
I'm with Ronny - a bit on how to work with BehaviorComponent would be cool.

And that's a hint on where to start looking if you want to study up while waiting for that part....
#6
07/04/2012 (9:57 am)
Quote:BONUS: I only planned on submitting a closed-source game. However, if you participate and/or donate during BlazeJam, I will give you access to my code. Maybe its the R&amp;D T2D stuff, maybe its a completely separate project. If you can prove you helped out in any way possible, you will get treats.
Thank you for offering you time and your code to support those in need Mich.

I love the post on behaviors but I think this is the most important part of this post. I truly hope everyone helps or donates that can. These fires have destroyed many lives and anything we can do to help will be appreciated by these people.

as far as the behaviors, I am eagerly awaiting the c++ integration for part 2.
#7
07/06/2012 (10:55 am)
Hi @Mich.

Correct me if I'm wrong but these seem to be the BlazeJam main sites:

facebook: http://www.facebook.com/BlazeJam2012
auction: http://blazejam2012.ceruleangames.com/index.php/auction/blazejam-2012-auction
#8
07/06/2012 (11:02 am)
Woo hoo! Treat for me. I have my confirmation number from the donation site.
#9
07/06/2012 (11:08 am)
Back to behaviors. That's a cool way to do (non-autoscrolling) parallax. I think I will be having it.

A few months ago, I added a way to tell that the camera moved by a callback. This way I don't have to use onUpdate every frame. Yes, I am a CPU cheapskate, though I prefer to call it a value-oriented CPU user. Here is the small C++ patch that can call your scripts only when the camera changes:

Index: t2dSceneWindow.cc
===================================================================
--- t2dSceneWindow.cc	(revision 285)
+++ t2dSceneWindow.cc	(revision 286)
@@ -352,6 +352,9 @@
 
     // Set Camera Target to Current.
     mCameraTarget = mCameraCurrent;
+
+    calculateCameraView( &mCameraCurrent );
+    Con::executef(this, 2, "onUpdateCamera");
 }
 
 
@@ -475,6 +478,9 @@
 
     // Set Camera Target to Current.
     mCameraTarget = mCameraCurrent;
+
+    calculateCameraView( &mCameraCurrent );
+    Con::executef(this, 2, "onUpdateCamera");
 }
 
 
@@ -526,6 +532,9 @@
 
     // Set Camera Target to Current.
     mCameraTarget = mCameraCurrent;
+
+    calculateCameraView( &mCameraCurrent );
+    Con::executef(this, 2, "onUpdateCamera");
 }
 
 
@@ -973,6 +982,9 @@
     mCameraCurrent.mCameraWindow.extent.x = interpolate( mCameraSource.mCameraWindow.extent.x, mCameraTarget.mCameraWindow.extent.x, normCameraTime );
     mCameraCurrent.mCameraWindow.extent.y = interpolate( mCameraSource.mCameraWindow.extent.y, mCameraTarget.mCameraWindow.extent.y, normCameraTime );
     mCameraCurrent.mCameraZoom = interpolate( mCameraSource.mCameraZoom, mCameraTarget.mCameraZoom, normCameraTime );
+
+    calculateCameraView( &mCameraCurrent );
+    Con::executef(this, 2, "onUpdateCamera");
 }
#10
07/06/2012 (12:23 pm)
@Charlie - That's awesome! Thanks for lending a hand. I'll make sure you are on the list of treat recipients
#11
07/07/2012 (7:15 am)
I'm starting on my entry for BlazeJam 2012. While it may not always be exciting, I will be broadcasting via my personal Ustream. Here is the link: BlazeJam 2012. I will check that chat for viewer questions, but I will also be in our GarageGames IRC channel to talk about my progress.

I have decided to use the Torque 2D R&D to create a game entry, so anyone who participates or donates will get a demo of that.
#12
07/07/2012 (8:06 pm)
Neat method, Charlie. It would be very useful on mobile devices.
#13
07/08/2012 (9:04 am)
@Mich - Did I miss something? I thought you said you would be on Ustream and IRC. The only thing I found was your recorded vid. Everything OK over there?
#14
07/10/2012 (8:03 am)
Again, thanks for the blog. Really awesome to read and very helpful Mike.

You should include those blogs into the official documentation, where applicable. Of course without the Events etc. Or add links.
#15
07/13/2012 (2:36 pm)
@GG: Is everything alright? Where did Tuesday go? Where did FRIDAY go? So lonely! We missed you in the IRC channel. Is the building still standing? More importantly, is the repository still safe?
#16
07/19/2012 (4:29 pm)
I am still interested in seeing Behaviors: Part 2.
I donated to the BlazeJam and hope it helps those affected by the fires.
#17
07/20/2012 (12:40 pm)
We're alive. We are slammed. We are aware of the missed community involvement. Things are going well here, which is why we had a week of silence, so don't think otherwise.

Repo is still safe, finally fully internalized. No more external hosting and downtime.

@Greg - The next blog is coming, as is the code for those who donated to BlazeJam.