Game Development Community

Simple bird AI code

by Justin Knight · in Torque 3D Professional · 12/23/2009 (5:09 pm) · 3 replies

I'm trying to write a simple AI to control the birds in my level. I currently have a test bird staticShape and the following code to move it:

// Bird AI
// @TODO prevent going out of bounds
$bird = null;
$state = 0; // 1=fly 2=circle
$updateIntervalMS = 100; // How often to update position in ms
$flapping = false;
$flyDelta = 0.4;
$angleDelta = 0.0;

function startAI( %bird) {
   $bird = %bird;
   decideState();  // Decide initial state.
   scheduleFlap();
}

// Decide what to do next
function decideState() {
   // $state = getRandom( 1, 2);
   $state = 2; // Just circle constantly while investigating bug
   
   // Initialise chosen state.
   switch( $state) {
      case 1: // Fly
         // nothing to initialise
      case 2: // Circle
         $angleDelta = getRandom( 1, 9) / 100.0;        
   }

   // Schedule first update
   scheduleNext();   
   
   // Schedule next decision
   %decisionInterval = getRandom( 5000, 20000);
   schedule( %decisionInterval,0, decideState);      
}

function scheduleFlap() {
   $flapping = ! $flapping;
   if( $flapping)
      $bird.playThread( 0, "flap");
   else
      $bird.stopThread( 0);
      
   %flapInterval = getRandom( 5000, 20000);      
   schedule( %flapInterval, 0, scheduleFlap);
}

function scheduleNext() {
   if( isObject($bird)) {   
      switch( $state) {
         case 1:
            schedule( $updateIntervalMS,0, updateBirdFly);                        
         case 2:
            schedule( $updateIntervalMS,0, updateBirdCircle);      
      }  
   }
}

// Flying
function updateBirdFly() {
   if( isObject($bird) && $state==1) {   
       %forward = $bird.getForwardVector();
       %tbird = $bird.getTransform();
       %tbird.X += %forward.X * $flyDelta;
       %tbird.Y += %forward.Y * $flyDelta;
      $bird.setTransform( %tBird);         
   }
   scheduleNext();
}

// Circling 
function updateBirdCircle() {
   if( isObject($bird) && $state==2) {
      %tBird = $bird.getTransform();      
      %start = %tBird;
      %forward = $bird.getForwardVector();
      %tbird.X += %forward.X * $flyDelta;
      %tbird.Y += %forward.Y * $flyDelta;

      // adjust current heading by $angleDelta 
      %heading = getWord( %tBird, 6);
      %heading += $angleDelta;
      if( %heading>6.283)  %heading=0;
      %tBird = setWord( %tBird, 3, 0);  
      %tBird = setWord( %tBird, 4, 0);  
      %tBird = setWord( %tBird, 5, 1);          
      %tBird = setWord( %tBird, 6, %heading);     

      echo( %start @ " / " @ %tBird);           
      $bird.setTransform( %tBird);
   }  
   scheduleNext();
}

It's meant to circle but I'm getting strange behaviour with the rotation angle. Here's a bit of the console output:-
Quote:
-2.36893 -51.689 30.7601 0 0 1 3.82 / -2.61995 -52.0004 30.7601 0 0 1 3.89
-2.61995 -52.0004 30.7601 0 0 1 3.89 / -2.89214 -52.2935 30.7601 0 0 1 3.96
-2.89214 -52.2935 30.7601 0 0 1 3.96 / -3.18416 -52.5669 30.7601 0 0 1 4.03
-3.18416 -52.5669 30.7601 0 0 1 4.03 / -3.49459 -52.8192 30.7601 0 0 1 4.1
-3.49459 -52.8192 30.7601 0 0 1 4.1 / -3.8219 -53.0491 30.7601 0 0 1 4.17
-3.8219 -53.0491 30.7601 0 0 1 4.17 / -4.16449 -53.2556 30.7601 0 0 1 4.24
-4.16449 -53.2556 30.7601 0 0 -1 2.04319 / -4.52068 -53.4376 30.7601 0 0 1 2.11319
-4.52068 -53.4376 30.7601 0 0 1 2.11319 / -4.17809 -53.6441 30.7601 0 0 1 2.18319
-4.17809 -53.6441 30.7601 0 0 1 2.18319 / -3.85078 -53.874 30.7601 0 0 1 2.25319
-3.85078 -53.874 30.7601 0 0 1 2.25319 / -3.54035 -54.1263 30.7601 0 0 1 2.32319

For some reason on the line I've bolded the axis of rotation shifts from the +ve to -ve Z axis and the angle changes itself. I've only been using T3D for 3 weeks so I've probably missed something obvious - can anyone help?

In case they're relevant here is the datablock and part of my mission file that creates the bird object...
datablock StaticShapeData(BirdDB)
{
   shapeFile = "art/shapes/chris_models/cranimation1.cached.dts";
   category = "StaticShape";
};

new StaticShape(crane) {
      receiveSunLight = "1";
      receiveLMLighting = "1";
      useCustomAmbientLighting = "0";
      customAmbientLighting = "0 0 0 1";
      dataBlock = "BirdDB";
      position = "-8.39808 1.70238 30.7601";
      rotation = "0 0 1 0";
      scale = "1 1 1";
      isRenderEnabled = "true";
      canSaveDynamicFields = "1";
   };

Being new to torquescript and T3D I'd appreciate any other criticisms of my code - I may be doing things in long-winded or inappropriate ways.

Thanks,
Justin.

#1
12/23/2009 (10:51 pm)
Erm, its been this way all along. Don't worry, its not a bug in your code. Instead of doing "%tBird = setWord( %tBird, 5, 1);" do a check instead. If == 1 add $angleDelta else if it's == -1 subtract $angleDelta.

0 0 1 a = 0 0 -1 2*PI-a
#2
12/23/2009 (11:45 pm)
(ignore this post)
#3
12/24/2009 (5:01 am)
@Dreamarvel thanks very much, all working now.

Here's the fixed code in case it's of use to anyone else.
// Bird AI
// @TODO prevent going out of bounds
$bird = null;
$state = 0; // 1=fly 2=circle
$updateIntervalMS = 100; // How often to update position in ms
$flapping = false;
$flyDelta = 0.4;
$angleDelta = 0.0;

function startAI( %bird) {
   $bird = %bird;
   decideState();  // Decide initial state.
   scheduleFlap();
}

// Decide what to do next
function decideState() {
   $state = getRandom( 1, 2);
   
   // Initialise chosen state.
   switch( $state) {
      case 1: // Fly
         // nothing to initialise
      case 2: // Circle
         $angleDelta = getRandom( 1, 7) / 100.0;        
   }

   // Schedule first update
   scheduleNext();   
   
   // Schedule next decision
   %decisionInterval = getRandom( 5000, 20000);
   schedule( %decisionInterval,0, decideState);      
}

function scheduleFlap() {
   $flapping = ! $flapping;
   if( $flapping)
      $bird.playThread( 0, "flap");
   else
      $bird.stopThread( 0);
      
   %flapInterval = getRandom( 5000, 20000);      
   schedule( %flapInterval, 0, scheduleFlap);
}

function scheduleNext() {
   if( isObject($bird)) {   
      switch( $state) {
         case 1:
            schedule( $updateIntervalMS,0, updateBirdFly);                        
         case 2:
            schedule( $updateIntervalMS,0, updateBirdCircle);      
      }  
   }
}

// Flying
function updateBirdFly() {
   if( isObject($bird) && $state==1) {   
       %forward = $bird.getForwardVector();
       %tbird = $bird.getTransform();
       %tbird.X += %forward.X * $flyDelta;
       %tbird.Y += %forward.Y * $flyDelta;
      $bird.setTransform( %tBird);         
   }
   scheduleNext();
}

// Circling 
function updateBirdCircle() {
   if( isObject($bird) && $state==2) {
      %tBird = $bird.getTransform();      
      
      // Move forward by $flyDelta
      %forward = $bird.getForwardVector();
      %tbird.X += %forward.X * $flyDelta;
      %tbird.Y += %forward.Y * $flyDelta;

      
      // Check axis of rotation is correct
      if( getWord( %tBird, 3) != 0 || getWord( %tBird, 4) != 0) {
         %tBird = setWord( %tBird, 3, 0);
         %tBird = setWord( %tBird, 4, 0);  
         %tBird = setWord( %tBird, 5, 1);         
      }

      // Adjust current heading by $angleDelta 
      %heading = getWord( %tBird, 6);      
      %zaxis = getWord( %tBird, 5);                
      if( %zaxis>0)
         %heading += $angleDelta;
      else
         %heading -= $angleDelta;
      if( %heading>6.283)  %heading=0;
      %tBird = setWord( %tBird, 6, %heading);     
         
      $bird.setTransform( %tBird);
   }  
   scheduleNext();
}

I've got a feeling using setVelocity() when it's flying straight would be more efficient but it didn't seem to work.