Game Development Community

NPC AI Wander Code working sorta...

by Kevin Mitchell · in Torque 3D Professional · 05/10/2009 (5:52 pm) · 12 replies

$NPC::Attack=1;
$NPC::Defend=2;
$NPC::Spell=3;
$NPC::Arts=4;
$NPC::Item=5;
$NPC::Disabled=6;
$NPC::Waiting=7;
$NPC::Idle=8;
$NPC::Moving=9;
$NPC::PickDestionation=10;
$NPC::Init=15;
 datablock PlayerData(NPCMale : Player)  
 {  
      category= "NPC_CHARACTERS"; // Add to editor  
      shapeFile = "art/shapes/players/Tori/Tori.dts";

 };  

function NPCMale::onAdd(%this,%obj)  
{
   %this.schedule(50, "think",%obj);
}  
   
function PlayerData::create(%block)
{
  // The mission editor invokes this method when it
  // wants to create an object of the given datablock
  // type.  You only need one of these methods for any
  // class/datablock type (in this case StaticShape).
      %obj = new AIPlayer() {  
        dataBlock = NPCMale;  
        aiPlayer = true;  
        DestinationX=0;
        DestinationY=0;
        OriginX=0;
        OriginY=0;
        CurrentState = $NPC::Init;
        MovementRadius=10;
        MovementDistance=6;
        MovementSpeed=0.5;
        WaitTime=10000;
    };  
  return(%obj);
}

function NPCMale::WanderSomewhere(%this, %obj){
   %Xpos=0;
   %Ypos=0;
   
   %xfrm = %obj.getTransform();
   %charX=getWord(%xfrm,0);
   %charY=getWord(%xfrm,1);
   %distance=%obj.MovementDistance;
   
   %WanderMinX=%obj.OriginX-%obj.MovementRadius;
   %WanderMaxX=%obj.OriginX+%obj.MovementRadius;
   %WanderMinY=%obj.OriginY-%obj.MovementRadius;
   %WanderMaxY=%obj.OriginY+%obj.MovementRadius;
   
   while(1){
    %direction=(6.28/360)*(getRandom(0,360));
    %Xpos= mCos(%direction)*%distance;
    %Ypos= mSin(%direction)*%distance;
    
    if(
    %charX+%Xpos >= %WanderMinX &&
    %charX+%Xpos <= %WanderMaxX &&
    %charY+%Ypos >= %WanderMinY &&
    %charY+%Ypos <= %WanderMaxY )
    {
      %obj.DestinationX=%charX+%Xpos;
      %obj.DestinationY=%charY+%Ypos;
      %obj.setMoveDestination(" "@ %charX+%Xpos @" "@ %charY+%Ypos @" "@0,true);
      break;    
    }else{
      //Return to Origin
      %obj.setMoveDestination(" "@ %obj.OriginX @" "@ %obj.OriginY @" "@0,true);
      break;
    }
   }
}
function NPCMale::think(%this,%obj){
   %a=0;
   switch(%obj.CurrentState){
      case $NPC::Disabled :
         break;
      case $NPC::Waiting : 
         %obj.CurrentState=$NPC::PickDestionation;
         %this.schedule(%obj.WaitTime, "think",%obj);
         break;
      case $NPC::Idle :
         break;
      case $NPC::Moving :
         /*%xfrm = %obj.getTransform();
         %charX=getWord(%xfrm,0);
         %charY=getWord(%xfrm,1);
         %Vect1 = %charX@","@%charY@",0";
         %Vect2 = %obj.DestinationX@","@%obj.DestinationY@",0";
         %distance = VectorDist( %Vect1,%Vect2 );
         echo("%distance: "@%distance);
         if(%distance<2){
            %this.CurrentState=$NPC::Waiting;
         }else{
            %this.CurrentState=$NPC::Moving;
         }
         %this.schedule(50, "think",%obj);*/
         break;
      case $NPC::PickDestionation :
         %this.WanderSomewhere(%obj);
         %obj.CurrentState=$NPC::Moving;
         //%this.schedule(50, "think",%obj);
         break;
      case $NPC::Init :
         %xfrm = %obj.getTransform();
         %charX=getWord(%xfrm,0);
         %charY=getWord(%xfrm,1);
         %obj.OriginX=%charX;
         %obj.OriginY=%charY;
         %obj.setMoveSpeed(%obj.MovementSpeed);
         %obj.CurrentState=$NPC::Waiting;
         
         %this.schedule(50, "think",%obj);
         break;
   }
}

function NPCMale::onMoveStuck(%this, %obj){
   %obj.setMoveDestination(" "@ %obj.OriginX @" "@ %obj.OriginY @" "@0,true);
}
function NPCMale::onReachDestination(%this, %obj){
   %obj.CurrentState=$NPC::Waiting;
   %this.schedule(50, "think",%obj);
}

So I have the character in the Scripted objects section of the editor, and i can add the NPC to the field, with the custom fields in the object properties. So now all i have to add is the other NPC parts like speech, time of the day they will be displayed and things like that. My only problem is that I can only add 1 instance of this object. When I add another NPC the engine freezes.

The only thing I can thing that doing this is the face that the schedule is calling %this.Schedule instead of %obj.schedule but when I use %obj the function isn't called. I's there something I'm missing?

Also if someone is using this the onStuck is never called when NPC gets stuck on running into a fence so you can delete that.

Edit: HMM isn't not freezing now... maybe i had to delete my DSO's But still I should be able to do %obj.schedule to access my think function right?

As an added not i find it cool that I dropped one in the water and it started swiming ^^ Though he can't raise to the top of the water... Atleast he's swiming on the river floor.

#1
05/10/2009 (6:26 pm)
.. A ai that actually does something.. for torque.. gasp.. My Hero!!
#2
05/10/2009 (6:57 pm)
Neat, Thanks Kevin. This will be handy. Ill post back if i do anything extra cool with it.
#3
05/10/2009 (7:37 pm)
I still have things to add to this. Hopefully I can get this done by Friday. I'm trying to get it working like RPG makers NPC object. So you can set the NPC to a path dynamically or wonder around a cubic area maybe a rectangular area. Dialog and other things.
#4
05/11/2009 (5:57 am)
If its for a RPG you probably want to set HomeX,Y and max distance from home before returning, for it to wander not too far away.

Also, for Agro you probalby want some sort of A+ path finding system, so it runs around buildings/blocked objects once agroing a player.

Last one you should probably do in C++, and then add a function to get the path to script.

But then again, it looks like you got it all under control :).
#5
05/11/2009 (11:53 am)
# OriginX=0;
# OriginY=0;

These are the Home positions and the position is set on drop of the NPC

# MovementRadius=10;

This is the Maximum distance from origin they are allowed to wander, if their random wander point is out side that distance they will return to their origin.

# MovementDistance=6;

This is the max length they walk per wander check, obviously this can't be >= the Movement radius.


You did give me a great idea for avoiding objects... only I wonder how it will work... Is there a function to cast a ray between the NPC and the player to see if something is in the way?

Also I'm trying to make a resource that the T3D basic users can use as well. So I'm going to 'try' to keep it OOP oriented easy to drop with out putting micro code everywhere in 40000 files, and now engine changes as much as possible, which might be possible IMO.

Thanks BO, you developers really get my brain matter percolating.
#6
05/11/2009 (12:14 pm)
Quote:
Is there a function to cast a ray between the NPC and the player to see if something is in the way?

conatainerRaycast. This is the script I use for my AI when they're checking to see if they have a clear view of their target.

function AIPlayer::clearview(%this, %tgt)
{
   if(!isObject(%tgt)) return false;

   %muzzle = %this.getEyePoint();

   %targetsearch = containerRayCast(%muzzle, %aimpoint, $AIPlayer::Obstacles, %this);

   %impactpoint = firstWord(%targetsearch);
   if(%impactpoint == %tgt || %impactpoint == 0) return true;   // path is clear 
   else return false;
}

//and the list of obstacles to check for
$AIPlayer::Obstacles = 
  $TypeMasks::PlayerObjectType |      // other players, not the target
  $TypeMasks::TerrainObjectType |     // dirt, but not water
  $TypeMasks::StaticTSObjectType |    // trees and whatnot
  $TypeMasks::InteriorObjectType;     // walls

raycast codes on TDN
tdn.garagegames.com/wiki/TorqueScript_Console_Functions_16
#7
05/13/2009 (8:59 am)
I have some more add-ons to post but I've got the Argo and Clear path detection programmed, All i need now is to test it. This morning I had a few things to fix but it looks like it's made of WIN hopefully.

Small question is this a proper use of eval?

I have a stringNumberID of the Object. If i want to look at that objects name. Id have to envoke:


%targetFound=3464;
%targetNameVal = eval(%targetFound@".name");

Right? seemed to be failing horribly this morning before work.
#8
05/13/2009 (10:46 am)
The eval statement should work but, you can just do
%targetNameVal = %targetFound.name;

Just try in the console, write: echo(3464.name); it works too
#9
08/07/2009 (5:10 pm)
Hello, I am a Noob to torque, can you help me use the code posted here?

1, what do you call the script file this code is in?

2, Where should this file be located?

3, what file/command is used to activate this to get a npc into your game environment?
#10
08/08/2009 (10:12 am)
First off David. Answering all of these questions isn't exactly a simple here you go thing. If your willing to read to learn I know of a place that you can start.
The TDN network is a great place to start learning about scripting. the Resources section is also a good place to learn about scripting.
Since you have purchased the T3D and the TGEA you should purchase the TGE and start there. The TGE offered in my opinion a better place to start learning then TGEA ever did.

tdn.garagegames.com/wiki/Torque_Shader_Engine#script
#11
08/08/2009 (10:27 am)
@David,
this snippet of code is related to a resource
that Kevin released a few days after asking his question, so should not be taken as a stand alone solution.
if you are interested in what it proposes, try using his resource as a whole, its a good one, and if you can follow instructions, you'll have it working in no time
#12
08/08/2009 (11:21 am)
Thanks deep you said it all. I'm trying to weed time into improving that AI as well. But with school and working im drained and often spend time writing papers all the time...

keep looking to the sky ppl! bO_Od