Game Development Community

Some thoughts and questions about AI

by Jason Gossiaux · in Technical Issues · 02/16/2008 (9:35 am) · 5 replies

Howdy folks,

I've recently started constructing an AI Engine for my game, and have run into a few quirks that have required creative thinking to overcome. I thought I'd share my solutions and see if anyone had more sound methods for dealing with them.

The design of my AI engine is as such:

- I started with the fps demo structure and spawn various AIPlayers as is needed.

- Each AIPlayer has its scheduled Think() thread
- This thread looks at the current action in progress and then looks at the ActionQueue that exists.
- It decides if it should continue the current action or pick a new one from the ActionQueue
- The actions are pushed and pop'd using the provided framework

- There exists 1 ActionQueue for the game.
- This ActionQueue is a Priority driven queue.
- Actions are Pushed onto the Queue as complex strings tokenized by ";" characters
- an example is, "ActionMove; 121; 534; 445; Kork; 564564;54664;45644"
- The ActionQueue has a push and a pop function.
- Push takes several arguments, concatanates them into one string and stores them
- Pop finds the highest priority command and returns the string which is split back in the calling function

Does anyone know a more elegant way to store data in arrays in TorqueScript? I have to imagine making/breaking large strings is a time consuming process...

Does a better way exist than using queues for AI behavior? Conceptually my game will have various "job" creating objects. Like a farm or a blacksmith. The player will queue up items at the blacksmith to be made, which will create blacksmithy job items in the queue. Then as players check the queue they'll pop out those jobs and assign them to themselves. Arrays would be so handy, as each job will have 10+ variables associated with it. Perhaps someone has handled such a thing in a better manner?

#1
02/18/2008 (3:02 am)
I'm not dead certain what you're asking--you are aware that TS has arrays, right? They may be a little clunky but they do work.

I do not know this but my hunch is that if you wrote all that you describe already, you've probably got a pretty good grasp of TS. Sounds like a nice resource you could post. :-)
#2
02/18/2008 (8:15 am)
The problem with TS is that functions cannot return arrays. That totally destroys any benefit from using queues due to the overhead invoked when doing the string combines/breaks like I have shown above. I've also not tried multi dimensional arrays in TS yet to see if they work or not.

If you could just return an array from a function it would work so much easier.... So how have other people dealt with this TS limitation? Or is the overhead from doing the string combine/breaking not going to be that bad?
#3
02/18/2008 (4:40 pm)
I THINK what you could do is create a scriptobject, and pass that between functions.

I'm a little fuzzy about that, though. I do believe TS does support multi dimensional arrays, though, and perhaps you could stick it in a script object.

Let me know how that works out!
#4
02/18/2008 (5:20 pm)
A few options for passing arrays around -

* implement the array resource.
* create a script object and make the array a member field, then pass the object around.
eg
function function1()
{
   %foo = new ScriptObject();
   %foo.array[numElements] = 0;
   %foo.array[%foo.array[numElements]] = "some stuff"; %foo.array[numElements] = %foo.array[numElements] + 1;
   %foo.array[%foo.array[numElements]] = "more stuff"; %foo.array[numElements] = %foo.array[numElements] + 1;

   function2(%foo);

   for (%n = 0; %n < %foo.array[numElements]; %n++)
   {
      echo("%foo[" @ %n @ "] =" SPC %foo.array[%n]);
   }
   
   %foo.delete();
}

function function2(%obj)
{
   %obj.array[%obj.array[numElements]] = "yet more stuff"; %obj.array[numElements] = %obj.array[numElements] + 1;
   %obj.array[%obj.array[numElements]] = "and more stuff"; %obj.array[numElements] = %obj.array[numElements] + 1;
}

yields:
[2/18/2008 17:18:40][Inf][General] ==>function1();
[2/18/2008 17:18:40][Inf][General] %foo[0] = some stuff
[2/18/2008 17:18:40][Inf][General] %foo[1] = more stuff
[2/18/2008 17:18:40][Inf][General] %foo[2] = yet more stuff
[2/18/2008 17:18:40][Inf][General] %foo[3] = and more stuff
#5
02/18/2008 (5:29 pm)
Or heck, you could wrap it all up like this:
function ScriptArray::append(%this, %value)
{
   %this.array[%this.numElements] = %value;
   %this.numElements++;
}

function ScriptArray::get(%this, %index)
{
   return %this.array[%index];
}

function ScriptArray::set(%this, %index, %value)
{
   // note this allows you to go beyond the end of the array by one.
   if (%index > %this.numElements)
   {
      error("Subscript out of range:" SPC %index SPC "value:" SPC %value);
      return;
   }

   if (%index == %this.numElements)   
      %this.append(%value);
   else
      %this.array[%index] = %value;
}

function ScriptArray::size(%this)
{
   return %this.numElements;
}

function ScriptArray::dumpValues(%this)
{
   for (%n = 0; %n < %this.numElements; %n++)
   {
      echo(%n SPC %this.get(%n));
   }
}

function new_ScriptArray(%name)
{
   %obj = new ScriptObject();
   %obj.bindClassName("ScriptArray");
   %obj.numElements = 0;
   %obj.setName(%name);
   return %obj;
}
yielding:
==>new_ScriptArray(foo);
==>foo.dumpValues();
==>foo.append("orion");
==>foo.append("was");
==>foo.append("here");
==>foo.dumpValues();
0 orion
1 was
2 here
==>foo.set(0, "a barber");
==>foo.dumpValues();
0 a barber
1 was
2 here