Game Development Community

Starting off simple

by J Sears · in Technical Issues · 04/09/2007 (12:18 pm) · 11 replies

I've searched a lot for AI resources/tutorials and they all kind of just give you a whole big complex AI and have you just plug it in. That is handy for things but it doesn't really help to learn it.

I'm trying to start the AI off in my game simple and build up from there.

Right now what I want to do is have a bunch of spawn points together, spawn in a group of AI all at the same time, and then have a schedule that every so often it will spawn in another group as long as there's
Once they spawn I want them to move to one location on the map, so this will be done with a setMoveDestination and have some kind of marker on that spot ( I don't need them to path follow just spawn in and head for that spot)

If any one bot in that group gets attacked before reaching that spot they all go after the attacker, or if they get within x range of any player character before reaching the destination they go after him. If they reach destination they then start to search for closest player. If the player they're attacking dies, look for next closest player. I don't need any kind of retreat behaviour these are just attack till death kind of guys (and for starting I'm not even going to add attacks yet since it's melee and that will be tricky so just chase for now)


That's really how simple I want to start and hope to build off that for things like if while going after first attacker someone is closer attacking they might switch to him and fancy things like that.

Now I know this shouldn't be too hard to script in I'm just having trouble trying to piece it all together as to how to make it work I can understand the basics of different functions just not the whole picture yet. Any help would be great.

#1
04/10/2007 (3:44 am)
So far I have AI players spawn 5 at a time, as long as there's less the 11, this was my test of setting up a max limit of AIs but wanted to spawn them in full groups so max 15 AIs at once. All that works fine and they all run to the spot I want. The problem is I'm trying to set it up for them to respawn once enough AIs die to bring the count back below 11. Problem is I can't figure that part out I tried onDestroyed but it never ends up getting called. Instead everytime I kill one of the AIs I get the error message:

/player.cs unable to find object: ' ' Attempting to call function 'onDeath'

so I take it this means every AI player that spawns in is getting treated like a regular player? I'm not sure if that's right but I'm also not sure why if that's the case it's not able to use .onDeath correctly? I haven't modified any of the player.cs file so it's all stock 1.5.1. Anyone have any ideas?

this is my whole script so far thanks to
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=12407
as a starting point

edit: removed code to shorten the thread, see code below
#2
04/10/2007 (4:21 am)
I changed the end of the script to make it work right that if statement at the end messed up the count by one.
Another problem on top of the onDeath issue is if I change the code to try to name them (thought maybe the lack of a name might strangely be the problem) like so

then when they spawn the first five have a name of AIManager and the last 10 have an object number (all the same for all ten) of 1682 or something like that. Can't figure that out either

edit:removed code to shorten thread
#3
04/10/2007 (11:30 am)
I believe that "AIManager::onDestroyed", would be called when the AIManager gets destroyed. Use "YourAIDataBlockName::onDestroyed".

I use "onDisable" to tell me when an AI or Player has been killed, I don't know what the difference is between them. For the naming, try changing "AIManger::spawn(%name)", to "AIManger::spawn(%this, %name)", I think its trying to pass AIManager as the name.
#4
04/10/2007 (11:30 pm)
I couldn't get ondestroyed to work even with the datablock, I was looking into onDisable like you suggested when I decided to use onDamage instead and check to see if he died from the damage and if so update the items needed.
That ended up working out great and since I'm going to need to use onDamage anyways in the next part of the code to make him go after the player if damaged it seems like a good way to do it.
I couldn't get the name to work in the spot I was trying to get it to work so I moved the function I was declaring it. At this point I'm not even planning on having names above my targets anyways because I don't like that look even in rpgs so it doesn't matter just wanted it in case I changed my mind.
For all the people I'm sure don't care here's the updated code which currently spawns groups of 5 AI which then run to a spot, every 8 seconds it spawns a new group as long as there isn't more then 10 already there. When you kill off 5, 5 more respawn in and run to the spot. Going to end up adding a max limit so they will keep respawning in groups until a certain number is reached, that should only be 3 spots of code.
$TotalAIs = 0;
$AIIndex = 0;
new SimGroup(AIGroup);

datablock PlayerData(DemoPlayer : PlayerBody)
{
   shootingDelay = 2000;
};

//-----------------------------------------------------------------------------
// AIPlayer static functions
//-----------------------------------------------------------------------------

function AIPlayer::spawn(%name,%spawnPoint)
{
   // Create the demo player object
   %player = new AiPlayer() {
      dataBlock = DemoPlayer;
      path = "";
   };
   MissionCleanup.add(%player);
   %player.setShapeName(%name);
   %player.setTransform(%spawnPoint);
   AIGroup.add(%player);
   return %player;
}


//-----------------------------------------------------------------------------
//
function DemoPlayer::onDamage(%this,%obj, %delta)
{
  if(%obj.getState() $= "Dead")
   		 {
   		     $TotalAIs--;
   		     %this.player=0;
                  AIGroup.remove(%obj);
                                  $AIIndex--;
                 echo("was called AND did die");
   		 }
}

function pickAISpawnPoint() 
{
   %groupName = "MissionGroup/AISpawnPoints";
   %group = nameToID(%groupName);

   if (%group != -1) {
      %count = %group.getCount();
      if (%count != 0) {
         %index = getRandom(%count-1);
         %spawn = %group.getObject(%index);
         return %spawn.getTransform();
      }
      else
         error("No spawn points found in " @ %groupName);
   }
   else
      error("Missing spawn points group " @ %groupName);

   // Could be no spawn points, in which case we'll stick the
   // player at the center of the world.
   return "0 0 300 1 0 0 0";
}

function AIManager::think(%this)
{
      if ($TotalAIs < 11)
      {
         for(%AIs=0; %AIs<5; %AIs++)
         {
            %this.spawn();
            $TotalAIs++;
            %this.getClosestAI();
         }
      }
   %this.schedule(8000, think);
}

function AIManager::spawn(%this)
{
   %name = "Kork";
   %spawn = pickAISpawnPoint();
   %player = AIPlayer::spawn(%name, %spawn); 


   if (isObject(%player))
   {
      return %player;
   }
   else
      return 0;
}

//---------------------------
//Does the actual work
//---------------------------
function AIManager::getClosestAI(%this)
{
   %player = localClientConnection.player;
   %curAI = AIGroup.getObject($AIIndex);
   %markerName = "MissionGroup/city";
   %marker = nameToId(%markerName);
   %curAI.setMoveDestination(%marker.getposition());

  // if (vectorDist(%curAI.getPosition(), %player.getPosition()) < 50)
  //    {
  //       if (%curAI.getState() $= "dead")
  //          %curAI.stop();
//
  //       else
   //      {
   //         %curAI.setAimObject(%player, "0 0 2");
   //         %curAI.setMoveDestination(%player.getPosition());
   //      }
   //   }
   //   else
   //   {
   //      %curAI.setAimObject(NULL);
   //      %curAI.stop();
   //   }

   $AIIndex++;
}

Now I'm going to start working on setting it up so as they're going to the spot/have reached the spot. If a player gets close enough they go after him and for now just chase him around. Will add in the same response for taking damage.
#5
04/11/2007 (2:18 am)
Well I've taken the artificial intelligence beginning code
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=6742
And merged parts of that with what I had and cleaned up parts to now have the AI spawn, head at the marker (I got rid of the getclosestAI function and added the move location to the newer spawn function I have) if they notice a player on the way or once they're there they chase him around.
So now to finish off my basic ai (the rest of the improvements will have to be made once there's more working in my project) I want to set up a nice system where if the bot takes damage it goes after the person who did it. The artificial intelligence beginning code doesn't do what I want since it just has the bot search 360 degrees within a certain range to check for the nearest player.
I want it to go specifically at the person who caused the damage. Also I want to setup a system for saving targets so for instance if the bot is attacked it makes that his primary target even if there's a closer player. Also if a bot is in the middle of killing a player I want it to stay on that target whether another player gets closer or another player does damage.
I might have to put this step on hold for a few days until my laptop gets back so that I can actually test the code I work on.
#6
04/11/2007 (5:02 am)
Yea, I also needed someway of tracking a single player, this worked fairly well:

if (vectorDist(%curAI.getPosition(), %player.getPosition()) < 50)
      {
         if (%curAI.getState() $= "dead")
         {
            %curAI.target = "";
         }

         if (%curAI.target !$= "")
         {
            %curAI.setAimObject(%target, "0 0 2");
            %curAI.setMoveDestination(%target.getPosition());
         }
         else
            %curAI.target = %curPlayer;
      }
      else
         %curAI.target = "";
This will keep the AI tracking one player.

You could modify this and put it in a onDamge call. Also try looking into "initContainerRadiusSearch", instead of "vectorDist". With the radius search, you could look for a type of object in a specified range (for more then one player), instead of the distance between just a single player.
#7
04/11/2007 (5:34 am)
I don't know if containerradiussearch would help me. I want to make it so as soon as a bot gets hit by someone he then locks onto that player. So let's say he's spotted the closest player and is running at him, if he gets hit he will change and go after the person attacking him instead. So I just need to figure out a way to have him figure out which player damaged him. I'm hoping there's an easy way as opposed to going into everything about backtracking the direction the damage came from and doing a search in that direction to find a player object. I'm sure there's some nice easy way I just have to hunt it down
#8
04/12/2007 (7:08 am)
This morning, I had a random idea about how to find who hit you. If your using the server-side melee system, then I think it automatically gets attacker an target info.

For projectiles, you can do this:

In your projectiles onFire code, put this at the bottom,
//Store the player thats shooting
   %p.thisObject = %obj;

Then in your projectiles onCollision code, put this at the bottom,
//make sure only players get told to move
   if (%col.getType() & $TypeMasks::PlayerObjectType)
      %col.setMoveDestination(%obj.thisObject.getPosition()); //Move to the attacker
I hope this helps.
#9
04/12/2007 (10:12 am)
I can see how that works and why it was done that way. It won't work that easy with projectiles but it let's me know to look into just modifying the main onCollision function declaration to work with another input.
%p is used currently to make a new projectile and send it out when the crossbow is fired, and then it's taken in onCollision as the colliding object to know when it hits and to do it's damage. Since in the melee they don't want to send out a projectile they stole that return value to add the player id in.

That was still helpfull though because I'm planning on having lots of melee, I just need to figure out a way to do it with projectiles too.
#10
04/12/2007 (10:22 am)
It does work. I put it in my own game to test it before I posted here.

With "%p.thisObject = %obj;", every projectile stores the ID of who fired it.
It works fairly well.
#11
04/13/2007 (8:33 am)
Alright thanks I will get this put into my code later today