Patrol enemy behavior

In patrol enemy behavior, the enemy AI will move between two or more points. When the player comes close to the enemy, the enemy will attack the player, and if the player moves beyond the zone of the enemy or dies, then the enemy will continue to go on with their patrol duty.

Getting ready

To create an AI, we will create a class called PatrolAI, along with a bullet class. In the PatrolAI class, we will add the movement behavior, which will check the distance between the player and enemy. If the player comes close to the enemy, the enemy is required to turn around and start shooting at the player.

In the bullet class, we will add a movement function and delete the functionality for it.

Make sure to import the enemy and hero images as we will need them for this project. For this chapter, I will create a new project, so if you want to do the same, make sure you change the settings of your project accordingly.

How to do it…

First, let's create the PatrolAI class.

In the interface file, we will add the following:

#import "CCSprite.h"

@interface PatrolAI : CCSprite{

  CCSprite* _hero;
  CGPoint startPos;
  int xDirection;
  float xSpeed;
  float xAmplitude;

  int shootCounter;

}

-(id)initWithFilename:(NSString *) filename :(CCSprite*) hero : (CGPoint) position;

@end

The PatrolAI class inherits from the sprite class. We will create instance variables for the hero as we will need the position of the hero at all times; the behavior of the enemy is dependent on it. We also need the position of the starting point, the direction in which the enemy should be moving, and the speed and maximum distance from the start position that the enemy can patrol before they return.

The shootCounter counter is one that makes sure that bullets are shot after a certain interval instead of immediately.

The init function takes in the name of the filename, a reference to the hero sprite, and the starting point of the enemy.

Now, let's take a look at the implementation file, as follows:

#import "PatrolAI.h"
#import "Bullet.h"

@implementation PatrolAI

-(id)initWithFilename:(NSString *) filename :(CCSprite*) hero : (CGPoint) position
{
  if (self = [super initWithImageNamed:filename]) {

    _hero = hero;
    xDirection = 1.0;
    xSpeed = 5.0f;
    xAmplitude = 100.0f;

    [self setPosition:position];

    startPos = self.position;

    shootCounter = 0;

  }



  return self;
}

We first will import the PatrolAI.h and Bullet.h files. We will create the bullet class once we are done with this class.

In the init function, we will assign the hero reference to the _hero variable. We will then set values for direction, speed, and amplitude. We will set the position of the enemy sprite to the position that was passed in and assign startPos, the current position of the enemy sprite. Finally, we will set the shootCounter value to 0. The following is the code we will use:

- (void)update:(CCTime)delta {

  shootCounter -- ;
  if(shootCounter <= 0)
    shootCounter = 0;

    self.position = ccpAdd(self.position , CGPointMake(xSpeed * xDirection, 0));


  if(self.position.x > startPos.x + xAmplitude)
    xDirection = -1.0;
  else if(self.position.x < startPos.x - xAmplitude)
    xDirection = 1.0;

    float dist = ccpDistance(self.position, _hero.position);

  if(dist < 100){

    if(self.position.x - _hero.position.x < 0)
      self.scaleX = -1.0;
    else if(self.position.x - _hero.position.x > 0)
      self.scaleX = 1.0;

    if(shootCounter == 0){

      shootCounter = 30;

      [self shoot];
    }

  }else{

    if(xDirection > 0)
      self.scaleX = -1.0;
    else
      self.scaleX = 1.0;
  }


}

In the update function, we will decrement the shoot counter by one every frame. Then, if the value of shootCounter goes to less than zero, we will set the value of the counter to zero.

After this, we will set the position of the player by adding the current position in the x direction to the product of the speed and direction values. As we don't want the enemy to move in the y direction, the value is unchanged in the y direction.

We will then check whether the current position in the positive x direction is more than the sum of the starting x position and amplitude. If it is greater, then we will change the direction value to -1 so that the enemy starts moving in the opposite direction.

The same needs to then be checked for the opposite direction. If the current x position is less than the starting x position minus the amplitude, then the value of direction is set to 1 so that the enemy starts moving the positive x direction.

Next, we will get to the core of the enemy behavior logic. We will first get the distance between the player and enemy. If the distance is less than 100, then we will check whether the player is behind the enemy or in front. If the player is behind, then we will flip the scale so that the enemy faces the player.

Then, we will check whether the counter is less than 0. If it is, then we will call the shoot function and set the counter to 30.

If the hero is not close to the enemy, then the direction of the enemy will be checked, the x scale value will be flipped, and the enemy will behave normally. Take a look at the following script:

-(void)shoot{

  Bullet* bullet = [[Bullet alloc]initWithFilename:@"bullet.png"
    Hero:_hero
    pos:self.position
    Direction:self.scaleX * -1.0];

    [[self parent]addChild:bullet];

}

@end

In the shoot function, we will create a new bullet by passing in the image of the bullet, the position of the hero, and the position and direction of the enemy. We will then add the bullet to the parent class of the enemy, which is the MainScene class.

Let's now take a look at the bullet class.

The interface file is as follows:

#import "CCSprite.h"

@interface Bullet : CCSprite{

  CCSprite* _hero;
  CGPoint startPos;
  int xDirection;
  float xSpeed;

}

-(id)initWithFilename:(NSString *) filename Hero:(CCSprite*) hero pos: (CGPoint) position Direction: (int)Direction;

@end

It is very similar to the PatrolAI class except that we don't have an amplitude variable that we have to worry about.

The following is the implementation class:

#import "Bullet.h"

@implementation Bullet

-(id)initWithFilename:(NSString *) filename Hero:(CCSprite*) hero pos: (CGPoint) position Direction: (int)Direction
{
  if (self = [super initWithImageNamed:filename]) {

    _hero = hero;
    xDirection = Direction;
    xSpeed = 10.0f;

    [self setPosition:position];

    startPos = self.position;
  }

  return self;
}

In the init file, as always, we will initialize the variables. We will assign the hero to a local hero variable and set the direction to the current xDirection value, the speed to 10, and the position of the sprite to the position passed in while also assigning it to the startPos variable, as follows:

- (void)update:(CCTime)delta {

  self.position = ccpAdd(self.position , CGPointMake(xSpeed * xDirection, 0));

  float distX = ccpDistance(startPos, self.position);

  if(CGRectIntersectsRect(self.boundingBox, _hero.boundingBox) && self != NULL){

    [self removeFromParent];
  }

  if(distX > 100 && self != NULL){

    [self removeFromParent];
  }

}


@end

In the update function, we will first set the position of the bullet, similar to what we did for the enemy.

Next, instead of the checking for the distance between the position of the hero and bullet, we will take a look at the distance between the position of the bullet and its starting point.

Next, we will check whether the bullet intersected with the hero sprite. If it did intersect, then we will remove the bullet from the parent class, where we added the bullet.

Finally, we will check whether the distance calculated earlier was more than 100. If this is true, then we will still remove the bullet from the parent class.

How it works…

Now, to see how this all works, let's add the following to the MainScene.h file:

@interface MainScene : CCNode{

  CGSize winSize;
  CCSprite* hero;

  CGPoint touchLocation;
  bool touched;  

}

@end

Next, in the MainScene.m file, we will add the following:

#import "MainScene.h"
#import "PatrolAI.h"

@implementation MainScene

+(CCScene*)scene{

  return[[self alloc]init];
}

- (void)onEnter{
  [super onEnter];
  self.userInteractionEnabled = YES;
}

- (void)onExit{
  [super onExit];
  self.userInteractionEnabled = NO;
}

-(id)init{

  if(self = [super init]){

    winSize = [[CCDirector sharedDirector]viewSize];

    self.contentSize = winSize;
    touched = false;

    CCSprite* backgroundImage = [CCSprite spriteWithImageNamed:@"Bg.png"];

    backgroundImage.position = CGPointMake(winSize.width/2,
      winSize.height/2);
    [self addChild:backgroundImage];

    hero = [CCSprite spriteWithImageNamed:@"hero.png"];
    hero.position = CGPointMake(winSize.width * .125, winSize.height/4);
    [self addChild: hero];

    CGPoint startPos = CGPointMake(winSize.width * .5 , winSize.height/4);
    PatrolAI* pEnemy = [[PatrolAI alloc] initWithFilename:@"enemy.png" :hero :startPos];
    [self addChild: pEnemy];

  }

  return self;
}

- (void)touchBegan:(CCTouch *)touch withEvent:(CCTouchEvent *)event{
  touchLocation = [touch locationInNode:self];
  touched = true;

}

- (void)touchEnded:(CCTouch *)touch withEvent:(CCTouchEvent *)event{

  touchLocation = [touch locationInNode:self];
  touched = false;

}


- (void)update:(CCTime)delta {

  if(touched){

    if(touchLocation.x > winSize.width/2){

      hero.position = ccpAdd(hero.position,CGPointMake(5, 0));
      hero.scaleX = 1.0;

    }else if (touchLocation.x < winSize.width/2){

      hero.position = ccpAdd(hero.position,CGPointMake(-5, 0));
      hero.scaleX = -1.0;
    }
  }
}

@end

We just need to add the background, hero, and PatrolAI values to the init function, and the PatrolAI behavior will handle the rest.

You should be well acquainted with the rest of the code. We just enabled and disabled the touches in the class, touchBegan and touchEnded, and the update function made the hero move according to the touches, and that is all!

You can add additional functionality to the code, such as scoring and game loop, if you want to.

How it works…
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.228.138