SKScene iPad height width reversed - ios

I am trying to fill my SKScene with tiles, in an iPad app that only supports landscape mode. Within the scene I detect h & w as such:
int h = [UIScreen mainScreen].bounds.size.height;
int w = [UIScreen mainScreen].bounds.size.width;
Unfortunately, the dimensions sent back are the reverse of what they need to be. There are lots of topics in SO discussion this problem within the content of a view controller or a view, and the solution seems to be to detect screen size in viewWillAppear, not in viewDidLoad. This does not seem to be a valid solution for SKScenes, however.
Any suggestions?

Try, not using viewDidLoad and use this
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
SKView * skView = (SKView *)self.view;
if (!skView.scene) {
skView.showsFPS = YES;
skView.showsNodeCount = YES;
SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
}

It doesn't make sense to initialize the scene in the layout handler. Resize the scene in the layout handler. (That's what it's for.)
#implementation SGOMyScene
{
SKScene *scene;
}
- (void)viewDidLoad
{
[super viewDidLoad];
SKView *skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
scene = [SGOMyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene];
}
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
SKView *skView = (SKView *)self.view;
scene.size = skView.bounds.size;
}
Your scene should implement - (void)didChangeSize:(CGSize)oldSize to move everything into the right place.
Hooray, now your game handles device rotations too.

Related

SKVideoNode with AVPlayer is playing but not visible on iOS 9.0

I have a problem with Xcode 7.0 (7A220) and iOS 9.0. I add a video (.mp4 or mov, both not working) with an AVPlayer and SKVideoNode. On the simulator with iOS 8.4 it's working fine, but on iOS 9.0 (also simulator) the video is not showing. I can hear the audio though.
The code in my GameScene (of class SKScene):
-(void)didMoveToView:(SKView *)view {
NSURL *fileURL = [NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource:#"VideoName" ofType:#"mp4"]];
_player = [AVPlayer playerWithURL: fileURL];
_videoNode = [[SKVideoNode alloc] initWithAVPlayer:_player];
_videoNode.size = CGSizeMake(200, 100);
_videoNode.position = CGPointMake(200, 200);
_videoNode.zPosition = 1000;
[self addChild:_videoNode];
_player.volume = 0.5;
[_videoNode play];
}
In the UIViewController I have this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
/* Sprite Kit applies additional optimizations to improve rendering performance */
// I have set this to NO, but it doesn't help
skView.ignoresSiblingOrder = NO;
// Create and configure the scene.
CGRect rect = [[UIScreen mainScreen] bounds];
GameScene *videoScene = [[GameScene alloc] initWithSize:rect.size];
videoScene.scaleMode = SKSceneScaleModeAspectFill;
GameScene *scene = [GameScene unarchiveFromFile:#"GameScene"];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:videoScene];
}
So the app is not crashing, the video is playing (I can hear it), but the video is invisible on iOS 9 only. Does someone have an idea to fix it?

SpriteKit background not fitting to screen on iPhone 6 plus

I am using a scale mode fill to load a scene with a background with size of 640x1136 as follows:
SKView * skView = (SKView *)self.view;
skView.ignoresSiblingOrder = YES;
MainMenu *scene = [MainMenu sceneWithSize: self.view.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene];
I am then loading the background inside the screen:
SKSpriteNode *menuBg = [SKSpriteNode spriteNodeWithImageNamed:#"mainBg"];
menuBg.anchorPoint = CGPointZero;
menuBg.position = CGPointZero;
[self addChild:menuBg];
And I expected the backgorund to fill the screen, however it won't resize to fill the screen. I am only holding a #2x background image - what am I doing wrong?
You are not positioning the node in the center of the scene and therefore it won't be scaled correctly with the Aspect Fill scalemode.
Try this instead:
menuBg.anchorPoint = CGPointMake(0.5, 0.5);
menuBg.position = CGPointMake(self.size.width/2, self.size.height/2)
It will set the anchor to the center of your node, and position the node in the center of the scene.

Objective-C. How is your first SKScene initialised?

This method sets up the initial scene and is called as soon as the application opens:
#import "GameScene.h"
#import "WarScene.h"
#import "Math.h"
#implementation GameScene
{
SKNode *map;
float oldY;
float oldX;
BOOL buttonClicked;
int thisButton;
float oldMapPosition;
float midScreenX;
float midScreenY;
int separation;
float sendY;
BOOL interacting;
CGVector speed;
float oldPosition;
float newPosition;
BOOL isReleased;
int iterations;
float diff;
BOOL comeToStop;
BOOL actualStop;
BOOL clicked;
}
int numberOfLevels = 20;
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
map = [SKNode node];
separation = [UIScreen mainScreen].bounds.size.height;
if (UIDeviceOrientationIsLandscape((UIDeviceOrientation)[[UIApplication sharedApplication] statusBarOrientation]))
{
//landscape mode
midScreenX = ([[UIScreen mainScreen]bounds].size.width>[[UIScreen mainScreen]bounds].size.height?[[UIScreen mainScreen]bounds].size.width:[[UIScreen mainScreen]bounds].size.height)/2;
midScreenY = ([[UIScreen mainScreen]bounds].size.width<[[UIScreen mainScreen]bounds].size.height?[[UIScreen mainScreen]bounds].size.width:[[UIScreen mainScreen]bounds].size.height)/2;
}
else
{
//portrait mode
midScreenX = [[UIScreen mainScreen]bounds].size.width/2;
midScreenY = [[UIScreen mainScreen]bounds].size.height/2;
}
NSLog(#"Initial Width: %f Height: %f", midScreenX*2, midScreenY*2);
map.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize: CGSizeMake([UIScreen mainScreen].bounds.size.width, separation*(numberOfLevels+1))];
map.physicsBody.affectedByGravity = NO;
map.physicsBody.allowsRotation = NO;
clicked = NO;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(handleTap:)];
tap.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tap];
self.backgroundColor = [SKColor blackColor];
for (int i = 0; i<numberOfLevels; i += 1) {
SKLabelNode *title;
{
title = [SKLabelNode labelNodeWithFontNamed:#"Cochin"];
title.text = [NSString stringWithFormat: #"Level %d", 1+i];
title.fontSize = 60;
title.position = CGPointMake(CGRectGetMidX(self.frame),
((i+1)*separation));
title.fontColor = [UIColor whiteColor];
title.name = [NSString stringWithFormat: #"Level %d", 1+i];
[map addChild:title];
}
}
[self addChild:map];
}
This works exactly as I intended it to, however, when I call this from another class:
-(void)continueMethod:(UIButton*)button{
NSLog(#"InitwithSize to GameScene Width: %f Height: %f", midScreenX*2, midScreenY*2);
GameScene *map = [[GameScene alloc] initWithSize: CGSizeMake(midScreenX*2 ,midScreenY*2)];
SKTransition *reveal = [SKTransition revealWithDirection:SKTransitionDirectionDown duration:1.0];
SKView * skView = (SKView *)self.view;
UIView *viewToRemove = [self.view viewWithTag:3];
[viewToRemove removeFromSuperview];
[skView presentScene:map transition:reveal];
}
The scene which should be set up doesn't appear as I intended it to, or indeed how it used to look the last time that it was initialised. The separation variable appears bigger, the text appears bigger and everything is wider. I've verified that the UIScreen that I have initialised is exactly the same throughout.
This led me to question how the initial scene of a SKSprite application comes to be and whether it is different to "initWithSize:" however, looking through all the code that comes in the game template I can't ever see my first scene get called. The closest that I can find to initWithSize: is the following from the GameViewController-
GameScene *scene = [GameScene unarchiveFromFile:#"GameScene"];
scene.scaleMode = SKSceneScaleModeAspectFill;
Firstly, is this what is initialising my initial scene? Secondly is it in anyway different to initWithSize: ? And finally if the answer to the previous tow questions is yes, should it be used instead of initWithsize: ?
Yes, this is the initialization of the initial scene! What you are seeing in the View Controller is a slightly different initialization. You are using the sks file, first of all, and initWithCoder: is being called when the view controller unarchives it. However, this isn't very different from initWithSize if you specify all of the same dimensions and properties.
That being said, the scene's scaleMode in your initialization and the view controller's scaleMode is different. As you can see, the view controller specifies:
scene.scaleMode = SKSceneScaleModeAspectFill;
The AspectFill scale mode according to Apple:
The scaling factor of each dimension is calculated and the smaller of the two is chosen. Each axis of the scene is scaled by the same scaling factor. This guarantees that the entire scene is visible but may require letterboxing in the view.
When you initialize your scene, on the other hand, you leave the default scaleMode, which happens to be SKSceneScaleModeFill. This maps each axis of the scene to the view, so you get a distorted scene. What you need to do is simply state:
map.scaleMode = SKSceneScaleModeAspectFill;
This will use the same scale mode as in the original scene and the distortion that you see now will be gone.
Hope this helps, let me know if you have questions.

SKSpriteNode position

I am trying to (learn how to) build a game using SpriteKit and i ran into a problem that has been bugging me for hours.
I have a class named Tank with the following constructor:
+ (instancetype)tankAtPosition:(CGPoint)position {
Tank *tank = [self spriteNodeWithImageNamed:#"tank_base_1"];
tank.position = position;
tank.anchorPoint = CGPointMake(0.5, 0.5);
return tank;
}
In my scene i have the following constructor:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:#"level_bg_1"];
background.xScale = 0.40f;
background.yScale = 0.40f;
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:background];
Tank *tank = [Tank tankAtPosition:CGPointMake(CGRectGetMidX(self.frame), 512)];
[self addChild:tank];
}
return self;
}
which compiled results in the following render:
Everything ok for now, however, if i change the y of the tank to 256:
Tank *tank = [Tank tankAtPosition:CGPointMake(CGRectGetMidX(self.frame), 256)];
I get this:
As far as i know, the bottom is y = 0 and and the middle point y = 512, so when i specify a y = 256 it should be centered in the bottom half of the screen. Why is is near the edge?
The testing device is a ipad retina mini and in the Deployment Info > Devices i specified iPad.
What am i missing? Thank you.
i figured it out. the frame size was all messed up because i set my game to run in landscape only. solution: initialize the scene in viewDidLayoutSubviews instend of viewDidLoad
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [Level sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}

SKView bounds.size get inverted on touch event (Sprite Kit)

Please help me understand the following behavior (iOS Sprite Kit).
The output of the code I present below is:
1.skView.bounds.size = 768.00, 1024.00
2.skView.bounds.size = 1024.00, 768.00
As seen above, width and height are switched between the two methods, and that causes my second scene to be presented not in the correct scale.
My game will run in landscape mode only, which means that in fact, the second width x height ratio is the correct one (though it is the first that renders the scene in the correct aspect ratio, which in itself is a mystery to me).
Can anyone tell me how I can fix this behavior? What am I doing wrong?
- (void)viewDidLoad
{
[super viewDidLoad];
self.currentGameState = GS_INTRO_SCENE;
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
printf("1.skView.bounds.size = %.2f, %.2f\n", skView.bounds.size.width, skView.bounds.size.height);
SKScene *scene = [SKTGameIntroScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.currentGameState == GS_INTRO_SCENE) {
self.currentGameState = GS_GAME_PLAY;
SKView * skView = (SKView *)self.view;
printf("2.skView.bounds.size = %.2f, %.2f\n", skView.bounds.size.width, skView.bounds.size.height);
SKScene *nextScene = [SKTMyScene sceneWithSize:skView.bounds.size];
nextScene.scaleMode = SKSceneScaleModeAspectFill;
SKTransition *fadeIn = [SKTransition fadeWithDuration:5.0f];
[skView presentScene:nextScene transition:fadeIn];
}
}
I greatly appreciate it in advance.
** edit: **
#giorashc solved my problem suggesting I move the scene initiation to -(void)viewWillLayoutSubviews method.
But my second scene remains stretched.... here is the full code for SKTViewController.m after the change (can anyone say why second view is still stretched?):
//
// SKTViewController.m
// SpriteKitTest
//
// Created by Claudia Dazcal on 13/07/14.
// Copyright (c) 2014 DazcalFamily_inc. All rights reserved.
//
#import "SKTViewController.h"
#import "SKTMyScene.h"
#include "SKTGameIntroScene.h"
typedef enum
{
GS_INTRO_SCENE,
GS_GAME_PLAY
}GameStates;
#interface SKTViewController ()
#property GameStates currentGameState;
#property BOOL firstSceneLoaded;
#end
#implementation SKTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.currentGameState = GS_INTRO_SCENE;
self.firstSceneLoaded = NO;
}
-(void)viewWillLayoutSubviews
{
if (!self.firstSceneLoaded) {
self.firstSceneLoaded = YES;
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
printf("1.skView.bounds.size = %.2f, %.2f\n", skView.bounds.size.width, skView.bounds.size.height);
SKScene *scene = [SKTGameIntroScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.currentGameState == GS_INTRO_SCENE) {
self.currentGameState = GS_GAME_PLAY;
SKView * skView = (SKView *)self.view;
printf("2.skView.bounds.size = %.2f, %.2f\n", skView.bounds.size.width, skView.bounds.size.height);
//CGSize DebugSize = CGSizeMake(skView.bounds.size.height, skView.bounds.size.width);
//SKScene *nextScene = [SKTMyScene sceneWithSize:DebugSize];
SKScene *nextScene = [SKTMyScene sceneWithSize:skView.bounds.size];
nextScene.scaleMode = SKSceneScaleModeAspectFill;
//SKTransition *fadeIn = [SKTransition fadeWithDuration:5.0f];
//[skView presentScene:nextScene transition:fadeIn];
[skView presentScene:nextScene];
}
}
- (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
* edit 2 **
Ok, it`s all sorted out now.
#giorashc solved my problem right away when he suggested moving the scene initiation to viewWillLayoutSubviews. It turns out that since I was previously working with the wrong bounds, I set up my original scene with the wrong values and now that it was fixed, it looked stretched.
But it`s fixed now. Thank you so much #giorashc!!
Initialize your scene in the viewWillLayoutSubviews method. In this method the view bounds are updated correctly.
Make sure though that you initialize the scene only once as this method is called whenever the view controller presents another view controller or the application is changing its orientation.
In the first method the view hasn't rotated to landscape mode yet. In the second it has rotated to landscape mode so is now reporting bounds that are wider than it is tall.

Resources