How can i move from a SKScene to an UIViewController - ios

i've got a little problem with my spritkit game.
I have created a little Jump N Run game where a penguin must dodge as icebergs or you can also collect fish. As a penguin you have three lives. Are these lives now in the end because you were already met three times of icebergs, I want that one will be automatically redirected to a new UIViewController by then finally can perform new actions. I have everything created with xib files and now just do not know how I made ​​the skscene back to a UIViewController gelange ...
Would be great if someone could help me. :)

The most clean solution is to declare and implement a protocol to let the UIViewController know that it should react to a change in SKScene.
#protocol MySceneDelegate <NSObject>
- (void)closeScene;
#end
#interface MyScene : SKScene
#property(nonatomic, strong) SKShapeNode *ball;
#property (weak) id <MySceneDelegate> delegate;
#end
View controller that shows the scene should implement a closeScene method and set itself as a delegate of the scene. Example:
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
MyScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Set the delegate
[scene setDelegate:self];
// Present the scene.
[skView presentScene:scene];
}
Then in the scene you can call the closeScene method of the view controller which was set as a delegate:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
/* Called when a touch begins */
if ([_delegate respondsToSelector:#selector(closeScene)])
{
[_delegate performSelector:#selector(closeScene)];
}
}
Hope it helps.

Related

Passing Data to SKScene from UIVIewController

What I am trying to accomplish is (should be at least..) very simple: I want to present an SKScene with data that I have in the UIViewController that is presenting this scene. In other words, I have a variable chapter declared as the following:
#import <UIKit/UIKit.h>
#import <SpriteKit/SpriteKit.h>
#interface ViewController : UIViewController
#property (nonatomic) NSInteger chapter;
#end
I have tested with logs that the variable chapter is in fact declared (in my case i am just testing with it as 4).
Here is my ViewController.m file and how I am presenting my scene:
- (void)viewDidLoad {
[super viewDidLoad];
.
.
.
// Configure the view.
SKView *skView = (SKView *)self.view;
if (!skView.scene) {
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
scene = [OLevel sceneWithSize:CGSizeMake(skView.bounds.size.width, skView.bounds.size.height)];
scene.scaleMode = SKSceneScaleModeResizeFill;
// Set the SKScene chapter #
scene.chapter = self.chapter; // <<<< NOT WORKING?
NSLog(#"CHAP TO PASS: %u", self.chapter);
NSLog(#"CHAP RECEIVED: %u", scene.chapter);
// Present the scene.
[skView presentScene:scene];
}
With scene defined as the following, with the viewDidLoad being the one shown above: (I have it subclassed)
#interface ViewController () {
OLevel *scene;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
.
.
.
Here is my OLevel.h class:
#import <SpriteKit/SpriteKit.h>
#import <GameplayKit/GameplayKit.h>
#interface OLevel : SKScene <SKPhysicsContactDelegate, GKAgentDelegate>
#property (nonatomic) NSInteger chapter;
#end
And my OLevel.m*:
- (id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
NSLog(#"Creating scene");
NSLog(#"CHAP: %u", self.chapter);
[self setUpScene];
}
return self;
}
I am very much confused as to why this approach is not working. I have set the property in my ViewController to the desired chapter value, I have then told my Olevel what value it is and to set it's own chapter property to such value, and then present the scene correctly. Why do I continue to get 0??
2016-07-18 23:52:32.189 TESTAPP[2038:684620] CHAP TO PASS: 4
2016-07-18 23:52:32.193 TESTAPP[2038:684620] CHAP RECEIVED: 0
2016-07-18 23:52:32.195 TESTAPP[2038:684620] Creating scene
2016-07-18 23:52:32.195 TESTAPP[2038:684620] CHAP: 0
I know this question has been answered here with the same approach that I had originally used before looking for an answer, but I have yet to get it to work..
If anyone knows what I am doing wrong please help me.
You have initialize your scene with the wrong syntax, without type:
...
// Create and configure the scene.
OLevel *scene = [OLevel sceneWithSize:CGSizeMake(skView.bounds.size.width, skView.bounds.size.height)];
scene.scaleMode = SKSceneScaleModeResizeFill;
// Set the SKScene chapter #
scene.chapter = self.chapter;
...
If you want you can also subclass init method with chapter
OLevel.h:
#interface GameScene : SKScene <SKPhysicsContactDelegate, GKAgentDelegate>
#property (nonatomic) NSInteger chapter;
-(id)initWithSize:(CGSize)size chapter:(NSInteger)chapter;
+(id)sceneWithSize:(CGSize)size chapter:(NSInteger)chapter;
#end
OLevel.m:
#import "GameScene.h"
#implementation GameScene
-(id)initWithSize:(CGSize)size chapter:(NSInteger)chapter {
if (self = [super initWithSize:size]) {
self.chapter = chapter;
//some custom code here
NSLog(#"Creating scene");
NSLog(#"CHAP: %ld", (long)self.chapter);
}
return self;
}
+(id)sceneWithSize:(CGSize)size chapter:(NSInteger)chapter {
return ((GameScene *)[[self alloc] initWithSize:size chapter:chapter]);
}
-(void)didMoveToView:(SKView *)view {
...
}
}
In this case you can create your scene as:
// Create and configure the scene.
scene = [OLevel sceneWithSize:CGSizeMake(skView.bounds.size.width, skView.bounds.size.height) chapter:self.chapter];
scene.scaleMode = SKSceneScaleModeResizeFill;
You can also try to pass your data using the NSNotificationCenter
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:myObject forKey:#"someKey"];
[[NSNotificationCenter defaultCenter] postNotificationName: #"TestNotification" object:nil userInfo:userInfo];

Presenting a SKSpritenode from different class ("addChild")

I have a Class for my Sprite and I'm trying to addChild from that class. The problem is it renders it and doesn't crash - I just can't see the image. I know the images position is right because created and added the child from the gameScene class and it worked and loaded.
I'm thinking this isn't working because its not subclass of SKscene that's why I added the main node from gameScene but didn't solve the problem.
I set up a "mainNode" node
keys is just the name of the sprite and its the class name
I know the image is being rendered its just not visible for some reason.
I know this because if I add no image name not "key_green" and change it to nothing. I get an console msg saying it can't find that image.
gameScene.m
#import "GameScene.h"
#import "Keys.h"
#implementation GameScene
#synthesize mainNode;
-(void)didMoveToView:(SKView *)view {
self.backgroundColor = [UIColor whiteColor];
mainNode = [SKNode node];
[self addChild:mainNode];
Keys * KeysOBJ =[[Keys alloc] init];
[KeysOBJ initKeys];
}
keys.h
#import <SpriteKit/SpriteKit.h>
#import "GameScene.h"
#interface Keys : SKSpriteNode{
SKSpriteNode * key1;
}
#property SKNode* mainNode;
-(void)initKeys;
-(void)loadKeys;
keys.m
#import "Keys.h"
#import "GameScene.h"
#implementation Keys{
}
#synthesize mainNode;
-(void)initKeys{
key1 = [SKSpriteNode spriteNodeWithImageNamed:#"key_red"];
key1.position = CGPointMake((self.frame.size.width)- (self.frame.size.width)+350, (self.frame.size.height)-(self.frame.size.height)+50);
[mainNode addChild:key1];
}
Your mainNode is nil in your Keys because you never set it before you call initKeys. This in theory will get it working.
Keys * KeysOBJ =[[Keys alloc] init];
KeysOBJ.mainNode = mainNode;
[KeysOBJ initKeys];
Also consider this
key1.position = CGPointMake((self.frame.size.width)- (self.frame.size.width)+350, (self.frame.size.height)-(self.frame.size.height)+50);
self doesn't have a size because you just called init and never supplied a texture or a size. Meaning your sprite location will likely be 350,50.
With that being said you are over complicating it. Your Keys shouldn't be a subclass of SKSpriteNode if you are never going to set a texture to it. It should be a SKNode.
Hopefully that makes some sense and gets you pointed back in the right direction.

Accessing variables from another class when importing a .ccbi file

I know this topic is covered in a hundred posts here, but I'm having a lot of trouble with this particular instance and cannot figure it out.
Basically, I'm using Spritebuilder to import sprites/nodes into my game. I import a sprite of some specific class in the body of the GameScene class, but I want to be able to define a variable inside of my sprite class, and then edit it from the GameScene class. For example, if my sprite collects a coin inside the GameScene, I want to change the speed of my sprite inside of the update method in the sprite class.
Below is my code, but unfortunately it isn't working. The variable increaseY and increaseX do not appear to be available in my GameScene class. I know this is because I didn't instantiate the Penguin class properly, but I don't know how to properly create an instance of this class while simultaneously importing the .ccbi file of it. The problem line is commented on and has a bunch of ** next to it to easily find it. It's in GameScene.m. I really appreciate the help, been stuck on this for several hours.
Penguin.h
#import "CCSprite.h"
#interface Penguin : CCSprite
{
float xPosition;
float yPosition;
}
#property (nonatomic,assign) float increaseY;
#property (nonatomic,assign) float increaseX;
#end
Penguin.m
#import "Penguin.h"
#implementation Penguin
#synthesize increaseX;
#synthesize increaseY;
- (id)init {
self = [super init];
if (self) {
CCLOG(#"Penguin created");
}
return self;
}
-(void) update:(CCTime)delta
{
self.position = ccp(self.position.x + increaseX,self.position.y + increaseY);
}
#end
GameScene.h
#import "CCNode.h"
#interface GameScene : CCNode
#end
GameScene.m
#import "GameScene.h"
#import "Penguin.h"
#implementation GameScene
{
CCPhysicsNode *_physicsNode;
CCNode *_catapultArm;
CCNode *_levelNode;
CCNode *_contentNode;
}
// is called when CCB file has completed loading
- (void)didLoadFromCCB {
self.userInteractionEnabled = TRUE;
CCScene *level = [CCBReader loadAsScene:#"Levels/Level1"];
[_levelNode addChild:level];
}
// called on every touch in this scene
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
[self launchPenguin];
}
- (void)launchPenguin {
// loads the Penguin.ccb we have set up in Spritebuilder
CCNode* penguin = [CCBReader load:#"Penguin"];
penguin.position = ccpAdd(_catapultArm.position, ccp(16, 50));
[_physicsNode addChild:penguin];
//THE FOLLOWING LINE DOES NOT WORK********************************
penguin.increaseY = 1;
// Gives Error------Property "increaseX" not found on object of type "CCNode *"
self.position = ccp(0, 0);
CCActionFollow *follow = [CCActionFollow actionWithTarget:penguin worldBoundary:self.boundingBox];
[_contentNode runAction:follow];
}
You have to change this line:
CCNode* penguin = [CCBReader load:#"Penguin"];
To this line:
Penguin* penguin = (Penguin*)[CCBReader load:#"Penguin"];
In the old line you were using the compiler was giving you an error because the CCNode class does not have a property called increaseX. increaseX is part of the Penguin class. If you want to access properties of the Penguin class you need to use a cast and let the compiler know, that what you are loading using the CCBReader is actually a Penguin instance.

Weird Error "No known class method for selector 'sceneWithSize:'"

I've recently started working with Sprite Kit now i've just opened a new sprite kit project and removed the MyScene class as I don't need it. I've then created my own subclass of SKView called PhyscisScene now when i've done this and replaced the code in ViewController.mI get this error on line 24 in ViewController.m: No known class method for selector 'sceneWithSize:' Below is all my Class files i've changed hopefully you can tell me whats going on:
ViewController.m
#import "ViewController.h"
#import "PhysicsScene.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [PhysicsScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskAll;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#end
PhysicsScene.m
#import "PhysicsScene.h"
#implementation PhysicsScene
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
PhysicsScene is a subclass of SKView. It looks like PhysicsScene should be a subclass of SKScene. The solution is to convert PhysicsScene to a subclass of SKScene since that is what it looks like you want to use it for.
Looking at the docs, sceneWithSize is a class method of SKScene, not SKView.

SpriteKit - Return to a persistent scene in the same state it was left in.

The concept of being able to return to a previous scene from where you left off is briefly mentioned here: https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/DesigningGameswithSpriteKit/DesigningGameswithSpriteKit.html
Other than that I can't find any more documentation on the subject. I know in other frameworks like Cocos2D you can pop scenes on a stack, and even have multiple scenes running at the same time.
How do I get this to work in SpriteKit.
I have a game with a swiping menu for character selection. Upon selecting a character the scene changes to another menu. I want users to be able to hit a back button and be presented with the previous scene, where the character they selected is in full view.
At the moment I have it presenting the previous scene as a new one. Which of cause creates it in a fresh state, with the first character in plain view, and not the character they selected.
This should be really simple, but for all my googling, I do not have a clue on how to implement this.
I don't know if anyone is still checking this, but I believe that I have another way to solve the problem.
I made a custom "PauseScene" and the user can set a return scene, meaning that when you are ready to go back, the PauseScene's view presents the return scene. Here's the code:
PauseScene.h
#import "MAScene.h"
#interface PauseScene : MAScene
#property (weak, nonatomic, readonly) SKScene * returnScene;
-(void)setReturnScene:(SKScene *)otherScene;
#end
PauseScene.m
#import "PauseScene.h"
#import "MASpriteButton.h"
#interface PauseScene ()
#property (strong, nonatomic) MASpriteButton * resumeButton;
#end
#implementation PauseScene
-(void)setReturnScene:(SKScene *)otherScene
{
_returnScene = otherScene;
}
-(void)didMoveToView:(SKView *)view
{
[self createContent];
}
-(void)createContent
{
__weak typeof(self) weakMe = self;
MASpriteButton * resume = [[MASpriteButton alloc] initWithBackgroundImageNamed:#"cardback.png" andSelectedImageNamed:#"cardback.png" andCallbackBlock:^{
NSLog(#"removing...");
[weakMe.view presentScene:self.returnScene];
}];
resume.size = CGSizeMake(self.widthToHeightRatio * 20, self.heightToWidthRatio * 20);
resume.position = CGPointMake(self.size.width/2, self.size.height/2);
self.resumeButton = resume;
[self addChild:self.resumeButton];
[self setBackgroundColor:[UIColor blueColor]];
}
#end
The way I used it was that when the user hits a pause button in the gameplay scene, I call these lines:
self.paused = YES;
PauseScene * pScene = [[PauseScene alloc] initWithSize:self.size];
[pScene setReturnScene:self];
[self.view presentScene:pScene];
Note that self in this little block is the gameplay scene.
By having the pause scene keep a weak pointer to the gameplay scene, it can keep a pointer to the return scene without dealloc-ing it when the pause scene is dealloc-ed.
PS The MAScene class is just a little extension of the SKScene class, I just added a couple things to it. If anyone wants that too just let me know.
Assuming the menu scene is also implemented in Sprite Kit, you could create a modal view controller, present it, and put the Sprit Kit scene over that modal view.
So, specifically, create a new UIViewController inheriting class MenuViewController, and a new SKScene inheriting class, MenuScene. The class 'MenuScene' MenuScene should be the Scene you're looking to present. Hook up MenuScene and MenuViewController like you would normally hook up an SKScene and its view controller. Make sure you have the original scene's view controller as a property of the original SKScene.
Wherever you want to present this menu, you can call from the original SKScene:
MenuViewController *modalViewController = [[MenuViewController alloc] init];
[self.viewController presentModalViewController:modalViewController];
There are simpler ways to just transition b/w SKScene instances, but if you want to keep the intial SKScene running in the background, this is what you would have to do, I believe.
You just should keep strong pointer to the leaving scene before you call new
Here is example of realisation via singleton (actually usual singleton with scene pointer, no magic)
#interface Global : NSObject
+ (Global*)sharedInstance;
#property (strong, nonatomic) SKScene *mainScene;
#end
And .m file
#implementation Global
+ (instancetype) sharedInstance{
static Global *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[Global alloc] init];
});
return _sharedInstance;
}
#end

Resources