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.
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.
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.
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.
3.149.228.138