Right now I have five bricks at the top of the screen all on the same y position and equally spaced apart. I would like to have five more breaks spawn outside of the top of the scene and slide down every 5 seconds. While the new bricks are sliding down I want the previous bricks to slide down to make room for the new. I want this sequence to continue on forever. I would like the new bricks to pick up the same properties of the old ones , such as when a ball hits them the react.
With the code I put in to accomplish this, I get a single brick spawning and then sliding down. Also the new brick does not act the same way as the old, it does not act like its a part of the physics world. How do I get 5 bricks spawning then sliding down and applying the same properties as the other bricks.
This code is the code I added to spawn more bricks:
-(void) spawnMoreBricks:(CGSize)size {
for (int i = 0; i < 5; i++) {
SKSpriteNode *brick = [SKSpriteNode spriteNodeWithImageNamed:#"brick"];
//resize bricks
brick.size = CGSizeMake(60, 30);
//position it
brick.position = CGPointMake(self.size.width/2, self.size.height);
SKAction *wait = [SKAction waitForDuration:5];
SKAction *spawn = [SKAction runBlock:^{[self addChild: brick];
SKAction *move = [SKAction moveByX:0 y:-80 duration:3];
[brick runAction: move];
}];
SKAction *spawnSequence = [SKAction sequence:#[wait, spawn]];
SKAction *repeatSequence = [SKAction repeatActionForever:spawnSequence];
[self runAction:repeatSequence];
}
}
This code adds the bricks into the scene right when it initializes, they react to the ball colliding with them and everything:
- (void) addBricks:(CGSize)size {
for (int i = 0; i < 5; i++) {
//create brick sprite from image
SKSpriteNode *brick = [SKSpriteNode spriteNodeWithImageNamed:#"brick"];
//resize bricks
brick.size = CGSizeMake(60, 30);
//psoition bricks
int xPos = size.width/5 * (i+.5);
int yPos = size.height - 25;
brick.position = CGPointMake(xPos, yPos);
brick.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:brick.frame.size];
brick.physicsBody.dynamic = NO;
brick.physicsBody.categoryBitMask = brickCategory;
//add bricks to scene
[self addChild:brick];
}
}
Related
as the title suggest, i have issue with creating contact between my enemies sprite and the hero laser(bullet). i create my enemies trough the following method and am adding them to the view.
-(void)Enemies{
//not always come
int GoOrNot = [self getRandomNumberBetween:0 to:1];
if(GoOrNot == 1){
SKSpriteNode *enemy;
int randomEnemy = [self getRandomNumberBetween:0 to:1];
if(randomEnemy == 0)
enemy = [SKSpriteNode spriteNodeWithImageNamed:#"enemy_spaceship.png"];
else
enemy = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship15.png"];
enemy.scale = 0.4;
enemy.position = CGPointMake(450, 0);
enemy.hidden = NO;
CGPoint location = CGPointMake(450, 320);
SKAction *moveAction = [SKAction moveTo:location duration:2.5];
SKAction *doneAction = [SKAction runBlock:(dispatch_block_t)^() {
//NSLog(#"Animation Completed");
enemy.hidden = YES;
}];
SKAction *moveAsteroidActionWithDone = [SKAction sequence:#[moveAction,doneAction ]];
[enemy runAction:moveAsteroidActionWithDone withKey:#"asteroidMoving"];
[self addChild:enemy];
_enemyBullets = [[NSMutableArray alloc] initWithCapacity:kNumEnemyBullet];
for (int i = 0; i < kNumEnemyBullet; ++i) {
SKSpriteNode *enemyBullets = [SKSpriteNode spriteNodeWithImageNamed:#"rocket"];
enemyBullets.hidden = YES;
[enemyBullets setXScale:0.5];
[enemyBullets setYScale:0.5];
[_enemyBullets addObject:enemyBullets];
[enemy addChild:enemyBullets];
}
}
}
then i add the bullets through a mutable array and then adding my bullets to the enemies sprites as their child. that part works. i can create contact between hero and the enemy bullet and it gets detected. what i have issue with is that my hero's laser does not create contact with the enemy thus i can't make the enemy take hit from the laser. i tries adding the physics body to the method i am using but throws all the other sprites into hell and they don't respond properly anymore.
the following code is my collision code which i am using in update method:
for (SKSpriteNode *enemyBullets in _enemyBullets) {
if (enemyBullets.hidden) {
continue;
}
if ([_ship intersectsNode:enemyBullets]) {
enemyBullets.hidden = YES;
SKAction *blink = [SKAction sequence:#[[SKAction fadeOutWithDuration:0.1],
[SKAction fadeInWithDuration:0.1]]];
SKAction *blinkForTime = [SKAction repeatAction:blink count:4];
SKAction *shipExplosionSound = [SKAction playSoundFileNamed:#"explosion_large.caf" waitForCompletion:NO];
[_ship runAction:[SKAction sequence:#[shipExplosionSound,blinkForTime]]];
_lives--;
_livesLabel.text = [NSString stringWithFormat:#"%d Lives", _lives];
NSLog(#"your ship has been hit!");
}
}
is there any way that i can use to create a physics body for my enemies so i can create the contact between the hero laser and the enemies sprite with the same structure i have currently ? any help is amazingly appreciated.
You most certainly can create a physics body for your enemies. Right after this code enemy = [SKSpriteNode spriteNodeWithImageNamed:#"enemy_spaceship.png"]; you can add a physics body like this:
enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
enemy.physicsBody.categoryBitMask = CategoryEnemy; // or whatever you need
enemy.physicsBody.collisionBitMask = CategoryRock; // or whatever you need
enemy.physicsBody.contactTestBitMask = CategoryBullet // or whatever you need
enemy.physicsBody.dynamic = NO; // or YES
enemy.physicsBody.allowsRotation = NO; // or YES
enemy.physicsBody.restitution = 0; // or another value between 0 to 1
enemy.physicsBody.friction = 0.0; // or another value between 0 to 1
There are many ways to create a physics body. You can do a rectangle shape, circle shape, outline a texture or draw your own.
You should read the Apple docs on this subject to get a better understanding of what you can do and what properties are available.
I have a row of 5 sprites and they spawn every 4 seconds and move down along the Y-axis. The sprites are spawning the correct amount each time, I have 5 sprites equal spaced apart across the screen, they're all center perfectly.
However when the performSelector action is called, when it spawns them in they don't go to the right position. The move over to the left so far that I can only see half a sprite on the left side of the screen. After testing it, it seems they are all stacked on top of each other at the wrong position.
Any idea whats going on? I'd appreciate any help. Here's all the code I'm using in the scene.
-(void) addBricks:(CGSize)size {
for (int i = 0; i < 5; i++) {
//create brick sprite from image
SKSpriteNode *brick = [SKSpriteNode spriteNodeWithImageNamed:#"brick"];
//resize bricks
brick.size = CGSizeMake(60, 30);
//psoition bricks
int xPos = size.width/7.5 * (i+.5);
int yPos = 450;
brick.position = CGPointMake(xPos, yPos);
//add move action
SKAction *wait = [SKAction waitForDuration:3];
SKAction *move = [SKAction moveByX:0 y:-36.9 duration:1];
SKAction *sequence = [SKAction sequence:#[wait, move]];
SKAction *repeatMove = [SKAction repeatActionForever:sequence];
[brick runAction:repeatMove];
[self addChild:brick];
}
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.backgroundColor = [SKColor colorWithRed:(243.0f/255) green:(228.0f/255) blue:(165.0f/255) alpha:1.0];
//add action to spawn bricks
SKAction *spawn = [SKAction performSelector:#selector(addBricks:) onTarget:(self)];
SKAction *delay = [SKAction waitForDuration:4];
SKAction *delayThenSpawn = [SKAction sequence:#[delay, spawn]];
[self runAction:[SKAction repeatActionForever:delayThenSpawn]];
[self addBricks:size];
}
return self;
}
as LearnCocos2D mentioned in your last question.. you cant use a selector when youre passing a parameter into a method.
addBricks expects size .. you arent passing size into it.
change your spawn action to
SKAction *spawn = [SKAction runBlock:^{
// make this whatever size you want, i'm just using the scene's size
[self addBricks:self.size];
}];
that should get you started
I'm working on a game that has 3 clouds in the background which are each SKSpriteNodes. In order to create a parallax effect I'm moving them down the screen as the character is moving up (the screen is moving up as well).
I removing them in the update method after they reach the bottom of the screen. I would like to add them back at a Y position 20px above the viewable scene so that would be 500x on an iPhone 4 and 588px on a iPhone 5. Then just repeat over and over.
Then only way I can think of to do this is to chain SKActions and run a sequence, but I don't think that is the best way. I'm trying to find a way to add them back after they have been removed in the update method.
-(id)initWithSize:(CGSize)size {
...
//Moving Clouds
[self performSelector:#selector(createClouds) withObject:nil afterDelay:.2];
}
-(void)createClouds {
SKSpriteNode *cloud1 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud1"];
cloud1.position = CGPointMake(40, 200);
cloud1.xScale = .5;
cloud1.yScale = .5;
cloud1.name = #"Cloud1";
cloud1.zPosition = 0;
SKAction *moveCloud1 = [SKAction moveToY:-20 duration:15];
[cloud1 runAction:[SKAction repeatActionForever:moveCloud1]];
[self addChild:cloud1];
SKSpriteNode *cloud2 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud2"];
cloud2.position = CGPointMake(300, 320);
cloud2.xScale = .5;
cloud2.yScale = .5;
cloud2.name = #"Cloud2";
cloud2.zPosition = 0;
SKAction *moveCloud2 = [SKAction moveToY:-40 duration:15];
[cloud2 runAction:[SKAction repeatActionForever:moveCloud2]];
[self addChild:cloud2];
SKSpriteNode *cloud3 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud3"];
cloud3.position = CGPointMake(200, 450);
cloud3.xScale = .5;
cloud3.yScale = .5;
cloud3.name = #"Cloud3";
cloud3.zPosition = 0;
SKAction *moveCloud3 = [SKAction moveToY:-200 duration:20];
[cloud3 runAction:[SKAction repeatActionForever:moveCloud3]];
[self addChild:cloud3];
}
-(void)update:(CFTimeInterval)currentTime {
....
// Remove Cloud from parent
[self enumerateChildNodesWithName:#"Cloud1" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x < 0 || node.position.y < 0) {
[node removeFromParent];
//Add node back to the scene at a Y position above the screen then move
// the cloud downwards again. Repeat Forever.
// Exp: Move Cloud 1 to X position 40, Y position 500 (20 pixels above the top of the screen for an iPhone 4s)
}
else {
// Do something?
}
}];
You can add an SKAction to move your cloud back to the top:
SKSpriteNode *cloud1 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud1"];
cloud1.position = CGPointMake(40, 200);
cloud1.xScale = .5;
cloud1.yScale = .5;
cloud1.name = #"Cloud1";
cloud1.zPosition = 0;
SKAction *moveCloud1 = [SKAction moveToY:-20 duration:15];
// SKAction to move cloud back to top of screen
SKAction *moveToTop = [SKAction moveToY:CGRectGetHeight(self.frame)+20 duration:0];
SKAction *cloud1Action = [SKAction sequence:#[moveCloud1, moveToTop]];
[cloud1 runAction:[SKAction repeatActionForever:cloud1Action]];
You won't need the code in the update method.
I think you can simply change the position
Also, use CGRectGetMinY to use relative positioning, easier to manage different screen sizes.
And check only the Y position too, because X won't change if they're moving vertically.
This is my idea:
-(void)update:(NSTimeInterval)currentTime {
for(int i = 1; i<=3; i++){
NSString* cloud = [NSString stringWithFormat:#"Cloud%d",i];
[self enumerateChildNodesWithName:cloud usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.y < 0) {
node.position = CGPointMake(node.position.x,CGRectGetMaxY(self.frame)+20);
}
}];
}
}
I am trying move nodes to a set X distance using an SKAction with the same duration. The Nodes are in different X positions so the speed of how the node moves is different, is there a way to make the speed the same without affecting the positioning of the nodes?
Example.
-(void)beginningGamePlatforms {
SKSpriteNode *beginningPlatform = [SKSpriteNode spriteNodeWithImageNamed:#"PlatformBeginning"];
beginningPlatform.zPosition = 2;
beginningPlatform.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:beginningPlatform.size];
beginningPlatform.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
beginningPlatform.position = CGPointMake(beginningPlatform.position.x - 20, beginningPlatform.position.y - 129);
beginningPlatform.physicsBody.dynamic = NO;
SKSpriteNode *platformSpawn1 = [SKSpriteNode spriteNodeWithImageNamed:#"platformSpawn1"];
platformSpawn1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:platformSpawn1.size];
platformSpawn1.position = CGPointMake(beginningPlatform.position.x, CGRectGetMidY(self.frame));
platformSpawn1.position = CGPointMake(platformSpawn1.position.x + 196, platformSpawn1.position.y - 129);
platformSpawn1.physicsBody.dynamic = NO;
SKSpriteNode *platformSpawn2 = [SKSpriteNode spriteNodeWithImageNamed:#"platformSpawn1"];
platformSpawn2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:platformSpawn2.size];
platformSpawn2.position = CGPointMake(platformSpawn1.position.x,CGRectGetMidY(self.frame));
platformSpawn2.position = CGPointMake(platformSpawn2.position.x + 196, platformSpawn2.position.y - 129);
platformSpawn2.physicsBody.dynamic = NO;
SKAction *moveLeft = [SKAction moveTo:CGPointMake(-100 , beginningPlatform.position.y) duration:9];
SKAction *removeFromParent = [SKAction removeFromParent];
SKAction *AllTwo = [SKAction sequence:#[moveLeft,removeFromParent]];
[self addChild:beginningPlatform];
[self addChild:platformSpawn1];
[self addChild:platformSpawn2];
[beginningPlatform runAction:AllTwo];
[platformSpawn1 runAction:AllTwo];
[platformSpawn2 runAction:AllTwo];
}
In my code I created 3 platforms with a set distance within each other that when the sprite images combined, it creates the illusion of a single wide platform. However when running the actions, the illusion breaks. Any information will be appreciated.
You can try replacing the moveTo: in:
SKAction *moveLeft = [SKAction moveTo:CGPointMake(-100 , beginningPlatform.position.y) duration:9];
with a moveBy: varient. Currently, the different platforms start at different positions to make the illusion of a continuous platform, but by moving all of them to the same place, they won't maintain their original position intervals.
For context, I'm making a brick-breaker game in iOS and want the bricks I create to all move to the left and right repeatedly as the ball bounces around the scene.
I have a method in my code that I call once when the scene is presented that adds a couple of rows of sprite nodes to the scene as bricks, as shown below
-(void) addBricks:(CGSize)size {
// add a new row of bricks
for (int j = 1; j <= 3; j++) {
// add top column of bricks
for (int i = 0; i < 5; i++) {
SKSpriteNode *brick = [SKSpriteNode spriteNodeWithImageNamed:#"brick"];
brick.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:brick.frame.size];
brick.physicsBody.dynamic = NO;
brick.physicsBody.categoryBitMask = brickCategory;
int xPos = ((brick.frame.size.width / 2) + 20) + ((brick.frame.size.width / 2) + 30) * i;
int yPos = size.height - (30 * j);
// generate the sequence that the brick will perform
SKAction *moveHorizontal = [SKAction moveToX:xPos + 20 duration:1.0f];
moveHorizontal.timingMode = SKActionTimingEaseOut;
SKAction *moveBack = [moveHorizontal reversedAction];
SKAction *wait = [SKAction waitForDuration:0.4f];
SKAction *backAndForth = [SKAction sequence:#[moveHorizontal, wait, moveBack, wait]];
SKAction *repeatHorizMove = [SKAction repeatActionForever:backAndForth];
brick.position = CGPointMake(xPos, yPos);
// add the brick
[self addChild:brick];
// make it move back and forth
[brick runAction:repeatHorizMove];
}
}
}
My problem is that, while all of the bricks will move to the right when the scene starts, they don't move back and repeat the action over and over like I want them to. I think it may have something to do with giving them all the same name when they are programmatically created, but I know the scene retains all of the bricks despite them having the same name.
How can I get it so that all of the SpriteNodes (bricks) that I create keep moving back and forth forever?
The moveToX action is not reversible. Always check the reference for comments like this.
This should fix it:
SKAction *moveHorizontal = [SKAction moveToX:xPos + 20 duration:1.0f];
moveHorizontal.timingMode = SKActionTimingEaseOut;
SKAction *moveBack = [SKAction moveToX:xPos duration:1.0];