Problems loading scene async with button animation - objective-c

I was trying to implement an animated start button in my main menu. Because my scene takes a while to load, I want to bridge the waiting time with that button animation. Unfortunately the animation does not start. What is the problem with my code?
-(void)buttonAnimation{
SKAction *HUDzoom = [SKAction scaleTo:3 duration:1];
SKAction *HUDzoomOut = [SKAction scaleTo:1.0 duration:1];
SKAction *HUDAnimation = [SKAction sequence:#[HUDzoom, HUDzoomOut]];
[self.startButton runAction:[SKAction repeatActionForever:HUDAnimation]];
}
-(void)loadScene{
SKScene *restart = [[Level_1 alloc] initWithSize:self.size];
[self.view presentScene:restart];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"startLevel1"]){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self loadScene];
dispatch_async(dispatch_get_main_queue(), ^{
[self buttonAnimation];
});
});
}
}

That's because you're loading the scene asynchronically, and only after that is done you start the button animation asynchronically:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// loading the scene
[self loadScene];
// when scene has finished loading, animate the button asynchronically
// (this makes no sense)
dispatch_async(dispatch_get_main_queue(), ^{
[self buttonAnimation];
});
});
Instead you should start the animation, then load the scene asynchronically.
[self buttonAnimation];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self loadScene];
});
The button is animated by Sprite Kit actions, while you can start the animation asynchronically it won't make the entire animation asynchronically. Instead you just need to ensure that any blocking methods like loadScene run asynchronically.

Related

SpriteKit + xcode6 present scene crash app

I am creating a simple MainMenu scene and on "play" button select I move to the Game scene as follows:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"play"]) {
SKScene *gameScene = [[GameScene alloc] initWithSize:self.size];
SKTransition *fadeTransition = [SKTransition fadeWithColor:[UIColor blackColor] duration:0.3];
[self.view presentScene:gameScene transition:fadeTransition];
}
}
}
After the transition app crash with EXC_BAD_ACCESS (code=1). I am currently running Xcode6 + SpritKit/Objective-C
Found the problem - apparently caused by particle targetNode assignment:
starParticle.targetNode = self.scene;
which probably should have been released before presenting other scene

Testing If an object is nil or not

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"play"]){
NSLog(#"play was touched");
SKScene *mainGameScene = [[MainGame alloc] initWithSize:self.size];
if (!mainGameScene){
SKTransition *transition = [SKTransition fadeWithColor:[UIColor blackColor] duration:0.5];
[self.view presentScene:mainGameScene transition:transition];
}
}
}
From my understanding the code above checks if mainGameScene is nil and if it is it then goes through the if statement.
Is this beneficial at all or is it just a waste of code because if the method that this code is in is called multiple times won't it create new objects of SKScene?
It is used to check if mainGameScene is initialized, because you can't presentScene if it is nil.
if you want only to check if object is initialized, then
SKScene *mainGameScene;
//creates new variable - place it at the beginning of the code, before #implementation and #interface
if(mainGameScene){
// it is already inited (you have already tapped once), it will be called always, since it is inited at else
//it is called when you tap the second, the third time, etcetera
SKTransition *transition = [SKTransition fadeWithColor:[UIColor blackColor] duration:0.5];
[self.view presentScene:mainGameScene transition:transition];
}else
{
//Calls only once, when you first touching the screen, and initing mainGameScene.
//It won't be called ANYMORE, since it is inited.
mainGameScene = [[MainGame alloc] initWithSize:self.size];
}
The code you have now is useless, as it is trying to do something with object, that isn't initialized.In my case, your mainGameScene won't be reinitialized, once it is inited.

SK Scene transition background

I've just started to learn how to program with xCode and more specifically with the new SpriteKit. So bare in mind I'm kind of new to this.
I've managed to move to a different scene by pressing on a label, but somehow the background of my scene has gone all wrong. the background should be like the first scene, but it gave me a background with two horizontal bars. I don't think I've done anything wrong since I've just copied the code needed to set the background to the second scene.
1st scene:
http://imageshack.us/photo/my-images/23/kyud.png/
2nd scene:
http://imageshack.us/photo/my-images/198/472h.png/
my code used to set up both backgrounds:
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:#"background_start"];
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
background.xScale = 0.7;
background.yScale = 0.7;
[self addChild:background];
transition is done with:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if([node.name isEqualToString:#"startgame"]){
Options_Scene *gs = [[Options_Scene alloc] initWithSize:self.size];
SKTransition *doors = [SKTransition doorsOpenVerticalWithDuration:0.5];
[self.view presentScene:gs transition:doors];
}
}
PS: I find it odd why I have to scale it to 0.7 when the background actually has the dimensions 480x320.
As you are using landscape I would swap the
viewWillAppear
With
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
SKView * skView = (SKView *)self.view;
if (!skView.scene) {
//skView.showsFPS = YES;
//skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [PMyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
}

Scrolling with two finger gesture

I have a drawing view on a UIScrollView.
What I want to do is draw lines with one finger, and scrolling with two fingers.
The drawing view is to draw lines through touchesMoved as below.
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch;
CGPoint lastTouch, currentTouch;
for (touch in touches)
{
lastTouch = [touch previousLocationInView:self];
currentTouch = [touch locationInView:self];
CGContextRef ctx = CGLayerGetContext(drawLayer);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, lastTouch.x, lastTouch.y);
CGContextAddLineToPoint(ctx, currentTouch.x, currentTouch.y);
CGContextStrokePath(ctx);
}
[self setNeedsDisplay];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGContextDrawLayerInRect(drawContext, self.bounds, drawLayer);
CGContextClearRect(CGLayerGetContext(drawLayer), self.bounds);
[self setNeedsDisplay];
}
and on a viewController,
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
[_scrollView setContentSize:CGSizeMake(320, 800)];
[self.view addSubview:_scrollView];
_drawingView = [[DrawingView alloc] initWithFrame:CGRectMake(0, 0, 320, 800)];
[_scrollView addSubview:_drawingView];
for (UIGestureRecognizer *gestureRecognizer in _scrollView.gestureRecognizers)
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
{
UIPanGestureRecognizer *panGR = (UIPanGestureRecognizer *) gestureRecognizer;
panGR.minimumNumberOfTouches = 2;
}
}
It works ok on simulator however the drawing is too slow on a real device. What is wrong and any suggestion?
Ty!
I solved.
Shouldn't draw whole screen with [self setNeedsDisplay]. Should draw a area where need to redraw with [self setNeedsDisplay withRect:]
Better use panGesture recogniger than touchesBegin~End. There's delay between touchesBegin and touchesEnd.

CCPanZoomController + Tappable Sprites

I am creating a zoomable and pan-able map for my game (using CCPanZoomController) . Within this map I would like to have a tappable sprite, and when it is tapped, "do something"…
I can get the two things to work perfectly in separate projects, however when I try to combine them, nothing happens when I tap my sprite.
I have included an image to demonstrate further:
//in my init section
self.isTouchEnabled = YES;
mapBase = [CCSprite spriteWithFile:#"MapBase.png"];
mapBase.anchorPoint = ccp(0, 0);
[self addChild:mapBase z:-10];
gym = [CCSprite spriteWithFile:#"Gym.png"];
gym.scale = 0.3;
gym.position = ccp(1620, 250);
[self addChild:gym z:1];
CGRect boundingRect = CGRectMake(0, 0, 2499, 1753);
_controller = [[CCPanZoomController controllerWithNode:self] retain];
_controller.boundingRect = boundingRect;
_controller.zoomOutLimit = _controller.optimalZoomOutLimit;
_controller.zoomInLimit = 2.0f;
[_controller enableWithTouchPriority:1 swallowsTouches:YES];
//end of init
-(void) registerWithTouchDispatcher
{
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self
priority:0 swallowsTouches:NO];
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchPoint1 = [touch locationInView:[touch view]];
if (CGRectContainsPoint(gym.boundingBox, touchPoint1)) return YES;
return NO;
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchPoint2 = [touch locationInView:[touch view]];
if (CGRectContainsPoint(gym.boundingBox, touchPoint2)){
CCLOG(#"SPRITE HAS BEEN TAPPED");
}
}
I want to beable to zoom in/out and pan the wholemap (including the ‘Gym’ sprite).
And if the sprite 'Gym' is tapped by the user, then i would like to “do something”.
If anyone can figure this out, I would be extremely grateful!
Thanks.