How do I initialize all scenes at the start in Sprite Kit? - ios

Whenever I want to transition to a certain scene, it takes a couple of seconds before the SKTransition even starts. Is it possible to have all scenes initialized before the game starts?
NewScene *newScene = [NewScene sceneWithSize:self.size];
SKTransition *reveal = [SKTransition moveInWithDirection:SKTransitionDirectionUp duration:0.5];
reveal.pausesIncomingScene = NO;
[self.view presentScene:newScene transition:reveal];
I've tried this with didMoveToView as well:
#implementation NewScene
-(id)initWithSize:(CGSize)size {
if(self = [super initWithSize:size]) {
// Load a bunch of stuff like this:
SKSpriteNode *menuButton = [SKSpriteNode spriteNodeWithImageNamed:#"mainMenu"];
menuButton.position = CGPointMake(self.frame.size.width/2,self.frame.size.height/4);
menuButton.name = #"menuButton";
[self addChild:menuButton];
[menuButton setScale:0.8];
}
}
How can I make sure that my Sprite Kit game runs smoothly?
EDIT:
It turns out the problem was that I kept putting the main thread to sleep to fade out music. I've made all my audio methods run in the background and it works fine now.

A good approach is to load all resources asyncronously before SKScene's initialization. Apple uses this approach in Adventure game:
NewScene.h:
typedef void (^AGAssetLoadCompletionHandler)(void);
#interface GameScene : SKScene<SKPhysicsContactDelegate, UIGestureRecognizerDelegate>
+ (void)loadSceneAssetsWithCompletionHandler:(AGAssetLoadCompletionHandler)handler;
#end
NewScene.m:
#implementation NewScene
-(id)initWithSize:(CGSize)size {
if(self = [super initWithSize:size]) {
// Load a bunch of stuff like this:
SKSpriteNode *menuButton = [SKSpriteNode spriteNodeWithTexture:[self menuButtonTexture];
menuButton.position = CGPointMake(self.frame.size.width/2,self.frame.size.height/4);
menuButton.name = #"menuButton";
[self addChild:menuButton];
[menuButton setScale:0.8];
}
}
#pragma mark - Shared Assets
+ (void)loadSceneAssetsWithCompletionHandler:(AGAssetLoadCompletionHandler)handler {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// Load the shared assets in the background.
[self loadSceneAssets];
if (!handler) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
// Call the completion handler back on the main queue.
handler();
});
});
}
+ (void)loadSceneAssets {
sBackgroundTexture = [SKTexture textureWithImageNamed:#"background"];
sMenuButtonTexture = [SKTexture textureWithImageNamed:#"mainMenu"];
// etc.
}
static SKTexture *sBackgroundTexture = nil;
- (SKTexture *)backgroundTexture {
return sBackgroundTexture;
}
static SKTexture *sMenuButtonTexture = nil;
- (SKTexture *)menuButtonTexture {
return sMenuButtonTexture;
}
#end
Then just present NewScene from your UIViewController:
if (!self.skView.scene) {
CGSize viewSize = self.view.bounds.size;
// Here you can present some loading scene or splash screen
[NewScene loadSceneAssetsWithCompletionHandler:^{
NewScene *scene = [[NewScene alloc] initWithSize:viewSize];
[self.skView presentScene:scene transition:[SKTransition crossFadeWithDuration:1.f]];
}];
}

Related

Bonbons falling continuously from random positions

I have got the following problem in SpriteKit: My aim is to let bonbons fall continuously from the top of the screen. These bonbons can be collected by the player. Every 0.8 seconds or so, another bonbon should fall down and when the player collides with one of the bonbons, it should disappear. This is my code:
-(void)populate {
for (int i = 0; i < 2; i++) {
[self generate];
}
}
-(void)generate {
Y = (arc4random() % 280) - 140;
bonbon = [SKSpriteNode spriteNodeWithImageNamed:#"Bonbon.png"];
bonbon.size = CGSizeMake(20, 20);
bonbon.name = #"bonbon";
bonbon.physicsBody.categoryBitMask = bonbonCategory;
bonbon.position = CGPointMake(Y, 500);
bonbon.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bonbon.size.width/2];
bonbon.physicsBody.dynamic = YES;
[bonbon addChild:bonbon];
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
if ([contact.bodyA.node.name isEqualToString: #"wallLeft"] || [contact.bodyB.node.name isEqualToString: #"wallLeft"])
{
[hero.physicsBody applyImpulse: CGVectorMake(100, 60)];
self.jumpDirection = YES;
}
else if ([contact.bodyA.node.name isEqualToString: #"bonbon"] || [contact.bodyB.node.name isEqualToString: #"bonbon"])
{
[world enumerateChildNodesWithName:#"bonbon" usingBlock:^(SKNode *node, BOOL *stop) {
PointsLabel *pointsLabel = (PointsLabel *)[self childNodeWithName:#"pointsLabel"];
[pointsLabel increment];
NSLog(#"didContactBonbon");
[bonbon removeFromParent];
NSLog(#"removedFromParent");
}];
}
else {
[hero.physicsBody applyImpulse: CGVectorMake(-100, 60)];
self.jumpDirection = NO;
}
}
I hope you understand my problem. Currently, no bonbons are falling at all. If you need more information, please do not hesitate to ask.
Greets
edit: I hope the formatting is better now. I am quite a newbie, i'm sorry for foolish mistakes :) This is in my MyScene.m, when is call the populate in the initWithSize method with [self populate]; I receive the message signal SIGABRT. What did I do wrong?
I think the problem is that you are adding your SKSpriteNode named bonbon to itself with :
- (void)generate {
...
bonbon = [SKSpriteNode spriteNodeWithImageNamed:#"Bonbon.png"];
...
[bonbon addChild:bonbon];
}
Try to add bonbon to your SKScene instance.
To use an SKView instance you have to create a UIViewController and give its view the SKView class. You can do that in your storyboard.
Then, in your viewController :
#implementation GameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
// Configure the scene
SKScene *scene = [SKScene sceneWithSize:self.view.bounds.size];
// You can add objects to your scene
[scene addChild:bonbon];
// Present the scene.
[skView presentScene:scene];
}
#end
This is a very simple example but if you want more information, I suggest you read this tutorial: Sprite Kit Tutorial for Beginners.

SpriteKit change Image after swipe gesture

I am trying to code a game. I got an object, that can jump and slide.
I want to hold the animation of 'run' while jumping, but while sliding, I want to change the image. My problem: the image won't show off , if I just change the image to 'slide7'.
Nothing happens.
The slide animation should appear only for about 4 seconds, than go again into the run animation. Any suggestions?
My Code :
-(void)Mensch{
SKTexture * MenschTexture1 = [SKTexture textureWithImageNamed:#"Mensch1"];
MenschTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture * MenschTexture2 = [SKTexture textureWithImageNamed:#"Mensch2"];
MenschTexture2.filteringMode = SKTextureFilteringNearest;
SKAction * Run = [SKAction repeatActionForever:[SKAction animateWithTextures:#[MenschTexture1, MenschTexture2] timePerFrame:0.4]];
Mensch = [SKSpriteNode spriteNodeWithTexture:MenschTexture1];
Mensch.size = CGSizeMake(45, 45);
Mensch.position = CGPointMake(self.frame.size.width / 5, Boden.position.y + 73);
Mensch.zPosition = 2;
Mensch.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Mensch.size];
Mensch.physicsBody.dynamic = YES;
Mensch.physicsBody.allowsRotation = NO;
Mensch.physicsBody.usesPreciseCollisionDetection = YES;
Mensch.physicsBody.restitution = 0;
Mensch.physicsBody.velocity = CGVectorMake(0, 0);
[Mensch runAction:Run];
[self addChild:Mensch];
}
- (void)didMoveToView:(SKView *)view
{
UISwipeGestureRecognizer *recognizerDown = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(handleSwipeDown:)];
recognizerDown.direction = UISwipeGestureRecognizerDirectionDown;
[[self view] addGestureRecognizer:recognizerDown];
}
-(void)handleSwipeDown:(UISwipeGestureRecognizer *)sender
{
[Mensch removeAllActions];
Mensch = [SKSpriteNode spriteNodeWithImageNamed:#"slide7.png"];
NSLog(#"Slide");
}
You are replacing the Mensch object. More specifically, you are replacing the pointer you have to the object, but that doesn't stop it from being displayed in the scene.
You can either:
Replace the SKTexture inside the sprite, which should be displayed immediately. This means Mensch.texture = [SKTexture textureWithImageNamed:#"slide7.png"];
Replace the Sprite. This entails removing the current sprite ([sprite removeFromParent]) and adding the new one ([self addChild:newSprite]).
I think you want the first one, since you don't have to recreate the Mensch object.
Might I add that you are performing a lot of Mensch-specific logic in this object? It would be better (from an Object Oriented standpoint) to move this creation code to an SKSpriteNode subclass Mensch.
#interface Mensch : SKSpriteNode
- (void) displayRunAnimation;
- (void) displaySlideAnimation;
#end
#implementation : Mensch
- (instancetype) init {
self = [super init];
if (self) {
// your initialization code here, including physics body setup
[self displayRunAnimation];
}
return self;
}
- (void) displayRunAnimation {
SKTexture * MenschTexture1 = [SKTexture textureWithImageNamed:#"Mensch1"];
MenschTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture * MenschTexture2 = [SKTexture textureWithImageNamed:#"Mensch2"];
MenschTexture2.filteringMode = SKTextureFilteringNearest;
SKAction * Run = [SKAction repeatActionForever:[SKAction animateWithTextures:#[MenschTexture1, MenschTexture2] timePerFrame:0.4]];
// if you plan to call this often, you want to cache this SKAction, since creating it over and over is a waste of resources
[self runAction:Run];
}
- (void) displaySlideAnimation {
[self removeAllActions];
self.texture = [SKTexture textureWithImageNamed:#"slide7.png"];
NSLog(#"Slide");
// re-start the runAnimation after a time interval
[self performSelector:#selector(displayRunAnimation) withObject:nil afterDelay:4.0];
}

Sprite-Kit Change Image of Node when screen gets touched

There is a hero that is controlled by tapping on the screen. I want the hero too look a little different every time the screen gets touched.
What I did is setting up two images that are a little different. I want the image of the hero to be changed when there is a touch event.
Until now I set up an array to save the information in but it kinda won't work out:
NSMutableArray *heroFrames = [NSMutableArray array];
NSString* textureName = nil;
if (UITouchPhaseBegan) {
textureName = #"hero1.gif";
}
else {
textureName = #"hero2.gif";
}
SKTexture* texture = [SKTexture textureWithImageNamed:textureName];
[heroFrames addObject:texture];
[self setHeroFrames:HeroFrames];
self.hero = [SKSpriteNode spriteNodeWithTexture:[_heroFrames objectAtIndex:1]];
I get an exception when running this.. Any other idea how to achieve my problem?
Thanks guys!
Welcome to SO.
Try this code:
#import "MyScene.h"
#implementation MyScene
{
BOOL myBool;
SKSpriteNode *hero;
SKTexture *texture1;
SKTexture *texture2;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
myBool = false;
texture1 = [SKTexture textureWithImageNamed:#"hero1.gif"];
texture2 = [SKTexture textureWithImageNamed:#"hero2.gif"];
hero = [SKSpriteNode spriteNodeWithTexture:texture1];
hero.position = CGPointMake(200, 150);
[self addChild:hero];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if(myBool == true)
{
hero.texture = texture1;
myBool = false;
} else
{
hero.texture = texture2;
myBool = true;
}
}

How can I make sprite universal in Cocos2d iOS for garbage cleanup?

I'm currently learning Cocos2D and I can't figure out how to access my sprite '_imgStudio' to make it universal so I can clean it up the image after my "removeStudio" timer finishes. I'd like to be able to move both the CCSprite draw functions into the spawnStudio function and to kill it once the timer finishes running.
However, I get an error saying 'Use of undeclared identifier '_imgStudio' so my removeStudio timer right now is pretty much useless.
Here's part of my helloWorldFile.
#implementation GameEngine
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
GameEngine *layer = [GameEngine node];
[scene addChild: layer];
return scene;
}
#pragma mark Main Initializer
//Initalizes GameSetup
-(id) init
{
if( (self=[super init]) ) {
NSLog(#"Game Setup Worked!");
CGSize winSize = [CCDirector sharedDirector].winSize;
CCSprite *_imgStudio = [CCSprite spriteWithFile:#"Studio~ipad.png"];
_imgStudio.opacity = 0;
_imgStudio.position = ccp(winSize.width/2, winSize.height/2);
[_imgStudio runAction:[CCFadeTo actionWithDuration:2.5f opacity:255]];
[self addChild:_imgStudio];
[self scheduleOnce: #selector(spawnStudio:) delay:10];
[self scheduleOnce: #selector(removeStudio:) delay:20];
}
return self;
}
-(void) spawnStudio: (ccTime) dt {
NSLog(#"TEST");
}
-(void) removeStudio: (ccTime) dt {
[_imgStudio removeFromParentAndCleanup:YES];
NSLog(#"ImageRemoved");
}
Thanks!

SKScene.backgroundColor not working. Always default color

Hi I'm setting my SKScene's backgroundColor property in its didMoveToView function like this :
self.backgroundColor = [SKColor colorWithRed:0.2 green:0.2 blue:1.0 alpha:1.0];
But the background colour is always the default grey. I have another simple SpriteKit test project and setting the background colour the same way works.
I've removed all nodes from my scene to rule out the possibility that the background is being overlayed.
Do you know what might be stopping my background colour appearing?
Thanks.
I just tested this and it works as expected for me.
Here's my initial scene, HelloScene.m
#import "HelloScene.h"
#import "NewScene.h"
#interface HelloScene ()
#property BOOL contentCreated;
#end
#implementation HelloScene
- (void)didMoveToView:(SKView *)view
{
if (!self.contentCreated) {
[self createSceneContents];
self.contentCreated = YES;
}
}
- (void)createSceneContents
{
self.backgroundColor = [SKColor colorWithRed:0.333 green:0.780 blue:0.961 alpha:1];
self.scaleMode = SKSceneScaleModeAspectFit;
[self addChild:[self newHelloNode]];
}
- (SKLabelNode *)newHelloNode
{
SKLabelNode *helloNode = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
helloNode.name = #"helloNode";
helloNode.text = #"Hello !";
helloNode.fontSize = 42;
helloNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
return helloNode;
}
#if TARGET_OS_IPHONE
#pragma mark - Event Handling - iOS
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self runHelloNodeAction];
}
#else
#pragma mark - Event Handling - OS X
- (void)mouseDown:(NSEvent *)event
{
[self runHelloNodeAction];
}
#endif
- (void)runHelloNodeAction
{
SKNode *helloNode = [self childNodeWithName:#"helloNode"];
if (helloNode != nil) {
helloNode.name = nil;
SKAction *moveUpAndZoom = [SKAction group:#[[SKAction moveByX:0.0 y:100.0 duration:0.5],
[SKAction scaleTo:1.5 duration:0.5]]];
SKAction *fadeAway = [SKAction fadeOutWithDuration:0.25];
SKAction *remove = [SKAction removeFromParent];
SKAction *moveSequence = [SKAction sequence:#[moveUpAndZoom, fadeAway, remove]];
[helloNode runAction:moveSequence completion:^{
SKScene *newScene = [[NewScene alloc] initWithSize:self.size];
SKTransition *doors = [SKTransition doorsOpenVerticalWithDuration:0.5];
[self.view presentScene:newScene transition:doors];
}];
}
}
#end
Here's my second scene, NewScene.m
#import "NewScene.h"
#interface NewScene ()
#property BOOL contentCreated;
#end
#implementation NewScene
- (void)didMoveToView:(SKView *)view
{
if (!self.contentCreated) {
[self createSceneContents];
self.contentCreated = YES;
}
}
- (void)createSceneContents
{
self.backgroundColor = [SKColor purpleColor];
self.scaleMode = SKSceneScaleModeAspectFit;
}
#end

Resources