More begginer stuff : Enemies and rotation -code snippet-

A forum to store posts deemed exceptionally wise and useful
Post Reply
Cristian

More begginer stuff : Enemies and rotation -code snippet-

Post by Cristian »

Hi I'm back!
This time with a pretty requested feature, rotating enemies to face the player (or any other node) and move towards it.
Well let's get started

1) Turning the node(The function is not mine. I found it while searching the forums)

Code: Select all

vector3df getTargetAngle(vector3df v, vector3df r) 
{
   //v -current node position
   //r -target node position

   vector3df angle; 
   float x,y,z; 
   x = r.X - v.X; 
   y = r.Y - v.Y; 
   z = r.Z - v.Z; 
    
   //angle in X-Z plane 
   angle.Y = atan2 (x, z); 
   angle.Y *= (180 / PI); //converting from rad to degrees 
    
   //just making sure angle is somewhere between 0-360 degrees 
   if(angle.Y < 0) angle.Y += 360; 
   if(angle.Y >= 360) angle.Y -= 360; 
    
   //angle in Y-Z plane while Z and X axes are already rotated around Y 
   float z1 = sqrt(x*x + z*z); 
    
   angle.X = atan2 (z1, y); 
   angle.X *= (180 / PI); //converting from rad to degrees 
   angle.X -= 90; 
    
   //just making sure angle is somewhere between 0-360 degrees 
   if(angle.X < 0) angle.X += 360; 
   if(angle.X >= 360) angle.X -= 360; 

   return angle; 
}
2) getting the distance

Code: Select all

f64 getDistance(ISceneNode* sn1 ,ISceneNode* sn2) 
{ 
     vector3df pos = sn1->getPosition(); 
     return  pos.getDistanceFrom(sn2->getPosition());  
}
3) and finally, in your let's say Enemy::Update() function
do something like:

Code: Select all

core::vector3df facing( sin( enemyNode->getRotation().Y * PI/180.0f ), 0, cos( enemyNode->getRotation().Y * PI/180.0f ) ); 
facing.normalize();  
[your enemy node]->setPosition([your enemy node]->getPosition()+(facing*[movement speed]));
Of course you should check if the player is in range of the enemy, for example my ESpider has 3 states:
1) if (getDistance(Spider1 ,Player)<50). In this state the enemy faces the target, showing that it's aware of his presence.
2) if (getDistance(Spider1 ,Player)<30). In this state the enemy advances towards the player, until...
3) if (getDistance(Spider1 ,Player)<15). The enemy attacks the player!
Of course this type of behaviour is good only for animals and crappy monsters.
A human enemy should also try to dodge any ranged weapons.
To dodge or strafe modify the "facing.X" or "facing.Z" values ("facing" is the vector in the code).
Also, keep in mind that Irrlicht does NOT time events, so you must do it manually.
A good & simple example is Emil_halim's CTimer class for BCX.

Here's its adaptation for C++:

CTime.cpp

Code: Select all

#include <irrlicht.h>

using namespace irr;
using namespace core;

static u32 _Basetime;
void TimeInit(IrrlichtDevice* device)
 {
     _Basetime = device->getTimer()->getTime();
 }
class TTime
 {
    
       u32  startTime;
       u32  endTime;
       bool Active;
    public:
       TTime(u32 start,u32 duration)
        {
           startTime  = start + _Basetime ;
           endTime    = startTime + duration;
           Active     = true;
        }
        
       ~TTime () {}
       
       bool CheckTime(IrrlichtDevice* device)
        {
            if (!Active)return false;
            u32 tick  = device->getTimer()->getTime();
            if (tick > startTime && tick < endTime)
               return true;
            else
               return false;
        }
        
       bool CheckStartTime(IrrlichtDevice* device)
        {
            if (!Active)return false;
            u32 tick  = device->getTimer()->getTime();
            if (tick > startTime)
               return true;
            else
               return false;
        } 
         
       bool CheckEndTime(IrrlichtDevice* device)
        {
            if (!Active)return false;
            u32 tick  = device->getTimer()->getTime();
            if (tick > endTime)
               return true;
            else
               return false;
        } 
        
       void UpdateTime(u32 start,u32 duration)
        {
           startTime  = start + _Basetime;
           endTime    = startTime + duration;
        }   
        
       void Enable()
        {
             Active     = true;
        }
        
       void Disable()
        {
             Active     = false; 
        } 
        
       void setAfter(TTime& other ,u32 period)
        {
             startTime  = other.startTime + period;
             endTime    = other.endTime + period;
        }         
        
       void setAS(TTime& other)
        {
             startTime  = other.startTime;
             endTime    = other.endTime;
        }            
};
Bye. See u all later. :) :D
bish
Posts: 3
Joined: Sat May 28, 2005 11:49 am
Location: United Kingdom
Contact:

Post by bish »

Thx, there are some realy usefull functions there. :D
Xico
Posts: 30
Joined: Sun Jun 05, 2005 5:08 pm
Location: Buenos Aires, Argentina
Contact:

Using modulus

Post by Xico »

Hi, instead of:

Code: Select all

   if(angle.X < 0) angle.X += 360;
   if(angle.X >= 360) angle.X -= 360; 
you can use modulus to contrain in between 0 and 359:

Code: Select all

angle.X = angle.X % 359; 
or:

Code: Select all

angle.X = (angle.X-90) % 359; 
hearsedriver
Posts: 81
Joined: Fri Aug 22, 2003 12:06 pm
Location: Germany
Contact:

Re: Using modulus

Post by hearsedriver »

Xico wrote:you can use modulus to contrain in between 0 and 359:

Code: Select all

angle.X = angle.X % 359; 
or:

Code: Select all

angle.X = (angle.X-90) % 359; 
It should read "% 360". Modulo is the rest of the division, hence to get values between 0 and 359 you need the rest of the division by 360. Anyway, we're talking about floats here, so the right method is fmodf.

~hearsedriver();
matthias gall, lead programmer at sechsta sinn - email matthias@sechsta-sinn.de
Post Reply