
Extending CCSprite
Dr. Nicholas Fringe's enemies are an army of UFOs controlled by very intelligent extraterrestrial beings trying to wipe out all of mankind. That's why we're going to create them as a separate class that will derive from CCSprite
, where we will define its evolved behavior.
Some developers prefer to derive this kind of class from CCNode
and include a CCSprite
instance as it offers more potential, but for the moment we are going to keep it simple and just extend CCSprite
.
First of all, let's create the new class:
- Right-click on the Classes group in the project navigator and select New File….
- Click on iOS | cocos2d v3.x and choose to create the new file from the
CCNode
class template. - Type
CCSprite
in the available field and click on Next. - Call the file as
UFO
and be sure that the Classes folder is selected before clicking on Create.
Then replace the contents of UFO.h
with the following block of code:
#import <Foundation/Foundation.h> #import "cocos2d.h" @interface UFO : CCSprite { } // Declare property for number of hits @property (readwrite, nonatomic) int numHits; // Declare method to init UFOs -(id) initWithHits:(int)hits; @end
Replace the contents of UFO.m
with these lines:
#import "UFO.h" @implementation UFO // Implement initWithHits -(id) initWithHits:(int)hits{ // Initialize UFO sprite specifying an image self = [super initWithImageNamed:@"ufo_green.png"]; if (!self) return(nil); // Initialize number of hits _numHits = hits; return self; } @end
We declared a readwrite
integer property to keep the control of the number of hits a UFO can receive before exploding, and as you can see, we're taking advantage of the auto-synthesized properties feature that will help us minimize coding.
We also declared a custom init
method so we can assign initial values to each instance of the UFO
class; in our case, we just want to specify the image to create the UFO
object and the initial number of hits. Notice that we are calling the parent's initWithImageNamed
method because if we don't, our class won't be properly initialized.
To conclude with the UFO
class, you just need to add the corresponding image to the project:
- In the project navigator, right-click on the Resources group and select Add Files to "ExplosionsAndUFOs"….
- In the Resources file, you will find
ufo_green.png
, so select it and click on Add.
Now let's put some enemies in the scene so Dr. Fringe can begin saving our planet. First, declare these two private instance variables by adding them in GameScene.m
, after CCParticleSystem *_fire;
:
// Declare an array of UFOs NSMutableArray *_arrayUFOs; // Max number of UFOs in scene int _numUFOs;
Initialize them by adding the following lines in the init
method, just before return self;
:
// Initialize the array of UFOs _numUFOs = 3; _arrayUFOs = [NSMutableArray arrayWithCapacity:_numUFOs];
We initialized the UFOs array to contain three objects for the moment, but things will get harder for our scientist later.
Import the UFO
class to GameScene.m
by adding the following line at the top of the class just after #import "GameScene.h"
:
#import "UFO.h"
The next step is adding the enemies to the scene, and we're going to achieve this by scheduling a spawn method where we will define their behavior. In the init
method, add the following line after the lines you added earlier:
// Schedule the UFOs spawn method [self schedule:@selector(spawnUFO) interval:5.0f];
Implement the spawnUFO
method by adding the following:
-(void)spawnUFO { if ([_arrayUFOs count] < _numUFOs){ // Create a new UFO UFO *ufo = [[UFO alloc] initWithHits:3]; // Set inital UFO position ufo.position = CGPointMake(ufo.contentSize.width / 2, _screenSize.height + ufo.contentSize.height / 2); // Adding the new UFO to the array [_arrayUFOs addObject:ufo]; // Adding the UFO to the scene [self addChild:ufo]; //Creating movement actions CCActionMoveTo *actionMoveInitialPosition = [CCActionMoveTo actionWithDuration:0.6 position:CGPointMake(ufo.position.x, _screenSize.height - ufo.contentSize.height / 2)]; CCActionMoveTo *actionMoveRight1 = [CCActionMoveTo actionWithDuration:0.3 position:CGPointMake(_screenSize.width - ufo.contentSize.width / 2, _screenSize.height - ufo.contentSize.height / 2)]; CCActionMoveTo *actionMoveDownLeft = [CCActionMoveTo actionWithDuration:0.3 position:CGPointMake(ufo.contentSize.width / 2, _screenSize.height - 2 * ufo.contentSize.height)]; CCActionMoveTo *actionMoveRight2 = [CCActionMoveTo actionWithDuration:0.6 position:CGPointMake(_screenSize.width - ufo.contentSize.width / 2, _screenSize.height - 2 * ufo.contentSize.height)]; CCActionSequence *ufoSequence = [CCActionSequence actionWithArray:@[actionMoveInitialPosition, actionMoveRight1, actionMoveDownLeft, actionMoveRight2]]; // Repeat movement infinitely CCActionRepeatForever *ufoLoop = [CCActionRepeatForever actionWithAction:ufoSequence]; // Run the UFO movement [ufo runAction:ufoLoop]; } }
I apologize for the big block of code, but don't worry, it's easy to understand what we've just done.
Once we get the array ready to receive objects, we schedule a method that will take care of the UFO
spawn. I decided to leave 5 seconds between spawns to give the scientist a chance.
The first thing we do in spawnUFO
is to check whether arrayUFOs
is already filled with the maximum amount of objects, and if it's not, then we proceed with initializing the object by specifying the number of hits we want it to support before exploding (3
in this case). Then we place UFO
off the top of the screen and we add it to both the array and the scene.
As we want the UFO objects to have some intelligence and be harder to kill, I decided to define a loop movement that consists of a four-movement sequence:
actionMoveInitialPosition
: This traces a path from outside the screen to its initial position, placed at the top-left corner of the screenactionMoveRight1
: This moves the enemy to the right side of the screen, keeping the samey
value, and performs a lateral displacementactionMoveDownLeft
: This places the object on the left side of the screen again but a little lower than the initial positionactionMoveRight2
: This performs another lateral displacement, keeping they
value
As you can see, the duration of the movements is different because I wanted to make the movement a little unpredictable.
The last line just runs the CCActionRepeatForever
action, similar to what we did in the particle effect section.
Now you can run the game and see how the spaceships draw a vertiginous zigzag pattern that is almost impossible to predict (I am joking).

Let's extend the UFO
class a little more so we can create instances of this class with a higher number of hits and a different texture image. First of all, add a couple more images for the new types of spaceship:
- Right-click on the Resources group and select Add Files to "ExplosionsAndUFOs"….
- In the
Resources
folder, you will findufo_red.png
andufo_purple.png
. Select them and click on Add.
To achieve this, we need to add a new property to UFO.h
, so add the following property:
// Declare property for type of UFO @property (readonly, nonatomic) UFOTypes ufoType;
However, you will need to define UFOTypes
too. We will create an enumerated type for the different kinds of spaceships, so add the following lines just above the interface declaration @interface UFO : CCSprite {
:
typedef enum { typeUFOGreen = 0, typeUFORed, typeUFOPurple } UFOTypes;
Once we've specified the value for the first enumerated component (typeUFOGreen = 0
), we don't need to specify the rest as it will follow an enumerated sequence.
Declare a new initializer method with the following line in UFO.h
:
// Declare method to init UFOs with type -(id) initWithType:(UFOTypes)type;
Implement this method in UFO.m
with the following lines:
// Implement initWithType -(id) initWithType:(UFOTypes)type { // Set the ufo type _ufoType = type; NSString *textureName; int numHits; switch (_ufoType) { case typeUFOGreen: // Assign textureName and numHits values textureName = @"ufo_green.png"; numHits = 3; break; case typeUFORed: // Assign textureName and numHits values textureName = @"ufo_red.png"; numHits = 5; break; case typeUFOPurple: // Assign textureName and numHits values textureName = @"ufo_purple.png"; numHits = 7; break; default: break; } // Initialize UFO sprite specifying texture image self = [super initWithImageNamed:textureName]; if (!self) return(nil); // Initialize number of hits _numHits = numHits; return self; }
This method will receive the type as an input argument and depending on it, the method will create one kind of UFO or another. Notice that we're calling the [super initWithImageNamed:]
method to properly create our instance.
Then, find the old initialization:
UFO *ufo = [[UFO alloc] initWithHits:3];
Replace it with the new initializer method:
int type = arc4random_uniform(3); UFO *ufo = [[UFO alloc] initWithType:type];
We are passing a random number between 0 and 2 to create the UFO objects in an unpredictable manner. Come on, run the game and be scared of the almost indestructible spaceships with unknown technology!
Shooting some lasers
Did you think that the backpack-reactor was Dr. Fringe's only invention? You were wrong; he needs some kind of weapon to face the aliens and fortunately, he has developed some high tech lasers. So let's allow him to shoot them when we touch the screen.
- Right-click on the Resources group and select Add Files to "ExplosionsAndUFOs"….
- In the
Resources
folder, you will findlaser_green.png
, so select it and click on Add.
We're going to declare an array of lasers so we can keep control of them, so add these two private instance variables after int _numUFOs;
:
// Declare array of green lasers NSMutableArray *_arrayLaserGreen; // Max number of lasers in scene int _numLaserGreen;
Initialize the variables in the init
method by adding the following code before return self;
:
// Initialize the array of green lasers _numLaserGreen = 5; _arrayLaserGreen = [NSMutableArray arrayWithCapacity:_numLaserGreen];
Dr. Nicholas Fringe's invention is still in the beta phase, that's why he can't shoot more than five laser beams at the same time. We need to enable touch interaction, something that we already know how to do. In the init
method, add the following line at the end before return self;
:
self.userInteractionEnabled = YES;
Implement touchBegan
by adding these lines to GameScene.m
:
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event { if ([_arrayLaserGreen count] < _numLaserGreen){ // Create green laser and setting its position CCSprite *laserGreen = [CCSprite spriteWithImageNamed:@"laser_green.png"]; laserGreen.position = CGPointMake(_scientist.position.x + _scientist.contentSize.width / 4, _scientist.position.y + _scientist.contentSize.height / 2); // Add laser to array of lasers [_arrayLaserGreen addObject:laserGreen]; // Add the laser to the scene [self addChild:laserGreen]; // Declare laser speed float laserSpeed = 400.0; // Calculate laser's final position CGPoint nextPosition = CGPointMake(laserGreen.position.x, _screenSize.height + laserGreen.contentSize.height / 2); // Calculate duration float laserDuration = ccpDistance(nextPosition, laserGreen.position) / laserSpeed; // Move laser sprite out of the screen CCActionMoveTo *actionLaserGreen = [CCActionMoveTo actionWithDuration:laserDuration position:nextPosition]; // Action to be executed when the laser reaches its final position CCActionCallBlock *callDidMove = [CCActionCallBlock actionWithBlock:^{ // Remove laser from array and scene [_arrayLaserGreen removeObject:laserGreen]; [self removeChild:laserGreen]; }]; CCActionSequence *sequenceLaserGreen = [CCActionSequence actionWithArray:@[actionLaserGreen, callDidMove]]; [laserGreen runAction:sequenceLaserGreen]; } }
The very first thing we're doing is checking whether we have shot the maximum number of laser beams. If not, we create a new one, setting its position by the laser gun and adding it to the scene and the array of green lasers.
Each laser will trace a vertical path from its initial shooting position to the top, off the screen. That's why we're calculating the final position as the same x axis value and _screenSize.height + laserGreen.contentSize.height / 2
on the y axis. This is the same approach we take every time we want to place some sprite off the screen, so it's nothing new to us.
As we want the laser to always move at the same speed, we specify its velocity to calculate the duration of the movement and we make this calculation in the same way we did in the previous chapter: divide the distance to be covered by the laser's speed.
Then we create a CCActionMoveTo
instance with the calculated direction and the final position. Once the move action ends, we want the laser to disappear from both the scene and the array, so we implement a CCActionCallBlock
action where we place this logic.
As the last instruction, we build a sequence with the movement action and the action block and run it. Build and run your game and look what we've just done.

When UFOs collide
Despite being in the beta phase, Dr. Fringe's laser beams can destroy the alien's spaceships, so let's implement collision detection for this purpose.
When a laser beam hits a UFO, on one hand the spaceship's number of hits will decrease for one unit, and on the other hand, the laser will disappear from the scene. The same will happen to the UFO when its number of hits is 0
.
First let's declare and implement an instance of the UFO
method to check whether its number of hits has reached 0 or not. In UFO.h
, add the following line just after the initWithHits
method:
// Declare check method -(BOOL) checkNumHits;
Then in UFO.m
, implement checkNumHits
by adding these lines at the bottom of the file, before the @end
clause:
// Implement checkNumHits -(BOOL) checkNumHits{ if(_numHits == 0) { // Remove UFO from scene and return TRUE [self removeFromParent]; return TRUE; } return FALSE; }
This method will look at _numHits
. If the _numHits
instance variable equals 0, and in that case, it will remove the object from its parent (the scene) and return TRUE
. For any other _numHits
instance variable, it will return FALSE
.
Back to GameScene.m
. We are going to develop a method to wrap collision detection tasks. In the update
method, add the following lines at the end:
// Collision detection [self detectCollisions];
Implement the method by pasting the following lines:
-(void)detectCollisions { CCSprite *laserGreen; // For each UFO on the scene for(UFO *ufo in _arrayUFOs) { // For each laser beam shot for (laserGreen in _arrayLaserGreen){ // Detect laserGreen-ufo collision if (CGRectIntersectsRect(ufo.boundingBox, laserGreen.boundingBox)) { CCLOG(@"COLLISION DETECTED"); // Decrease UFO's number of hits ufo.numHits--; // Check if numHits is 0 if ([ufo checkNumHits]) { CCLOG(@"UFO DESTROYED"); } } } } }
We're iterating the green lasers and the spaceships array and trying to detect a collision between their rectangles. When a collision happens, we decrease the UFO number of hits and we check whether it should be destroyed. In that case, we remove the object from the scene. However, at the moment it won't work properly. If you run the object, you will realize that's because once we destroy the first _numUFOs
spaceships, no more are spawned.
This is due to the fact that we aren't removing the destroyed spaceships from the array. You may be thinking that making an object disappear from the scene has a straightforward solution: remove the object from the array and the child from the scene, but it's not that simple.
If you want to try it, add this line after CCLOG(@"UFO DESTROYED");
:
[_arrayUFOs removeObject:ufo];
Run the project again and shoot until you destroy one UFO.
There you have it, the app crashed due to an uncaught exception:
Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x17805b900> was mutated while being enumerated.' ***
If you pay attention to the log message, it makes sense. It's warning us that we're modifying the array while it's being enumerated (iterated) and this is a forbidden action.
So, how can we deal with it? The approach followed by game developers is to create a separate array to store the objects we want to delete and then proceed to remove them as soon as the loop ends.
As we will have the same problem with laser beams if we try to remove them when a collision happens, we will apply a similar approach.
First of all, delete the line to remove the spaceship from arrayUFOs
and declare two new arrays after int _numLaserGreen;
:
// Array of removable laser beams NSMutableArray *_lasersGreenToRemove; // Array of removable UFOs NSMutableArray *_ufosToRemove;
Initialize them at the end of the init
method, just after self.userInteractionEnabled = YES;
:
// Initialize removable objects arrays _lasersGreenToRemove = [NSMutableArray array]; _ufosToRemove = [NSMutableArray array];
Then, in detectCollisions
, find the following line:
CCLOG(@"COLLISION DETECTED");
Replace it with:
// Stopping laser beam actions [laserGreen stopAllActions]; // Adding the object to the removable objects array [_lasersGreenToRemove addObject:laserGreen]; // Remove the laser from the scene [self removeChild:laserGreen];
Then find the line:
CCLOG(@"UFO DESTROYED");
Replace it with the following ones:
// Stopping ufo actions [ufo stopAllActions]; // Adding the object to the removable objects array [_ufosToRemove addObject:ufo];
Finally, add these lines after the _arrayLaserGreen
loop:
for (CCSprite *laserGreen in _arrayLaserGreen){ . . . } // Remove objects from array [_arrayLaserGreen removeObjectsInArray:_lasersGreenToRemove];
Add this one after the _arrayUFOs
loop:
for(UFO *ufo in _arrayUFOs) { . . . } // Remove objects from array [_arrayUFOs removeObjectsInArray:_ufosToRemove];
Remember that we aren't removing UFOs from the scene in this method because we're already performing this task inside checkNumHits
.
If you run the game now, you will find that each spaceship's spawn/destruction and the laser beam's shooting and disappearing functions are working correctly.