I am setting background color in this code:
- (void)randomBackground {
int random = arc4random() % 3;
NSLog(#"%d", random);
switch (random) {
case 0:
self.background = [SKSpriteNode spriteNodeWithImageNamed:#"image1"];
break;
case 1:
self.background = [SKSpriteNode spriteNodeWithImageNamed:#"image2"];
break;
case 2:
self.background = [SKSpriteNode spriteNodeWithImageNamed:#"image3"];
break;
}
self.background.position = CGPointMake(self.size.width / 2.0, self.size.height / 2.0);
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
NSLog(#"Size: %#", NSStringFromCGSize(size));
}
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.physicsWorld.contactDelegate = self;
return self;
}
-(void)didMoveToView:(nonnull SKView *)view {
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
[self randomBackground];
self.player = [SKSpriteNode spriteNodeWithImageNamed:#"ninja"];
self.player.position = CGPointMake(self.player.size.width / 2.0, self.player.size.height / 2.0);
[self removeAllChildren];
[self addChild:self.background];
[self addChild:self.player];
}
After my player looses, I present another scene, and than present this scene again:
SKAction *loseAction = [SKAction runBlock:^{
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
GameOverScene *gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:NO];
[self.view presentScene:gameOverScene transition:reveal];
}];
[hero runAction:[SKAction sequence:#[move, loseAction, moveDone]]];
Here is how I present main scene again:
[self runAction:
[SKAction sequence:#[[SKAction waitForDuration:0.5],
[SKAction runBlock:^{
[self.parentScene removeAllChildren];
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
GameScene *myScene = [GameScene sceneWithSize:self.size];
[self.view presentScene:myScene transition:reveal];
}]
]]];
The problem is that sometimes, when a specific background is loaded, the whole content seems to be covered by it, however, when I nslog the children of scene, the background is on the first place of an array.
As the result, I can see only background. Strangely enough, this happens only on certain backgrounds, while on other backgrounds it is OK and the content is displayed properly.
Any ideas how to fix it?
The order of initialization is actually very interesting, but its a completely different topic. To fix your issue, you need to specify each node's position on the z-axis, through the property zPosition. For example, if you write:
background.zPosition = -1;
myNode.zPosition = 1;
myNode will always present itself on top of background.
Related
I am having trouble having my game begin paused so that the player has to press a button to start the game. I also have collision detection working, so when the two objects collide they just fall off of the screen. Here is my current code:
- (instancetype)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.physicsWorld.contactDelegate = self;
[self buildBackground];
[self startScrolling];
_firstPosition = CGPointMake(self.frame.size.width * 0.817f, self.frame.size.height * .40f);
_squirrelSprite = [SKSpriteNode spriteNodeWithImageNamed:#"squirrel"];
_squirrelSprite.position = _firstPosition;
_atFirstPosition = YES;
_squirrelSprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
_squirrelSprite.physicsBody.categoryBitMask = squirrelHitCategory;
_squirrelSprite.physicsBody.contactTestBitMask = nutHitCategory;
_squirrelSprite.physicsBody.collisionBitMask = nutHitCategory;
_squirrelSprite.physicsBody.affectedByGravity = NO;
self.physicsWorld.contactDelegate = self;
[self addChild:_squirrelSprite];
// Declare SKAction that waits 2 seconds
SKAction *wait = [SKAction waitForDuration:3.0];
// Declare SKAction block to generate the sprites
SKAction *createSpriteBlock = [SKAction runBlock:^{
const BOOL isHeads = arc4random_uniform(100) < 50;
NSString* spriteName = isHeads ? #"lightnut.png" : #"darknut.png";
SKSpriteNode *nut = [SKSpriteNode spriteNodeWithImageNamed:spriteName];
BOOL heads = arc4random_uniform(100) < 50;
nut.position = (heads)? CGPointMake(257,600) : CGPointMake(50,600);
nut.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(200,160)];
nut.physicsBody.categoryBitMask = nutHitCategory;
nut.physicsBody.contactTestBitMask = squirrelHitCategory;
nut.physicsBody.collisionBitMask = squirrelHitCategory;
nut.physicsBody.affectedByGravity = NO;
self.physicsWorld.contactDelegate = self;
[self addChild: nut];
SKAction *moveNodeUp = [SKAction moveByX:0.0 y:-700.0 duration:1.3];
[nut runAction: moveNodeUp];
}];
// Combine the actions
SKAction *waitThenRunBlock = [SKAction sequence:#[wait,createSpriteBlock]];
// Lather, rinse, repeat
[self runAction:[SKAction repeatActionForever:waitThenRunBlock]];
}
return self;
}
My touchesBegan:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (_atFirstPosition)
{
SKAction *moveNodeLeft = [SKAction moveByX:-207.8 y:0.0 duration:0.85];
[_squirrelSprite runAction: moveNodeLeft withKey:#"moveleft"];
} else {
SKAction *moveNodeRight = [SKAction moveByX:207.8 y:0.0 duration:0.85];
[_squirrelSprite runAction: moveNodeRight withKey:#"moveright"];
}
_atFirstPosition = !_atFirstPosition;
_squirrelSprite.xScale *= -1.0;
}
Finally, my didBeginContact:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
if(firstBody.categoryBitMask == squirrelHitCategory || secondBody.categoryBitMask == nutHitCategory)
{
}
}
I've been trying to figure this out for the last 2 weeks and I have read many tutorials and code to start the game with a simple start button and end screen, but nothing has worked. Any help is greatly appreciated!
I would use a navigation controller and have your start button on the root view and have that button push a viewcontroller that presents your game.
I have been trying to achieve this from this morning but haven't figured it out. I have a SKSpriteNode which is an Image that displays the name of my game on the main screen. What I am trying to achieve is to change those images every half a second. I have made multiple images of the name with different colors so I can change images every half a second.That will give the effect of the title changing colors like a typical arcade game. This is what I have done so far...
- (void)viewDidLoad
{
[super viewDidLoad];
_images = [NSArray arrayWithObjects:#"YellowLabel.png", #"BlueLabel.png", #"GreenLabel.png", #"RedLabel.png", #"WhiteLabel.png", nil];
NSTimer *myTimer = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(doAnimation) userInfo:nil repeats:YES];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [TitleScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
SKSpriteNode *labelNode = [SKSpriteNode spriteNodeWithImageNamed:#"WhiteLabel.png"];
labelNode.position = CGPointMake(160, 400);
// Present the scene.
[skView presentScene:scene];
[self doAnimation];
[scene addChild:labelNode];
}
And I also have this additional method:
-(void)doAnimation {
SKSpriteNode *labelNode = [SKSpriteNode spriteNodeWithImageNamed:#"WhiteLabel.png"];
static int counter = 0;
if ([_images count] == counter+1) {
counter = 0;
}
labelNode = [SKSpriteNode spriteNodeWithImageNamed:[_images objectAtIndex:counter]];
}
Thanks for any help!
Here's one way you could do this with SKAction :
SKAction *actionWait = [SKAction waitForDuration:.5];
SKAction *actionBlock = [SKAction runBlock:^(void)
{
// do whatever you want to do every half second here
}];
SKAction *actionSequence = [SKAction sequence:#[actionWait, actionBlock]];
SKAction *actionRepeat = [SKAction repeatActionForever:actionSequence]
[self runAction:actionRepeat];
Another way, could be like this, since I think you are just animating textures :
// create an NSArray called anim that includes all your SKATextures
SKAction *actionAnimate = [SKAction animateWithTextures:anim timePerFrame:.5 resize:YES restore:NO];
SKAction *actionRepeat = [SKAction repeatActionForever:actionAnimate];
[self runAction:actionRepeat];
I have prepared my game to be published to the app store. Before I do that, I wanted to turn off the node count and FPS and did not want those two things to be displayed. I have a GameScene.m and a TitleScene.m. I tried view.showsFPS = NO; and view.showsNodeCount = NO; in my GameScene.m and it works fine. In my TitleScene.m I tried self.view.showsNodeCount = NO; and self.view.showsFPS = NO;, but it still shows the NodeCount and FPS. Here is my code for TitleScene.m:
#import "TitleScene.h"
#import "GameScene.h"
#implementation TitleScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.view.showsNodeCount = NO;
self.view.showsFPS = NO;
SKTexture *YellowLabelTexture = [SKTexture textureWithImageNamed:#"YellowLabel.png"];
SKTexture *BlueLabelTexture = [SKTexture textureWithImageNamed:#"BlueLabel.png"];
SKTexture *GreenLabelTexture = [SKTexture textureWithImageNamed:#"GreenLabel.png"];
SKTexture *RedLabelTexture = [SKTexture textureWithImageNamed:#"RedLabel.png"];
SKTexture *WhiteLabelTexture = [SKTexture textureWithImageNamed:#"WhiteLabel.png"];
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:#"awsome.png"];
background.size = CGSizeMake(640, 1136);
background.position = CGPointMake(0,0);
NSArray *anim = [NSArray arrayWithObjects:YellowLabelTexture, BlueLabelTexture, GreenLabelTexture, RedLabelTexture, WhiteLabelTexture, nil];
SKSpriteNode *labelNode = [SKSpriteNode spriteNodeWithImageNamed:#"WhiteLabel.png"];
labelNode.position = CGPointMake(self.size.width / 2, self.size.height / 2 * 1.5);
SKSpriteNode *startButtonNode = [SKSpriteNode spriteNodeWithImageNamed:#"playButton.png"];
startButtonNode.position = CGPointMake(self.size.width / 2, self.size.height / 3);
SKAction *actionAnimate = [SKAction animateWithTextures:anim timePerFrame:.3 resize:YES restore:NO];
SKAction *actionRepeat = [SKAction repeatActionForever:actionAnimate];
[labelNode runAction:actionRepeat];
[self addChild:background];
[self addChild:labelNode];
[self addChild:startButtonNode];
}
return self;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
GameScene* gameScene = [[GameScene alloc] initWithSize:self.size];
gameScene.scaleMode = SKSceneScaleModeAspectFill;
[self.view presentScene:gameScene transition:[SKTransition doorsOpenHorizontalWithDuration:1.5]];
}
Is there something that I am doing wrong? Thanks!
The default SpriteKit template sets node count and fps after the initialization of the scene in ViewController.m, you have to remove these lines.
Comment showsFPS and ShowsNodeCount in your ViewController.m file;
//skView.showsFPS = YES;
//skView.showsNodeCount = YES;
i'm trying to create an pause button in my game. at the moment when you click on the screen it will apply an impulse on the mover spriteNode. The problem is that it still applying the impulse when i click the pause button. How can i create an pause button without creating the impulse also.
i've tried changing the if statements to an else if and then making the last impulse part the else part, but this wont work.
touchBegan method:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//if fire button touched, bring the rain
if ([node.name isEqualToString:#"repeat"]) {
SKTransition *reveal = [SKTransition fadeWithDuration:2.0 ];
MyScene *newScene = [[MyScene alloc] initWithSize: CGSizeMake(self.size.width,self.size.height)];
// Optionally, insert code to configure the new scene.
[self.scene.view presentScene: newScene transition: reveal];
}
if ([node.name isEqualToString:#"home"]) {
SKTransition *reveal = [SKTransition fadeWithDuration:2.0 ];
Menu *newScene = [[Menu alloc] initWithSize: CGSizeMake(self.size.width,self.size.height)];
// Optionally, insert code to configure the new scene.
[self.scene.view presentScene: newScene transition: reveal];
}
if ([node.name isEqualToString:#"pause"]) {
[self pausedMenu];
}
if ([node.name isEqualToString:#"start"]) {
[self startMenu];
}
showpipes = showpipes + 1;
if (showpipes == 1) {
self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 );
SKAction* spawn = [SKAction performSelector:#selector(spawnPipes) onTarget:self];
SKAction* delay = [SKAction waitForDuration:2.0];
SKAction* spawnThenDelay = [SKAction sequence:#[spawn, delay]];
SKAction* spawnThenDelayForever = [SKAction repeatActionForever:spawnThenDelay];
[self runAction:spawnThenDelayForever];
}
started = 1;
if (noImpulse == 1) {
if (started == 1) {
mover.physicsBody.restitution = 0.0;
mover.physicsBody.velocity = CGVectorMake(0, 0);
[mover.physicsBody applyImpulse:CGVectorMake(0, 15)];
}
}
}
PAUSEDMENU METHOD:
-(void)pausedMenu
{
self.scene.paused = YES;
mover.scene.paused = YES;
[repeatButton removeFromParent];
[homeButton removeFromParent];
[menuBackground removeFromParent];
menuBackground = [SKSpriteNode spriteNodeWithColor:[SKColor colorWithWhite:0.0 alpha:0.3] size:CGSizeMake(self.frame.size.width*2, self.frame.size.height*2)];
[self addChild:menuBackground];
[pauseButton removeFromParent];
startButton = [SKSpriteNode spriteNodeWithImageNamed:#"staart"];
startButton.name = #"start";
startButton.position = CGPointMake(20, self.size.height-20);
startButton.size = CGSizeMake(40, 40);
[self addChild:startButton];
repeatButton = [SKSpriteNode spriteNodeWithImageNamed:#"repeat"];
repeatButton.name = #"repeat";
repeatButton.position = CGPointMake(self.size.width/2+30, self.size.height/2);
repeatButton.size = CGSizeMake(repeatButton.size.width/2, repeatButton.size.height/2);
[self addChild:repeatButton];
homeButton = [SKSpriteNode spriteNodeWithImageNamed:#"home"];
homeButton.name = #"home";
homeButton.position = CGPointMake(self.size.width/2-30, self.size.height/2);
homeButton.size = CGSizeMake(homeButton.size.width/2, homeButton.size.height/2);
[self addChild:homeButton];
}
Check out
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKView/Reference/Reference.html#//apple_ref/occ/instp/SKView/paused
If you pause your SKView that contains the game, all the state will be truly paused and you will be able to resume it after the pause menu has been dismissed. You'll have to present the pause menu in another SKView then the one that your game is contained in.
I am trying to animate a stickman walking. However, when I animate its texture atlas, the second image deforms and is stretched along the x-axis. Here's the code for setting up the scene:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* GROUND */
self.backgroundColor = [SKColor colorWithRed:0 green:1 blue:1 alpha:1.0];
CGFloat groundHeight = (size.height / 2) - 50;
CGSize groundSize = CGSizeMake(size.width, groundHeight);
SKSpriteNode *ground = [SKSpriteNode spriteNodeWithColor:[UIColor lightGrayColor] size:groundSize];
ground.position = CGPointMake(CGRectGetMidX(self.frame), (groundHeight / 2));
[self addChild:ground];
/* STICKMAN */
SKTextureAtlas *stickmanTextureAtlas = [SKTextureAtlas atlasNamed:#"Stickman"];
stickmanFrames = #[[stickmanTextureAtlas textureNamed:#"stickman1"],
[stickmanTextureAtlas textureNamed:#"stickman2"]];
stickmanSprite = [SKSpriteNode spriteNodeWithTexture:stickmanFrames[0]];
CGPoint stickmanPosition = CGPointMake(200, 100);
stickmanSprite.position = stickmanPosition;
[self addChild:stickmanSprite];
[stickmanSprite runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:stickmanFrames
timePerFrame:0.5f
resize:NO
restore:YES]] withKey:#"walkingInPlaceStickman"];
}
return self;
}
Why is this happening? Even without scaling the image using its CGSize this still happens. The images' actual sizes in pixels are the same.