sprite kit - objective c: slow fps when I create a lot nodes - objective-c

I wanted to create a space background so I make a for loop to create the stars. Here is the code:
for (int i = 0; i<100; i++) {
SKShapeNode *star= [SKShapeNode shapeNodeWithPath:Path.CGPath];
star.fillColor = [UIColor whiteColor];
star.physicsBody = nil;
int xposition = arc4random()%960;
int yposition = arc4random()%640;
star.position = CGPointMake(xposition, yposition);
float size = (arc4random()%3 + 1)/10.0;
star.xScale = size;
star.yScale = size;
star.alpha = (arc4random()%10 + 1 )/ 10.0;
star.zPosition = -2;
[self addChild:star];
}
But it takes a lot from my cpu. when the code is activated the cpu at top 78%.(I check the code in the iPhone simulator);
Somebody know how to fix it? thanks.

Your physics bodies continue to calculate even when off of the screen. You will need to remove them once they go out of the frame, otherwise everything will slow to a crawl. (And to echo what others have stated you will eventually need a real device).
From this document: Jumping Into Sprite Kit
You can implement the "Did Simulate Physics" method to get rid of the stars that fell from the bottom of the screen like so:
-(void)didSimulatePhysics
{
[self enumerateChildNodesWithName:#"star" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.y < 0)
[node removeFromParent];
}];
}
Note that you will first need to set the name of your star shapes by using the name property like so:
star.name = "star"

Related

Remove old tileMap - SpriteKit - Objective C

this code loads a map1.tmx tileMap when player reaches the "door". When the player enters the door it loads a new map2.tmx file. Problem is when the new map2.tmx file is loaded, the old map1.tmx is running behind map.2.tmx and using all of map.1.tmx wall collisions AND ignoring map2.tmx wall collisions. Is there a way to do an opposite of addObject like removeObject and add map2.tmx as the new map? I would like to make map.2 run when the players enters the door.
I have tried removeAllActions, removeAllChildren, removeTileAtCoord: and other approaches but I lack of SpriteKit experience. Any help is appreciated.
- (void)handleDoorCollisions:(Player *)player
{
if (self.doorOver) return;
NSInteger indices[8] = {7, 1, 3, 5, 0, 2, 6, 8};
for (NSUInteger i = 0; i < 8; i++) {
NSInteger tileIndex = indices[i];
CGRect playerRect = [player collisionBoundingBox];
CGPoint playerCoord = [self.doors coordForPoint:player.desiredPosition];
NSInteger tileColumn = tileIndex % 3;
NSInteger tileRow = tileIndex / 3;
CGPoint tileCoord = CGPointMake(playerCoord.x + (tileColumn - 1), playerCoord.y + (tileRow - 1));
NSInteger gid = [self tileGIDAtTileCoord:tileCoord forLayer:self.doors];
if (gid != 0) {
CGRect tileRect = [self tileRectFromTileCoords:tileCoord];
if (CGRectIntersectsRect(playerRect, tileRect)) {
[self.doors removeTileAtCoord:tileCoord];
// add new map2.tmx
self.map = [JSTileMap mapNamed:#"map2.tmx"];
[self addChild:self.map];
[self removeObject:????]
}
}
}
}
I had the same issue. My solution was/is on map load I collect all collision data in a List<> and check any collisions against this list.
When the player enters a different map the list is beeing cleared and reinitialized with the data from the new map.
I use this approach for some other things as well like objects the player can interact with or areas that trigger some actions.

SpriteKit only detecting partial collision on physics body

I have a simple jumping game setup where an hero jumps over a ball rolling towards him.
there is no gravity setup on the scene
the hero jump straight up and then straight down. if the hero stays still "didBeginContact" is fired and I can detect the collision. If the front of the hero hits the rolling ball when jumping up or coming down "didBeginContact" is fired. However if the back half of the hero land on the ball as it is going under it, it does not detect the collision.
Does anyone have any idea why it only catches the collision on the front? This seems more like edge detection vs. object detection! is that possible?
static const uint32_t groundCategory = 0x1 << 1;
static const uint32_t obstacleCategory = 0x1 << 2;
static const uint32_t heroCategory = 0x1 << 3;
SKSpriteNode *hero = [[SKSpriteNode alloc] initWithColor:[SKColor redColor] size:CGSizeMake(100, 115)];
[hero setPosition:CGPointMake(HERO_UP_POSITION_X, HEIGHT_GAME / 2 + hero.size.height / 2 + 10)];
hero.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:hero.size];
[hero.physicsBody setAffectedByGravity:NO];
hero.physicsBody.usesPreciseCollisionDetection = YES;
hero.physicsBody.allowsRotation = NO;
hero.physicsBody.categoryBitMask = heroCategory;
hero.physicsBody.contactTestBitMask = obstacleCategory | groundCategory;
hero.physicsBody.collisionBitMask = 0;
hero.zPosition = 0;
hero.name = kHeroUpName;
[self addChild:hero];
obstacle = [[SKSpriteNode alloc] initWithImageNamed:kImgObstacleDown];
obstacle.name = #"obstacle";
[obstacle setPosition:CGPointMake(WIDTH_GAME + x, HEIGHT_GAME / 2 + y)];
obstacle.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:obstacle.size.width / 2];
obstacle.physicsBody.usesPreciseCollisionDetection = YES;
[obstacle.physicsBody setAffectedByGravity:NO];
obstacle.physicsBody.categoryBitMask = obstacleCategory;
obstacle.physicsBody.contactTestBitMask = heroCategory;
obstacle.physicsBody.collisionBitMask = 0;
obstacle.zPosition = 1;
I figured out the problem.
I was checking if the object was past the hero in order to track score, If it was past the center of the hero I renamed it, so that it wouldn't get checked and scored again.
Thanks #0x141E your snippet made me realize that I was checking for names inDidBeginContact (Which fails) vs. checking for categoryBitMask
There are two test cases that needs to be in your didBeginContact: 1) if contact.bodyA is the hero and contact.bodyB is the obstacle and 2) if contact.bodyA is the obstacle and contact.bodyB is the hero. You are likely handling only one case. Here's an example of how to do that:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
if ((contact.bodyA.categoryBitMask == heroCategory && contact.bodyB.categoryBitMask obstacleCategory)
|| (contact.bodyA.categoryBitMask == obstacleCategory && contact.bodyB.categoryBitMask == heroCategory)) {
// Do something here
}
}

SpriteKit - Using a loop to create multiple sprites. How do I give each sprite a different variable name?

I am using SpriteKit.
The code below basically makes a lattice of dots on the screen. However, I want to call each 'dot' a different name based on its position, so that I can access each dot individually in another method. I'm struggling a little on this, so would really appreciate if someone could point me in the right direction.
#define kRowCount 8
#define kColCount 6
#define kDotGridSpacing CGSizeMake (50,-50)
#import "BBMyScene.h"
#implementation BBMyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
// Background
self.backgroundColor = [SKColor colorWithRed:0.957 green:0.957 blue:0.957 alpha:1]; /*#f4f4f4*/
CGPoint baseOrigin = CGPointMake(35, 385);
for (NSUInteger row = 0; row < kRowCount; ++row) {
CGPoint dotPosition = CGPointMake(baseOrigin.x, row * (kDotGridSpacing.height) + baseOrigin.y);
for (NSUInteger col = 0; col < kColCount; ++col) {
SKSpriteNode *dot = [SKSpriteNode spriteNodeWithImageNamed:#"dot"];
dot.position = dotPosition;
[self addChild:dot];
//6
dotPosition.x += kDotGridSpacing.width;
}
}
}
return self;
}
Here is an image of what appears on screen when I run the above code...
http://cl.ly/image/3q2j3E0p1S1h/Image1.jpg
I simply want to be able to call an individual dot to do something when there is some form of user interaction, and I'm not sure how I would do that without each dot having a different name.
If anyone could help I would really appreciate it.
Thanks,
Ben
- (void)update:(NSTimeInterval)currentTime {
for(SKNode *node in self.children){
if ([node.name containsString:#"sampleNodeName"]) {
[node removeFromParent];
}
}
}
Hope this one helps!
You can set the name property of each node inside the loop.
Then you can access them with self.children[index].
If you want to find a specific node in your children, you have to enumerate through the array.
Update:
To clarify how to search for an item by iterating, here is a helper method:
- (SKNode *)findNodeNamed:(NSString *)nodeName
{
SKNode *nodeToFind = nil;
for(SKNode *node in self.children){
if([node.name isEqualToString:nodeName]){
nodeToFind = node;
break;
}
}];
return nodeToFind;
}

fisrt frame appears green when i write movie with UIImages using AVAssetWriter

I Am writing a video using UIImages. I can successfully write movie.
The problem is that when i play the video it show first few frames as green.
Could any one of you help me. I am new to ios.
Thank You
I think the fps (frame per second) of your video will be low. In this case, I think you need not to call CVPixelBufferPoolCreatePixelBuffer. This calling makes the first empty frame and the base green screen is shown.
for(UIImage * img in imageArray)
{
buffer = [self pixelBufferFromCGImage:[img CGImage] andSize:size];
if( frameCount == 0 ) {
// Please comment out the following line
// CVPixelBufferPoolCreatePixelBuffer (NULL, adaptor.pixelBufferPool, &buffer);
}
BOOL append_ok = NO;
int j = 0;
while (!append_ok && j < 30)
{
if (adaptor.assetWriterInput.readyForMoreMediaData)
{

cocos2d Generating multiple of the same sprites with velocity

I'm pretty new to iOS and cocos2d and I'm having a problem trying to create what I want the code to do. Let me give you the rundown first then i'll show what I've got.
What I got so far is a giant sprite in the middle and when that is touched, I want to have say 2000 of a different sprite generate from the center position and like a particle system, shoot off in all directions.
First off, I tried coding implementing the velocity code (written in Objective-c) over to Cocos2d and that didn't work. -code-
-(void)ccTouchBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if(CGRectContainsPoint([[self getChildByTag:1] boundingBox], location))
{
for( int i = 0; i < 100; i++)
{
CCSprite *ballGuySprite = [CCSprite spriteWithFile:#"ball.png"];
[self addChild:ballGuySprite z:7];
ballGuySprite.position = ccp(((s.width + i *10) /2), (s.height + i *10) /2);
}
}
}
What that does is when I touch the first sprite, 100 of the other sprites are on top of each other leading to the top right corner.
The velocity code that I used when as followed and when I try to apply it to the sprite nothing happens. - Velocity code -
-(void) checkCollisionWithScreenEdges
{
if(ballGuysRect.origin.x <= 0)
{
ballVelocity.x = abs(ballVelocity.x);
}
if(ballGuysRect.origin.x >= VIEW_WIDTH - GUY_SIZE)
{
ballVelocity.x = -1 * abs(ballVelocity.x);
}
if(ballGuysRect.origin.y <= 0)
{
ballVelocity.y = abs(ballVelocity.y);
}
if(ballGuysRect.origin.y >= VIEW_HEIGHT - GUY_SIZE)
{
ballVelocity.y = -1 * abs(ballVelocity.y);
}
}
-(void) updateModelWithTime:(CFTimeInterval)timestamp
{
if(lastTime == 0.0)
{
lastTime = timestamp;
}
else
{
timeDelta = timestamp - lastTime;
lastTime = timestamp;
ballGuysRect.origin.x += ballVelocity.x * timeDelta;
ballGuysRect.origin.y += ballVelocity.y * timeDelta;
[self checkCollisionWithScreenEdges];
}
}
When I attach that code to the sprite, nothing happen.
I also tried adding a CCParticleExplosion which did do what I wanted but I still want to add a touch function to each individual sprite that's generated and they tend to just fade away.
So again, I'm still fairly new to this and if anyone could give any advice that would be great.
Thanks for your patients and time to read this.
Your code looks good to me, but you never seem to update the position of your sprites. Somewhere in updateModelWithTime I would expect you to set ballGuySprite.position = ballGuysRect.origin plus half of its height or width, respectively.
Also, I don't see how updateModelWithTime can control 100 different sprites. I see only one instance of ballGuysRect here. You will need a ballGuysRect for each sprite, e.g. an array.
Finally, I'd say that you don't really need ballGuysRect, ballVelocity, and the sprite. Ball could be a subclass of CCSprite, including a velocity vector. Then all you need to do is keep an array of Balls and manage those.
I am not sure what version of cocos2d you are using but a few things look a bit odd.
Your first problem appears to be that you are just using the same sprite over and over again.
Since you want so many different sprites shooting away, I would recommend that you use a CCSpriteBatchNode, as this should simplify things and speed things up.
The following code should help you get that set up and move them offscreen with CCMoveTo:
//in your header file:
CCSpriteBatchNode *batch;
//in your init method
batch = [CCSpriteBatchNode batchNodeWithFile:#"ball.png"];
//Then in your ccTouches method
for( int i = 0; i < 100; i++)
{
CCSprite *ballGuySprite = [CCSprite spriteWithFile:#"ball.png"];
[batch addChild:ballGuySprite z:7 tag:0];
ballGuySprite.position = ccp(where-ever the center image is located);
id actionMove = [CCMoveTo actionWithDuration:actualDuration
position:ccp(random off screen location)];
[ballGuySprite runAction:actionMove];
}
Also usually your update method looks something like the following:
-(void)update:(ccTime)delta{
//check for sprites that have moved off screen and disable them.
}
Hope this helps.