Game Development Community

dev|Pro Game Development Curriculum

A quick breakdown on datablocks, objects, %this and %obj

by RedOctopus · 05/31/2012 (6:46 pm) · 3 comments

So, today, whilst rewriting my AI code in order to make it more modular, I had to get a much better grasp on the difference between datablocks and objects when it comes to calling functions. In this context, I was getting confused between AIPlayer and, we'll say, DemoPlayer, and why calling functions between them wasn't working as expected.

Well, the way I now remember it, when writing functions that I want to be attached to a datablock, and thus more easily overloaded, is that a datablock function, typically uses %obj, a reference variable to the object the datablock is assigned to. In my AI example, my AIPlayer object has a datablock, DemoPlayer, assigned to it, but if I wanted a different type of AI, I'd assign it a different datablock, say TestPlayer.

So, here's the situation broken down: AIPlayer has functions that are used by all my AI types. One AI type will use a datablock named DemoPlayer and another AI type will use a datablock named TestPlayer. Both of these AIs will be able to make a melee attack, but one of them has twice the range. Below is how I would set up the script:

pickData(%this, %num)
{
   switch(%num)
   {
      case 1:
         AIPlayer::spawnAI("DemoPlayer");
      case 2:
         AIPlayer::spawnAI("TestPlayer");
   }
}

AIPlayer::spawnAI(%this, %datablock)
{
   %player= new AIPlayer()     //create a new AIPlayer object and give it the DemoPlayer datablock
   {
       datablock = %datablock;
   }
   %player.mainloop();
}

AIPlayer::mainloop(%this)
{
   %this.getDatablock().doMelee(%this);
   %this.mainloop();
}

DemoPlayer::doMelee(%this, %obj)
{
   echo(%obj @ " does short range melee attack");
}

TestPlayer::doMelee(%this, %obj)
{
   echo(%obj @ " does long range melee attack");
}

The above code makes a new AIPlayer with either the DemoPlayer datablock or the TestPlayer datablock, depending on whether you call the pickData function with either a 1 or a 2. Then, the mainloop is called and the doMelee function is called depending on whether the AI uses a DemoPlayer or TestPlayer datablock. Then the echo function prints a string that starts with the handle of the object that that datablock is assigned to, which is passed as the %obj variable.

While I'm at it, I'll try to describe the %this variable. It's pretty simple, really. When a function gets called, the handle of the calling object gets sent along with the function call. In this case, when we call %player.mainloop(), the AIPlayer object's handle is passed along with the call for the mainloop function. In the mainloop function, when we call %this.mainloop(), what we're really saying is (AIPlayer object handle).mainloop().

And that's about it. I don't know if this was very clear or not, but I know I regularly get confused about it, so I figured I'd write a blog about it.

About the author

Recent Blogs


#1
06/02/2012 (12:02 pm)
You might want to add a schedule on the main loop call cause if you do that on multi AI's you might lock the engine. It's a recursive function call that will go on for every and could lock your processes.

Also add the schedule handle to the AI create so you can disable a dead AI or cancel the schedule if needed for any reason.
#2
06/10/2012 (2:07 pm)
So many ways to "skin the cat" ... seems like just setting a variable on the objects when they spawn to control the range would work too.

AIPlayer::spawnAI(%this, %datablock, %meleeRange)
{
   %player= new AIPlayer()     //create a new AIPlayer object and give it the DemoPlayer datablock
   {
       datablock = %datablock;
   }
   %player.meleeRange = %meleeRange;

   %player.mainloop();
}
...

AIPlayer::doMelee(%this, %obj)  
{  
   if (%obj.meleeRange $= "2")
    {
     echo(%obj @ " does long range melee attack");  
    }
   else
    {
      // default
      echo(%obj @ " does short range melee attack");  
    }
}

#3
06/10/2012 (3:26 pm)
Here is how I would go about this, using some variables.

$enemy::isSet["Enemy1"] = true;
$enemy::health["Enemy1"] = 100;
$enemy::meleeRange["Enemy1"] = 10;
$enemy::datablock["Enemy1"] = someDataBlock;

function spawnAi(%name)
{
	if($enemy::isSet[%name])
	{
		%health = $enemy::health[%name];
		%meleeRange = $enemy::meleeRange[%name];
		%datablock = $enemy::dataBlock[%name];
		
		%player= new AIPlayer()     //create a new AIPlayer object and give it the DemoPlayer datablock
		{
			datablock = %datablock;
			health = %health;
			meleeRange = %meleeRange;
		};
		
		%player.mainloop();
	}
}

function AIPlayer::mainloop(%this)
{
	if(isEventPending(%this.mainLoop))
		cancel(%this.mainLoop);
	
   %this.getDatablock().doMelee(%this);
   
   %this.mainLoop = %this.schedule(1000,mainloop);
}

function aiPlayer::doMelee(%this)
{
	%meleeRange = %this.meleeRange;
	
	//do stuff with the range
}