Most efficient way to create CCSprites and particles in Cocos2D - iphone

Right now, in my game, I am spawning a sprite every second or so at the top of the screen (using a sceduler) using this code:
The init method:
[self schedule:#selector(addMeteor:) interval:1];
The scheduler method:
- (void)addMeteor:(ccTime)dt
{
CCTexture2D *meteor = [[CCTextureCache sharedTextureCache] addImage:#"Frame3.png"];
target = [CCSprite spriteWithTexture:meteor rect:CGRectMake(0, 0, 53, 56)];
//Rest of positioning code was here
}
Doing it this way causes a stutter in the frame rate every second or so (Whenever another sprite is spawned). Is there a way to eliminate that?
Thanks in advance!
Tate

I'm guessing the stutter is more likely coming from other parts of the code. Do you explicitly call removeChild on meteors? That might cause a hiccup, especially with many meteors.
My advice: create N meteor sprites up front. When you need one, make it visible and change its position. When you're done with it, set it to visible = NO to make it disappear.

Related

Duplicating a particle emitter effect in Sprite Kit

I need to have a particle emitters effects to appear duplicated in 2 spots (For split screen purposes), I am wondering if anybody has come across a way of doing this. My goal is to have this running on iOS 10. Here is what I have tried so far.
Assigning targetNode to an SKNode and then copying SKNode every frame: targetNode does not work in iOS 10 for me.
Assigning the particle emitter to an SKNode, and using view.textureFromNode to make a texture to copy every frame : takes 1/120th of a frame, too slow for me.
Assigning a custom action to a particle node that duplicates the node into another SKNode : Actions will not run on iOS 10
Copying the existing particle node in hopes that the seed is not random : The seed is random
Tried copying the particle emitter on update : Particle just spawns at origin
Tried SKReferenceNode : Just a copy of the emitter, runs on its own
The only option I am left for true emitting is writing my own particle emitter, which I am trying to avoid, so I am wondering if anybody else had this problem and knew of a solution to achieve desired effect.
The other thing I could do is prerendering the emitters, but this will take up a lot of texture memory if I go this route.
Edit: To help visualize, I drew a picture, The white border shows where the Split Screen happens. The black border shows where the scene wrapping happens.
As of right now, both Player 1 and Player 2 are on the original scene.
Player 2 is walking right and is about to hit a world wrap to move him to the left hand side of the world, but he has not hit it yet. So I need to have a copy of the scene happening so that it visually looks like one scene. Since player 1 is still on the original scene, the original emitter needs to stay in place. Player 2 is going to have to see the same image happening in the copy, otherwise once he passes that border, a "glitchy" effect will happen, and the illusion of wrapping is now gone.
Final Result:
We just said F%%% it, Player 1 and Player 2 will be looking at different emitter nodes, we will just attach the emitters to each players camera when needed.
This just works (I guess as intended), but I have no idea how much is performant or how it fits your needs and your current game. Anyways, maybe it can help you in some way. First, in your GameViewController...Split screen :)
#import "GameViewController.h"
#import "GameScene.h"
#implementation GameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * leftSKView = (SKView *)self.leftScene;
leftSKView.ignoresSiblingOrder = YES;
leftSKView.showsFPS = YES;
leftSKView.showsNodeCount = YES;
SKView * rightSKView = (SKView *)self.rightScene;
rightSKView.ignoresSiblingOrder = YES;
rightSKView.showsFPS = YES;
rightSKView.showsNodeCount = YES;
// Create and configure the scene.
GameScene *scene = [GameScene nodeWithFileNamed:#"GameScene"];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[leftSKView presentScene:scene];
[rightSKView presentScene:scene];
}
leftScene and rightScene are UIViews defined in a storyboard using autolayout to take half of a screen each. Also a class is changed to SKView (it was UIView). Pretty trivial...
Then in your game scene just add an emitter which will be shared between these two views. Because only one emitter is used, particles emitting (how they move) will be mirrored. So in game scene, just add a particle:
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"MyParticle" ofType:#"sks"]];
emitter.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidX(self.frame));
emitter.name = #"explosion";
emitter.targetNode = self;
[self addChild:emitter];
}
I know that you don't need all this code, but if this is what you want in some way, I will post it for completeness. And here is the result:

iPhone game development with Cocos2d: how to generate a cool light effect when an entity gets hit?

Abused topic considering the posts (e.g this or this) but I still don't quiet manage to get what I want.
I am working on a shooter game and I would like to light up my enemies when they get hit (as well as the player).
If I run the following my sprite doesn't get white. Is this the right direction?
-(void) gothitAnimation
{
ccColor3B originalColor = self.color;
id delay = [CCDelayTime actionWithDuration:0.4f];
[self runAction:[CCSequence actions: [CCTintTo actionWithDuration:0.01f red:255 green:240 blue:240], delay, [CCTintTo actionWithDuration:0.01f red:originalColor.r green:originalColor.g blue:originalColor.b] , nil]];
}
I have tried running only the CCTintTo action and it does work only with colours different than white.
I have found Arodius's game demo and it seem to me that there the player ship gets set multiple times to invisible and visible in a short sequence of actions when hit or in lower energy levels (see this demo). Also the enemy get a light effect when hit.
Any idea on how this has been achieved? Did the developer use the CCTintTo action or something else? I know that for the explosion he used ParticleEffects.
The default tint color for sprites is white. So you can't tint to white or make the sprite appear brighter with tinting.
Blinking can be achieved by setting the visible property on and off at an interval. One way to do that is to use the CCBlink action.
Your best bet is to use a particle effect. You can only achieve a true lighting effect if you're using cocos2d 2.0 and write a fragment shader for this particular effect.
I recently needed to do the same, and didn't want to deal with pixel shaders. I figured I could mess with how the texture is blended into the scene to get some sort of white flash out of it.
So, I figured out a reasonable light-flash effect by repeatedly changing the sprite's Destination glBlend function from GL_ONE_MINUS_SRC_ALPHA to GL_ONE, and then back.
Here's my toggle method:
-(void)toggleBlendFlash {
if (sprite_) {
ccBlendFunc blendFunc = sprite_.blendFunc;
if (blendFunc.dst == GL_ONE_MINUS_SRC_ALPHA) {
blendFunc.dst = GL_ONE; //flash white
} else {
blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA; //default
}
sprite_.blendFunc = blendFunc;
}
}
Here's my flashing sequence:
CCSequence *sequence =
[CCSequence actions:
[CCCallFunc actionWithTarget:targetEnemy selector:#selector(toggleBlendFlash)],
[CCDelayTime actionWithDuration:0.3],
[CCCallFunc actionWithTarget:targetEnemy selector:#selector(toggleBlendFlash)],
[CCDelayTime actionWithDuration:0.2], nil ];
It may not look perfect for your situation, but give it a try!
EDIT 1: Also, check these links out for more info/resources:
http://digitallybold.com/314/cocos2d-additive-sprite-color-tinting
http://www.andersriggelsen.dk/glblendfunc.php
EDIT 2:
Of course, I just realized that if your sprite is animating between different CCTexture2D objects (e.g. you have 1 file per frame), then it resets your blendFunc for every frame, which means you have to either create a category or a subclass for CCSprite or CCAnimate to re-apply your custom blendFunc... so I guess its not as quick of a solution as I had originally thought.

Collision detection between layers in Cocos2d?

I am making a game in Cocos2d. I have enemies that shoot, and have the character shoot. I created a separate layer for the enemies (and their bullets) and a separate layer for the character (and its bullets). The problem is, I don't know how to detect collisions between the two layers. Note, I have the Scene in HelloWorldLayer, and each of the above layers is a child of the scene. Any help is appreciated. Thanks!
You need to add following lines if your using chipmunks
shape->collision_type = kCollisionTypeParticle;
cpSpaceAddCollisionHandler(space_,
kCollisionTypeParticle,
kCollisionTypeParticle,
collisonDetect,
NULL,
NULL,
NULL,
self);
Here collisonDetect is a method we need to register as:
cpBool collisonDetect(cpArbiter *arb, struct cpSpace *space, void *data)
{
<YOUR CLASS> *layer = (<YOUR CLASS> *)data;
[layer collisonDetect:arb];
return cpTrue;
}
Now here here you will handle rest of the code
-(void)collisonDetect:(cpArbiter*)arb
{
NSLog(#"COLLISION DETECTED");
}
You can detect collision in Cocos2D using CGRectIntersectsRect.
Your idea regarding creation of separate layers for enemies and bullets might prove to be confusing. In this scenario you should consider going for one layer. You must have had a look on Ray Wenderlich of collision detection. If not have a look at Simple Cocos2d game.
If you require more help, let me know.
Why not create the bullets on the opposite layer from the bullet source, i.e. layer A is the character and the enemy bullets, layer B is the enemy and the character bullets? Then your collision detection would be on the same layer.
Look into CGRectIntersectsRect.... I haven't done Cocos2D in a LONG time but I do remember using a Scheduler to regularly invoke a method which would detect collisions using the CGRectIntersectsRect method...
I had a limited number of sprites on screen and on every pass of the collision detection method I would check to see if any of my enemy sprite frames intersected with my protagonist's frames using CGRectIntersectsRect.
This is how I did it:
Step 1:
Implement a method that uses CGRectIntersectsRect to check if the the sprite frames are touching. It could look something like:
- (BOOL)detectCollision
{
CGRect frame1 = someframe;
CGRect frame2 = anotherframe;
if(CGRectIntersectsRect(frame1, frame2))
return YES;
else
return NO;
}
Implement a Scheduler to invoke your collision detection method every seconds using:
[self schedule: #selector(detectCollision) interval:0.25];
This way in your game everytime the collisionDetect method is called you can detect collisions. :)

Combining two textures in one

I'm developing a game for iOS. I'm using cocos2d libs. I want to have an object, that have 3 parts - beginning, ending and the middle. I've got the image with these components. And the object can be stretched, when created. But only the middle part should be stretched, the beginning and endings should have no scaling. Because this operation is done only once i decided it's a good idea to create a new CCSprite for this object, and not to keep three (for increasing performance).
I'm using CCSPriteBatchNode for rendering, and i don't know if i really need to combine the object's parts (maybe rendering 3 parts using batch will be as fast as rendering one pre-combined object).
So there are two quastions:
Do i need to combine parts in one object?
If, yes - how can i do that?
Instead of combining the textures you could create a node and add the three sprites as children to it. You can then work with the parent node as a single entity.
Something along the lines of:
CCNode *sprites = [CCNode node];
CCSprite *spriteA = [CCSprite spriteWithSpriteFrameName:#"spriteA.png"];
spriteA.position = ccp(-10, 0);
[sprites addChild:spriteA];
CCSprite *spriteB = [CCSprite spriteWithSpriteFrameName:#"spriteB.png"];
spriteB.position = ccp(0, 0);
[sprites addChild:spriteB];
CCSprite *spriteC = [CCSprite spriteWithSpriteFrameName:#"spriteC.png"];
spriteC.position = ccp(10, 0);
[sprites addChild:spriteC];
You can scale and position each individual sprite depending on your parameters then work with the sprites object to position/scale them as a whole.
There might be a small performance hit so I would think twice before using this for a large amount of sprites, but I've been using this method in a few situations and in my case I didn't notice any issues with performance.
Look at the RenderTexture demo.
Instead of using the brush, you can use put your 3 parts onto it using those images instead of the brush.

App with expanding animated line in iOS ... howto

The basic idea
is very easy. Simplified you could say... a snake like line realized by a let's say 3px line is expanding across the screen collecting and interacting with different stuff, can be steered through user input. Like a continous line you would draw with a pen.
I've already started reading apple's documentation on Quartz/CG.
As I understand now, I need to put my rendering code into a UIView's drawRect.
Then I would need to set a timer (found some answers/posts here and there, nothing concrete though) that fires x times per second and calls setNeedsDisplay on the UIView.
Question:
How to realize the following:
Have whole snake on UIView 1 / (Layer ?), draw new part on UIView 2, merge them, so that the new part gets appended to UIView 1 (or CALayer instead of views ?). I ask explicitly about this cause I read, that one shouldn't redraw the same content over and over again, but just the new/moving part.
I hope you can provide me some sample code or some details which classes I should use and the strategy on how to use them / which calls to make.
Edit
OK, I see that my idea or what I've read before to realize this with Quartz and different views is not so wise... ( Daniel Bleisteiner )
So I switched to OpenGL now, well I'm looking into it, reading examples, Jeff LaMarche's OpenGL blog entries, etc..
I guess I would be able to draw my line. I think I would create classes for curves, straight lines / direction changes, etc. and then on user input I would create the related objects (depending on the steer input) store them in an array and then recreate and redraw the whole line by reading the object properties from the objects stored in the array on each frame. A simple line I would draw like this (code from Beginning iPhone Development)
glDisable(GL_TEXTURE_2D);
GLfloat vertices[4];
// Convert coordinates
vertices[0] = start.x;
vertices[1] = start.y;
vertices[2] = end.x;
vertices[3] = end.y;
glLineWidth(3.0);
glVertexPointer (2, GL_FLOAT , 0, vertices);
glDrawArrays (GL_LINES, 0, 2);
Maybe I will even find a way to antialias it but
now I'm more curious if my idea is good enough or if there are some better established strategies for this
and maybe someone could tell me how to seperate code for hud's, the line drawing itself and some menus that I will have to display f.e. at the beginning ... so that it's easy to transition from one "view" to another (like with a fade)? Some hints ?
Maybe you have also read a book, that will explain how to solve this kind of problems?
Or you can point me to another good answer / example code as I have huge problems in finding "animated drawing" examples.
this got me a little further, but it is still a little vague for me.
I don't know how to realize "update path that you draw (only with needed points + one last point that moves)"
Don't try to merge different views... setNeedsDisplay has a rect parameter that tells the core graphics part that only a certain part of the screen needs to be rendered again. Respect this parameter in your drawRect method and it should be enough for standard 2D games and tools.
If you intent to use intense graphics there is no other option than to use OpenGL. The performance is multiple times better and you won't have to care about optimizations... OpenGL does much in the background.
Use OpenGL ES. Then what you will want to do is create a run loop function or method which gets called by a CADisplayLink 30 to 60 times per second. 60 is the maximum.
So how does this work?
1) Create an assign-property for CADisplayLink. Do NOT retain your CADisplayLink, because display links and timers retain their target. Otherwise you would create a retain-cycle which may cause abandoned memory issues (which is even worse than a leak and much harder to discover).
#property (nonatomic, assign) CADisplayLink *displayLink;
2) Create and schedule the CADisplayLink:
- (void)startRunloop {
if (!animating) {
CADisplayLink *dl = [[UIScreen mainScreen] displayLinkWithTarget:self selector:#selector(drawFrame)];
[dl setFrameInterval:1];
[dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
self.displayLink = dl;
animating = YES;
}
}
Some things to note here: -setFrameInterval:1 tells the CADisplayLink to not skip any frame. This means: You get the maximum fps. BUT: This can be bad if your code needs longer than 1/60 seconds. In that case, it's better to set this to 2, for example. Makes your animation more fluid.
3) In your -drawFrame method do your OpenGL ES drawing as usual. The only difference is that this code gets called multiple times per second. Just keep track of time and determine in your code what you want to draw, and how you want it to be drawn. If you were to animate a rectangle moving from bottom left to top right with an animation duration of 1 second, you would simply interpolate the animation frames between start and end by applying a function which takes time t as argument. There are thousands of ways to do it. This is one of them.
4) When you're done or want to halt OpenGL ES drawing, just invalidate or pause your CADisplayLink. Something like this:
- (void)stopRunloop {
if (animating) {
[self.displayLink invalidate];
self.displayLink = nil;
animating = NO;
}
}