Cocos2d - Shooting Game Problem - iphone

everyone, I made a shooting game just like 1942(classic shooting game),but here's the question, FPS drops to 5~6 when the fighter shoot out a straight line of bullet,about 7~8 bullets in the screen,Actually, only 8 bullets of player will appear in games, when bullets fly out of the screen they would reset to the fighter's center and become invisible,all bullets are individual sprites, just like:
Sprites * bullet1 = [Sprite spriteWithFile:#"bullet.png"];
........
Sprites * bullet8 = [Sprite spriteWithFile:#"bullet.png"];
Is there any method can store a lots of bullets instead of defining them one by one??
Because enemies may fire too, I can imagine the FPS may get closer to zero.....
Can anyone help me??

you could do something like have a "bullet pool" in there, as you are using cocos, what I would do is to allocate lot's of bullets when I start the game. Check how many bullets do you need approximately, pre allocate those.
When an Entity in your game needs to fire a bullet, it just asks for a bullet to this pool, you give the properties to the "pre allocated" bullet, the bullet appears on the screen, and when it impacts/disappear you then return that bullet to your bullet pool.
if you need some code:
/*You pre-allocate your bullets.*/
for(int i = 0; i < MAX_BULLETS; i++)
{
Bullet *aBullet = [[Bullet alloc] init];
[bulletsArray addObject:aBullet];
[aBullet release];
}
//Then in game when you fire:
Bullet *aBullet = [PoolManager bulletWithSprite:myBulletSprite]; // Where myBulletSprite is PRE allocated and you don't allocate sprites in Game.

You can store the sprites in an array:
NSMutableArray * bulletsBuilder = [[NSArray alloc] init];
for(int i = 0; i < MAX_NUMBER_OF_SPRITES; ++i) {
[bullets addObject:[Sprite spriteWithFile:#"bullet.png"]];
}
NSArray * bullets = [NSArray arrayWithArray:bulletsBuilder];
[bulletsBuilder release];
And access them later using their identifier:
Sprite * spr = [bullets objectAtIndex:spriteIndex];

What's a problem? Use sprite batching! It's kind of special layer that allow you to speed up the rendering of the multiple sprites.
CCSpriteBatchNode* batch = [CCSpriteBatchNode batchNodeWithFile:#"bullet.png"];
Unless you're not Texture Atlas you cannot use different sprites to render them.
Then load one sprite and add to the batch itself as many sprites as you need.
for(;i>10;){[batch addSprite:sprite];}
Add to self.

Core Animation layers are lightweight and should help your performance. You can load the image once and then load the bullets image into the contents of an array of CALayers. Something like this:
// bulletImage is an instance variable
bulletImage = [UIImage imageNamed:#"bullet.png"];
bulletLayers = [[NSMutableArray alloc] init];
for (i = 0; i < 9; ++i)
{
CALayer *bulletLayer = [CALayer layer];
[bulletLayer setBounds:[bulletImage bounds]];
[bulletLayer setPosition:gunBarrelOrigin];
[bulletLayer setContents:(id)[bulletImage CGImage]];
[bulletLayers addObject:bulletLayer];
}
// Use the array of layers.
// ...
You don't specify whether you are using views or layers in your Sprite class, but if you are loading the same image multiple times, you're definitely creating unnecessary overhead.

Related

Sprite Kit loop multi node background

I am trying to loop my background image in SK, the problem is the background is long so I split the image up into tiles that I paste together with code. I want to loop this huge background but I don't know how, the regular methods of looping backgrounds haven't worked because my background is made up of multiple nodes.
This is my method I use to render the nodes together.
- (SKNode *) createBackgroundNode
{
// 1
// Create the node
SKNode *backgroundNode = [SKNode node];
// 2
// Go through images until the entire background is built
for (int nodeCount = 0; nodeCount < 20; nodeCount++) {
// 3
NSString *backgroundImageName = [NSString stringWithFormat:#"Background%02d", nodeCount+1];
SKSpriteNode *node = [SKSpriteNode spriteNodeWithImageNamed:backgroundImageName];
// 4
node.anchorPoint = CGPointMake(0.5f, 0.0f);
node.position = CGPointMake(160.0f, nodeCount*64.0f);
// 5
backgroundNode.name =#"background";
[backgroundNode addChild:node];
}
// 6
// Return the completed background node
return backgroundNode;
}
Then Inside my update method, I use this code to move all the tiles at once by moving the background layers position in my desired direction.
[self enumerateChildNodesWithName:#"background" usingBlock: ^(SKNode *node, BOOL *stop) {
SKSpriteNode *bg = (SKSpriteNode *) node;
bg.position = CGPointMake(0.0f, -((_player.position.y - 200.0f)/10));
}];
The problem is, I can't get the size of the background layer only the position (at least I think I can't) So I have no idea how I can loop all these tiles seamlessly ?
I preferably want to do it all in one class but any helpful suggestions would be greatly appreciated!

Random enemy spawning

I have a game that uses the camera view, and has enemies flying around the screen. You then shoot the enemies using your chosen weapon. At the moment I have 4 UIImageView's, in which the enemies are animated. I am wanting to have multiple instances of each enemy (there are 4) on the screen, and with time more spawn. What is the best way to do this?
This is part of the code to move one enemy using the gyro: (enemyCyborg is the UIImageView)
valueX4 = gyroData.rotationRate.y* 30;
valueY4 = gyroData.rotationRate.x* 40;
int newX4 = (int)(enemyCyborg.center.x +valueY4);
int newY4 = (int)(enemyCyborg.center.y -valueX4);
CGPoint newCenter4 = CGPointMake(newX4, newY4);
enemyCyborg.center = newCenter4;
And they are moving randomly within that:
- (void) moveCyborg {
[UIView animateWithDuration:1.0f animations:^{
int fromNumber = -30;
int toNumber = 60;
int randomNumber = (arc4random()%(toNumber-fromNumber))+fromNumber;
//Move the image view to 100, 100 over 10 seconds.
enemyCyborg.frame = CGRectMake((int)(enemyCyborg.center.x -randomNumber), (int)(enemyCyborg.center.y -randomNumber), enemyCyborg.frame.size.width, enemyCyborg.frame.size.height);
}];
}
This shows the UIImageView again, after it has been hidden from shooting it:
- (void) showCyborg {
enemyCyborg.hidden = NO;
enemyCyborg.center = CGPointMake((arc4random()%SCREEN_HEIGHT),(arc4random()%SCREEN_WIDTH));
enemyCyborg.animationImages = cyborganim;
enemyCyborg.animationDuration = 0.6;
enemyCyborg.animationRepeatCount = 0;
[enemyCyborg startAnimating];
}
You'll want to create objects encapsulating the enemy model and spawn those objects using some kind of enemy generator function. This generator function might be a written as a class function.
Here are some general suggestions:
1) You're using the block-based animation methods. Blocks are a good approach but depending on your performance and the number of enemies and other visual elements you plan to add, you may want to look into using Cocos2d which gives you all sorts of features. Ray Wenderlich has published a great intro to cocos2d tutorial that would get you up and running quickly. Link to that here. Many games for iPhone are written using either cocos2d or Unity.
2) Check in https://gamedev.stackexchange.com/ for further basic examples, if you haven't done so already.

Changing opacity for multiple sprites in Cocos2d

I'm relatively new to iphone programming, and I've been starting out with cocos2d. I was wondering if there was a way to set opacity for multiple sprites at once? I noticed that opacity isn't implemented for CCLayer, and opacity doesn't seem to propagate down to children of class CCSprite.
Is there any better way to do this than to override setOpacity on a custom ccnode and iterate through and set opacities individually? Or perhaps overriding draw and setting the blending mode manually?
Thanks!
You can loop through all the sprites in your scene, check if they are the sprites you want, then set their opacity like this (assuming you set their tag to OPACITY_SPRITE_TAG when you create the sprites)
for(CCSprite* sprite in [self children])
{
if([sprite tag] == OPACITY_SPRITE_TAG)
{
[sprite setOpacity:NEW_OPACITY];
}
}
Couldn't you just increment through an array of sprites that you want to change. Something like:
for(int i = 0; i < [myArray count]; i++){
CCSprite *mySprite = [myArray objectAtIndex:i];
[mySprite setOpacity:100];
}

How would I make an explosion animation on iOS?

I have an iOS game, and when a ball hits a target it explodes.
What would be the best way to animate this explosion?
If you're looking for something simple.
Make a series of images that animate the explosion. Add those to a UIImageView and start the animation. Something like this:
UIImage *image0 = [UIImage imageNamed:#"explosion0.png"];
UIImage *image1 = [UIImage imageNamed:#"explosion1.png"];
UIImage *image2 = [UIImage imageNamed:#"explosion2.png"];
UIImage *image3 = [UIImage imageNamed:#"explosion3.png"];
myImageView.animationImages = [NSArray arrayWithObjects:image0, image1, image2, image3, nil];
[myImageView setAnimationDuration:0.75f];
[myImageView startAnimation];
For a bouncing ball game, the Cocos2D game- and Box2d physics- (included with Cocos2D) engines are a great way to go.
Ray Wenderlich has three great tutorials on exactly what you want to do:
Intro to Box2D with Cocos2D Tutorial: Bouncing Balls
How To Create A Breakout Game with Box2D and Cocos2D Tutorial: Part 1
How To Create A Breakout Game with Box2D and Cocos2D Tutorial: Part 2
Once you get past the basics of working with Box2d objects, the key step is to create a Contact Listener (as described in the tutorials) which will tell you when you have a collision. Check (by iterating over contacts in your tick method) if the colliding objects are the ones you want for the explosion, and if so add them to a vector of objects to be destroyed (we can't do this while iterating over contacts or we will invalidate the iterator). Remember the coordinates of the collision and set a flag for explosion.
After exiting the loop destroy the bodies and run the explosion effect at the saved position.
Cocos2D has some very nice built-in explosion effects, and the one I am using for my project is CCParticleSun. You can find some screenshots of someone using this effect here. (The curiously named CCParticleExplosion just gave a lot of confetti flying around the screen.)
Here is the relevant portion from some preliminary code I have working, which is mostly from the tutorial. You will still need to do everything else, eg. create and set up your world and bodies in your init method, update positions, apply forces, and draw in your tick method, implement your Contact Listener class, etc. For all this refer to the tutorials.
std::vector<b2Body *>toDestroy;
std::vector<MyContact>::iterator pos;
CGPoint p;
bool explode = FALSE;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;
if ((contact.fixtureA->GetBody() == _moonBody && contact.fixtureB->GetBody() == _moon2Body) ||
(contact.fixtureA->GetBody() == _moon2Body && contact.fixtureB->GetBody() == _moonBody)) {
NSLog(#"Moons collided!");
explode = TRUE;
p = CGPointMake(PTM_RATIO*(_moonBody->GetPosition().x + _moon2Body->GetPosition().x)/2.,PTM_RATIO*(_moonBody->GetPosition().y + _moon2Body->GetPosition().y)/2.);
if (std::find(toDestroy.begin(), toDestroy.end(), _moonBody) == toDestroy.end()) {
toDestroy.push_back(_moonBody);
}
if (std::find(toDestroy.begin(), toDestroy.end(), _moon2Body) == toDestroy.end()) {
toDestroy.push_back(_moon2Body);
}
}
}
std::vector<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *) body->GetUserData();
[sprite.parent removeChild:sprite cleanup:YES]; }
world->DestroyBody(body);
}
if (explode)
{
explosion = [[CCParticleSun alloc]initWithTotalParticles:250];
explosion.texture = [[CCTextureCache sharedTextureCache] addImage:#"fire.png"];
explosion.autoRemoveOnFinish = YES;
explosion.startSize = 15.0f;
explosion.speed = 30.0f;
explosion.position = p;
explosion.duration = 1.0f;
[self addChild:explosion z:self.zOrder+1];
}
What you want, then, is a way to create a particle emission system. My suggestion is to use a CAEmitterLayer. Ray Wenderlich has a great tutorial: UIKit Particle Systems in iOS 5 Tutorial
iPhone Programming Tutorial: Animating a Game Sprite
If you need to make an explosion another alternative would to use Cocos2d and do:
CCParticleSun *anExplosion=[CCParticleGalaxy node];
[kk setPosition:ball.position];
[self addChild:anExplosion];
Not really an animation, also, from the question you don't seem to be using Cocos2d, but if you start using it you can do this...

Animating a CCSprite on cocos2d?

Hey guys, I'm new to cocos2d and iPhone development, im currently trying to create a game like "PapiJump",
What I'm currently having problems with, is animating the character CCSprite in my game,
I have created 3 images of my character, character pointing right, left and middle (http://i53.tinypic.com/ngzoyh.png)
so when the character changes its direction (left or right), it wont "jump" directly to the other direction but will animate..
In my code im using the TouchesEnded method, in this method im setting a variable named "touchState",
so if the user touched the right side of the screen it will set "touchState" to 1, otherwise it sets it to 2 (means left).
then in the "init" method i created a "schedule" that runs another method every 0.5 sec that is named: "updateGame",
the "updateGame" method updates the player's position, it works like that:
player.position = ccp(player.position.x , player.position.y - playerGravity.y);
I've read Ray's tutorial on animating CCSprites but i have no idea how to achieve the result that i need..
Thanks in advance and sorry for my bad english!
NSMutableArray *walkAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 4; ++i)
{
[walkAnimFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"spr_fillin_0%d.png", i]]];
}
CCAnimation *walkAnim = [CCAnimation animationWithFrames:walkAnimFrames delay:0.0333f];
_sprite = [CCSprite spriteWithSpriteFrameName:#"spr_fillin_01.png"];
[_sprite runAction:[CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO]]];
[<CCLAYER OR BATCH NODE> addChild:_sprite];
Edit: Looks like you're talking about just flipping the image around. You can just refetch a sprite with
_sprite = [CCSprite spriteWithSpriteFrameName:#"<New Sprite File>"];
(Or cache them and switch)
Can you clarify your question? If you want to set a different sprite frame, you can use setDisplayFrame:
CCSpriteFrame *spriteFrame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:frameName];
[sprite setDisplayFrame:frame];
or if you have an animation:
[sprite runAction:[CCAnimate actionWithAnimation:runAnimation restoreOriginalFrame:NO]];
But I feel like the question is a little vague.
if i got your question properly i can suggest two ways to solve your problem:
first you could rotate your object to till it is upside down, and the continue your previous animations using FlipX(or FlipY) flag marks as true or you can add an animation wich animates only characters rotation and when ever it's finnished change the FLipX(or FlipY) status.