Hellow. 🙂
Today we will continue our game, checking some more collisions and adding a Score System to our game. 😉
Before to start, if you’re following this tutorial’s series from the beginning (you may check the preview tutorial HERE)
8/9/2013 Last Update: Added the Shoot Button
I made some changes to put a little more challenge for our game:
Changes
I changed the size of the “spaceship” so, “Save As” the image bellow and replace your “spaceship.png” in your “Resources/” from your project.
(And if you are new here, yes, this nyancat is our “spaceship”)
“Can’t I scale the Sprite with code? :)”
Sure you can, the CGSprite have the “setScale
“, with this you can change the scale of your sprite, but don’t use it if you can do it outside your code. In my next tutorial I will be looking to show some nice methods. 🙂
Now Let’s begin the tutorial.
Let’s Shoot!
First thing Fof this tutorial I will be adding a “Shoot Sprite”, some kind of “bullet” or “lasershot” for our cat.
Save As the image bellow (and if you don’t see image bellow because it is too small, right-click HERE and Save As). 🙂
name it like: “shoot.png“.
Now add one more image, our Shoot Button, when you press it with your finger, our shoot shall be fired. Does the same thing to the image button bellow (save it) or click here. 😉
name it to: “buttonshoot.png“.
Now go to your XCode Project and add both images to “Resources/” Group.
Certainly the way I will do it is not the more adequate, but will be very easy to understand. 🙂
Inside of Game.h
I will declare some new variables:
Joystick* joystick;
CCSprite* player;
CGRect playerRect;
CGRect meteorRect;
CCSprite* shoot[3];
bool shootEffect[3];
CGRect shootRect;
CCSprite* meteor[10];
bool meteorEffect[10];
int countShoot;
int countMeteor;
int timer;
int score;
CCLabelTTF *scorepoint;
obs: Don’t forget remove your CCSprite meteor; from Game.h, our meteor array will take his place.
Your Game.h shall look like:
The “shoot” is a CGSprite in an Array, will be our “bullet”, the “countShoot” is a simple “integer” that will count our bullets and the last one is “canShoot” a boolean that will say if we can shoot or not.
Let’s head to our Game.m
We need to “init” our new variable, so let’s do it inside of “-(id) init{}”:
shoot[0] = [CCSprite spriteWithFile:@”shoot.png”];
shoot[0].position=ccp(20,300);
shootEffect[0]=true;
[self addChild:shoot[0] z:199];
shoot[1] = [CCSprite spriteWithFile:@”shoot.png”];
shoot[1].position=ccp(30,300);
shootEffect[1]=true;
[self addChild:shoot[1] z:199];
shoot[2] = [CCSprite spriteWithFile:@”shoot.png”];
shoot[2].position=ccp(40,300);
shootEffect[2]=true;
[self addChild:shoot[2] z:199];
countShoot=0;score=0;
Yes, if you notice, we have a limit of 3 shoot, each shoot will take 4 second to reload, so now let’s do it.
I’m creating a function called “addShoot” and inside of this function it will move the bullet from the player’s spaceship position and send this bullet on straight horizontal position to the right.
-(void) addShoot:(id) sender{
if (countShoot<3){
shoot[countShoot].position = player.position;id actionShoot = [CCMoveBy actionWithDuration:4.0
position:ccpMult(ccpNormalize(ccpSub(ccp(player.position.x+100,player.position.y),player.position)), 1000)];
id actionShootDone = [CCCallFuncND actionWithTarget:self
selector:@selector(enemyAtkFinished:data:) data:(void *)countShoot];
[shoot[countShoot] runAction:[CCSequence actions:actionShoot,actionShootDone, nil]];
countShoot++;}
}
The CCCallFuncND will call a function with an argument, this argument will be our shoot counter “countShoot” and the CCSequence will make this call happens after the actionShoot (the action that will make our “bullet” moves). So let’s create our “enemyAtkFinished”:
-(void)enemyAtkFinished:(id) sender data:(void *)shootNumber{
countShoot–;
shoot[(int)shootNumber].opacity=255;
shootEffect[(int)shootNumber]=true;
shoot[(int)shootNumber].position=ccp(((int)shootNumber*10)+10,300);
}
Now let’s put our shoot button with the CCMenuItem, for this, let’s go inside of “-(id) init{}” and add:
CCMenuItem *startMenuItem = [CCMenuItemImage itemFromNormalImage:@”buttonshoot.png” selectedImage:@”buttonshoot.png” target:self selector:@selector(addShoot:)];
startMenuItem.position = ccp(450, 60);
CCMenu *startMenu = [CCMenu menuWithItems:startMenuItem, nil];
startMenu.position = CGPointZero;
[self addChild:startMenu];
You may change the position inside of the ccp as you need, just remember, the CCMenu shall ends with a “nil”, if something goes wrong, double check the image name of the button “buttonshoot.png” and if the method addShoot is everything right. 😉
If you wish to run the code, you will probably get some alerts related to the meteor variable, it now needs to be an array, you can rename all meteor in Game.m to meteor[0] to run the code.
Now our Spaceship can shoot. 🙂
More Meteors!
Let’s make our game a little more challenger before proceed to the next step.
We will randomized this meteor and multiply them and depending on game’s time, the meteors will increase the speed.
Yet inside of Game.m, and inside of “-(id) init{}” add this “for” to fill our meteor array.
for(int i=0; i<10;i++){
meteor[i] = [CCSprite spriteWithFile:@”meteor.png”];
meteorEffect[i]=true;
[self addChild:meteor[i] z:198];}
score=0
timer=0;countMeteor=0;
[self schedule:@selector(meteorTick:) interval:1.0f/60.0f];
Don’t forget to remove the [self sendMeteor]; from the init, we will change this method a little.
Honestly the most usual way to use array in Objective C is with NSMutableArrays, but I think the traditional array it most understandable and for this game that will be enough. If you see any problem with that, post a comment. 🙂
Now with an array of meteors Let’s randomized them and speed them up with a “timer”.
For this, outside of the init, I will create a new method “meteorTick”, it will work just like our “tick” but for the meteors.
-(void) meteorTick: (ccTime) dt{
timer++;
CCLOG(@”timer: %i”,timer);
int result=timer % 10;
if(result==0){
float randommeteor=arc4random() % 10000;
if (randommeteor>9500-(timer/100)){
[self sendMeteor];
}
}
}
Well each 10 mseconds the arc4random() will random a number from 0 to 9999, and if the value is up to 9500, a meteor will come. 🙂
But we need yet a new “sendMeteor”, so delete the one you have there and add this one:
-(void) sendMeteor{
if(countMeteor<10){
meteor[countMeteor].opacity=255;
meteorEffect[countMeteor]=true;
float randomMeteorHeight=arc4random() % 320;
meteor[countMeteor].position = ccp(800, randomMeteorHeight);id actionGoMeteor = [CCMoveBy actionWithDuration:(6.0-timer/1000)
position:ccpMult(ccpNormalize(ccpSub(ccp(meteor[countMeteor].position.x-100,meteor[countMeteor].position.y),meteor[countMeteor].position)), 1000)];
id actionMeteorDone = [CCCallFuncN actionWithTarget:self
selector:@selector(meteorDone:)];
[meteor[countMeteor] runAction:[CCSequence actions:actionGoMeteor,actionMeteorDone, nil]];countMeteor++;
}
}
-(void) meteorDone: (id) sender{
countMeteor–;
}
For last here, we will need change the collision and adapt it to the meteor[array], change the “-(void) tick: (ccTime) dt{}” to:
-(void) tick: (ccTime) dt{
playerRect=[self rectPlayer];
for(int j=0;j<10;j++){
meteorRect=[self rectMeteor:j];
if(CGRectIntersectsRect(playerRect,meteorRect) && meteorEffect[j]==true)
{
[[CCDirector sharedDirector]
replaceScene:
[CCTransitionFade
transitionWithDuration:1
scene:[GameOver node]
]];
}
}if (joystick.velocity.x!=0 || joystick.velocity.y!=0 ){
player.position=ccp(player.position.x+100*joystick.velocity.x*dt,player.position.y+100*joystick.velocity.y*dt);
}
}
Oh! and don’t forget to change the method rectMeteor to array like this:
-(CGRect) rectMeteor:(int) array
{
return CGRectMake(meteor[array].position.x – (meteor[array].contentSize.width/2),
meteor[array].position.y – (meteor[array].contentSize.height/2),
meteor[array].contentSize.width, meteor[array].contentSize.height);
}
Now we have a true meteor evasion game. and if a meteor hit our “spaceshipt” the GameOver Screen pop up to us. Let’s do what the tittle of this post “More Simple Sprite Collision” say, yeah, now we gonna mae a collision between the “bullet” and the “meteor”.
Yet in Game.m add the method rectShoot, just like rectMeteor:
-(CGRect) rectShoot:(int) array
{
return CGRectMake(shoot[array].position.x – (shoot[array].contentSize.width/2),
shoot[array].position.y – (shoot[array].contentSize.height/2),
shoot[array].contentSize.width, shoot[array].contentSize.height);
}
Go back to “-(void) tick: (ccTime) dt{” most precisally inside of “for(int j=0;j<10;j++){” and bellow of “meteorRect=[self rectMeteor:j];” add:
for(int w=0;w<3;w++){
shootRect=[self rectShoot:w];
if(CGRectIntersectsRect(shootRect,meteorRect) && shootEffect[w]==true && meteorEffect[j]==true)
{
[self destroyMeteor:w:j];
}}
Well our last method let’s add the “destroyMeteor”:
-(void) destroyMeteor: (int) array1:(int)array2{
shoot[array1].opacity=0;
shootEffect[array1]=false;
meteor[array2].opacity=0;
meteorEffect[array2]=false;
[self changeScore:100];//We will use it on the next step.
}
Score!!!
Well, in our last method we already add a score sum of 100 points each time the spaceship’s bullet hit a meteor, but we don’t have the method “changeScore” yet. So, let’s continue. 🙂
Let’s go to the init of Game.m and add our Score View:
CCLabelTTF *scorelabel = [CCLabelTTF labelWithString:@”Score: ” fontName:@”Arial” fontSize:20];
scorepoint = [CCLabelTTF labelWithString:[NSString stringWithFormat:@”%i”,score] fontName:@”Arial” fontSize:20];
CCMenuItemLabel *labelItem1 = [CCMenuItemLabel itemWithLabel:scorelabel];
CCMenuItemLabel *labelItem2 = [CCMenuItemLabel itemWithLabel:scorepoint];
labelItem2.position = ccp(400, 300);
labelItem1.position = ccp(320, 300);
[labelItem2 setTag:111];
CCMenu *menuScore = [CCMenu menuWithItems:labelItem1,labelItem2, nil];
menuScore.position = CGPointZero;
[self addChild:menuScore];
Here I created 2 labels, one will show “Score” and the other the points. Now, let’s create a new method outside of the “init” to change the value of the score:
-(void) changeScore: (int)sum{
score=score+sum;
[scorepoint setString: [NSString stringWithFormat:@”%i”,score]];
}
NSString stringWithFormat this will convert your int, float or double to String. and now we have the score and a nice “spaceship” game. But the entire tutorial didn’t end yet.
Well guys that is all for this tutorial number 5, your full code in Game.m shall looks like:
That’s all. 🙂
The Next tutorial will be a very simple thing. We will create our “Pause Button” And it is already HERE!. 😉
Any question, or anything wrong, anything right please comment. 🙂
Any Source Codes yet?
I see there are a few times that your code in light gray type is different than your pictures. Although I’m happy that you include the pictures to double check code it is sometimes a hassle.
Also, it looks like you completely missed a section in your writing about the MenuItem shoot button.
Thanks for the awesome tutorials though!
I notice that a lot of the code you have written out in the gray text is different from what you have in the pictures. Thank you for having the pictures btw instead of just leaving it for people to try to figure out. It’s great that we can double check our code to yours with those.
It does seem that you completely missed a section though in the gray text in which you have a CCMenuItem for a shoot button. It’d be great to double check some things before posting, but otherwise thank you for these awesome tutorials!
Thank you, you are totally right, I completely forgot the shoot button, and I will update it. 🙂