Forward declaration in SpriteKit - ios

I have an issue with forward declaration in SKScene I have to show a label node's value from game scene to game over scene , for example , but the value it returns as null , here is my code :
GameOver Scene :
#import "MyScene.h"
#class MyScene;
#interface GameOver : SKScene {
MyScene *mainScene;
}
#implementation GameOver
- (void)didMoveToView:(SKView *)view {
scores = [[SKLabelNode alloc]initWithFontNamed:#"Pixel LCD7"];
scores.fontSize = 30;
scores.fontColor = [SKColor darkGrayColor];
//displaying score :
scores.text = [NSString
stringWithFormat:#"Score:%#",mainScene.scoreLabel.text];
scores.name = #"score";
scores.position = CGPointMake(CGRectGetMidX(self.frame), 230);
[self addChild:scores];
}
MyScene
#import "MyScene.h"
#class GameOver;
#interface MyScene : SKScene {
SKLabelNode *scoreLabel;
}
#property (nonatomic) SKLabelNode *scoreLabel;

It doesn't work because you initialise the label in wrong method.
Remove didMoveToView: method if you use it just to initialise and set up the label and move the code to initWithSize: method:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
// Your init code here
scores = [[SKLabelNode alloc]initWithFontNamed:#"Pixel LCD7"];
scores.fontSize = 30;
scores.fontColor = [SKColor darkGrayColor];
//displaying score :
scores.text = [NSString stringWithFormat:#"Score:%#",mainScene.scoreLabel.text];
scores.name = #"score";
scores.position = CGPointMake(CGRectGetMidX(self.frame), 230);
self addChild:scores];
}
return self;
}
You should add property to your GameOver Scene to accept your score or override initWithSize: to for example initWithSize:score: and you should update your score label when you initialise game over scene.

Related

Draw rectangle in spritekit

I'm trying to draw an empty rectangle with a SKShapeNode but does appear on the screen. If i set a color to the fill property the rectangle appears. What could be the issue?
//MyScene.h
#import <SpriteKit/SpriteKit.h>
#interface MyScene : SKScene
#end
//MyScene.m
#import "MyScene.h"
#implementation MyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKShapeNode *shapeNode = [SKShapeNode shapeNodeWithRectOfSize:CGSizeMake(self.frame.size.width/2, self.frame.size.height/2)];
shapeNode.strokeColor = [SKColor redColor];
shapeNode.lineWidth = 3;
[self addChild:shapeNode];
}
return self;
}
#end

Score move to otherscene from skscene

I can't receive score in other scene. I setup #propery but it doesn't works. Score is "0" every time it not change.
My codes here.
OtherScene.h
#interface OtherScene : SKScene
#propery NSUInteger score;
#end
OtherScene.m
#implementation OtherScene
{
SKLabelNode *scoreLabel;
}
-(void)addScoreLabel
{
scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
scoreLabel.text = [NSString stringWithFormat:#"SCORE: %lu", (unsigned long)self.score];
scoreLabel.position = CGPointMake(500, 50);
scoreLabel.name = #"gameOverScore";
[self addChild:scoreLabel];
}
MyScene.m
#interface MyScene ()<SKPhysicsContactDelegate>
#property NSUInteger score;
#end
- (void)gameOver
{
GameOverScene *gameOverScene = [GameOverScene sceneWithSize:self.size];
gameOverScene.score = self.score;
[self.view presentScene:gameOverScene transition:[SKTransition pushWithDirection:SKTransitionDirectionLeft duration:0.5]];
}
When/where do you call addScoreLabel?
If this is in OtherScene init you're effectively running it in the line
GameOverScene *gameOverScene = [GameOverScene sceneWithSize:self.size];
Meaning: before you assigned the score. You could simply call the update label method from MyScene after assigning the score:
GameOverScene *gameOverScene = [GameOverScene sceneWithSize:self.size];
gameOverScene.score = self.score;
[gameOverScene addScoreLabel];
Or even better, do this in a custom property setter method in OtherScene, ie setScore:(NSUInteger)score and separate creating the label from updating its score string to be more flexible.

ios 7 SKScene size vs visible area?

Apple's documentation has me a bit confused.
According to the documentation page : https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Nodes/Nodes.html the relationship between the visible area of the scene in points and the size of the scene should be the same according to the sentence :
"The size of the scene specifies the size of the visible portion of
the scene in points".
I created a custom scene that gets its size from the size of the device's screen (in my case I've been testing this for the iPad in portrait mode so the size should be 768 wide by 1024 high).
The following line which is being called in the scene's createContent method
NSLog(#"self.size : %f %f", self.size.width, self.size.height);
Returns
2014-07-15 14:53:28.844 SpriteWalkthrough[15888:90b] self.size : 768.000000 1024.000000
as expected.
However when I try to draw a SKSpriteNode at the position(self.size.width/2,self.size.height/2) the node is drawn in the upper right hand corner of the screen, not in the middle.
Why is this happening?
For other people who may be making similar mistakes drawing SpriteNodes to a scene, take a look at my source code for the scene, and in particular pay attention to the //self.sCar.car.position = self.sCar.position;line.
FoolAroundScene.h
#import <SpriteKit/SpriteKit.h>
#interface FoolAroundScene : SKScene
#end
FoolAroundScene.m
#import "FoolAroundScene.h"
#import "ScrollingBackground.h"
#import "SpriteCar.h"
#define BACKGROUND_NAME #"clouds.jpg"
#interface FoolAroundScene ()
#property BOOL contentCreated;
#property (strong, nonatomic) ScrollingBackground * sbg;
#property (strong, nonatomic) SpriteCar * sCar;
#end
#implementation FoolAroundScene
-(void) didMoveToView:(SKView *)view
{
if (!self.contentCreated) {
[self createContent];
self.contentCreated = !self.contentCreated;
}
}
-(void)createContent
{
self.sbg = [[ScrollingBackground alloc] initWithBackgroundImage:BACKGROUND_NAME size:self.size speed:2.0];
self.sCar = [[SpriteCar alloc] init];
[self addChild:self.sbg];
[self addChild:self.sCar];
self.sCar.position = CGPointMake(self.size.width/2, self.size.height/2);
//self.sCar.car.position = self.sCar.position;
SKSpriteNode * spriteCar = [self.sCar makeCarSprite];
spriteCar.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
NSLog(#"anchor point : %f %f", self.anchorPoint.x, self.anchorPoint.y);
NSLog(#"self.size : %f %f", self.size.width, self.size.height);
NSLog(#"self.scalemode : %d", self.scaleMode);
}
-(void) update:(NSTimeInterval)currentTime
{
[self.sbg update:currentTime];
}
#end
Even though I had set the sCar's position property to the middle of the scene, by setting its child node (the sCar's car property) position to the same value, the child's position will not be the same as its parent's. This is because the child's position is relative to its parent's.
Explained another way : the parent's position in the context of the scene was (384,512) and the child's position in the context of its parent was (384,512). However this means that the child's position in the context of the scene was actually (768,1024), which is why the car was being drawn in the upper right hand corner of the screen.
Also, in case anyone wants the implementation for the sprite car, here it is. A crappily drawn car that can be used to get a grip on how the Sprite Kit works.
SpriteCar.h
#import <SpriteKit/SpriteKit.h>
#interface SpriteCar : SKNode
-(id)init;
-(SKSpriteNode *)makeCarSprite;
#end
SpriteCar.m
#import "SpriteCar.h"
#interface SpriteCar ()
#property (nonatomic, strong) SKSpriteNode * car;
#end
#implementation SpriteCar
-(id) init
{
self = [super init];
if (self) {
self.car = [self makeCarSprite];
[self addChild:self.car];
}
return self;
}
-(SKSpriteNode *) makeCarSprite
{
SKSpriteNode * carBody1 = [[SKSpriteNode alloc] initWithColor:[SKColor redColor] size:CGSizeMake(64.0, 24.0)];
SKSpriteNode * carBody2 = [[SKSpriteNode alloc] initWithColor:[SKColor redColor] size:CGSizeMake(32.0, 32.0)];
SKSpriteNode * wheel1 = [[SKSpriteNode alloc] initWithColor:[SKColor blackColor] size:CGSizeMake(8.0, 8.0)];
SKSpriteNode * wheel2 = [wheel1 copy];
SKSpriteNode * light = [[SKSpriteNode alloc] initWithColor:[SKColor yellowColor] size:CGSizeMake(6.0, 6.0)];
carBody2.position = carBody1.position;
wheel1.position = CGPointMake(30.0, -30);
wheel2.position = CGPointMake(-30.0, -30.0);
light.position = CGPointMake(32.0, 11.0);
[carBody1 addChild:carBody2];
[carBody1 addChild:wheel1];
[carBody1 addChild:wheel2];
[carBody1 addChild:light];
SKAction * hover = [SKAction sequence:#[[SKAction moveByX:0.0 y:5.0 duration:0.1],
[SKAction waitForDuration:0.05],
[SKAction moveByX:0.0 y:-5.0 duration:0.1],
[SKAction waitForDuration:0.05]]];
[carBody1 runAction:[SKAction repeatActionForever:hover]];
return carBody1;
}
#end

Adding the NSObject to the a SKScene

I recently making a racing game with using the SpriteKit. I create an NSObject class called "GameObject" to store all the properties, like physics. I also create another NSObject class called "GameWorld" to store all the game objects, like creating player objects and other objects with their location and the functions of the direction buttons. However, when I start to write a game scene class, I cannot add the world object to the scene, which means the car doesn't show up in the scene. Would anyone can help me about this question? The codes of my SKScene class is provided below,
#interface GamePlay : SKScene
#property (nonatomic) GameWorld *world;
-(void)update:(NSTimeInterval)currentTime;
-(id) initWithSize:(CGSize)s andWorld:(GameWorld *)w;
#end
#implementation GamePlay
- (id) initWithSize:(CGSize)s andWorld:(GameWorld *)w
{
self = [super initWithSize:s];
if (self)
{
_world = w;
}
return self;
}
-(void) didMoveToView:(SKView *)view
{
if (!self.contentCreated ) {
[self createSceneContents];
self.contentCreated = YES;
}
}
-(void) createSceneContents
{
// turn off gravity for the world
self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
self.scaleMode = SKSceneScaleModeAspectFit;
//Create the background
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:#"road.png"];
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:background];
//Create the buttons for directions
SKSpriteNode *up = [SKSpriteNode spriteNodeWithImageNamed:#"Up.png"];
up.position = CGPointMake(290, 115);
up.name = #"upDirection";
[self addChild:up];
SKSpriteNode *down = [SKSpriteNode spriteNodeWithImageNamed:#"Down.png"];
down.position = CGPointMake(290, 40);
down.name = #"downDirection";
[self addChild:down];
SKSpriteNode *left = [SKSpriteNode spriteNodeWithImageNamed:#"Left.png"];
left.position = CGPointMake(30, 40);
left.name = #"leftDirection";
[self addChild:left];
SKSpriteNode *right = [SKSpriteNode spriteNodeWithImageNamed:#"Right.png"];
right.position = CGPointMake(CGRectGetMidX(self.frame), 40);
right.name = #"rightDirection";
[self addChild:right];
// add objects from GameWorld to this scene using the world's worldNode property
// Here is the place I confuse. the world doesn't show up
[self addChild:_world.worldNode];
}
//making buttons for the cars
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
//setting a node to keep the position of each direction buttons
SKNode *n = [self nodeAtPoint:positionInScene];
NSLog(#"Node n:%#", n);
//compare the location of each SKSpriteNode and the touch location
SKNode *up = [self childNodeWithName:#"upDirection"];
if (n == up) {
[self.world goForward];
}
SKNode *down =[self childNodeWithName:#"downDirection"];
if (n == down) {
[self.world goBackward];
}
SKNode *left = [self childNodeWithName:#"leftDirection"];
if (n == left) {
[self.world goLeft];
}
SKNode *right = [self childNodeWithName:#"rightDirection"];
if (n == right) {
[self.world goRight];
}
}
-(void)update:(NSTimeInterval)currentTime
{
//I don't know how to start yet. = =
}
#end
The GameWorld Class is provided below:
#interface GameWorld : NSObject
#property (nonatomic) SKNode *worldNode;
#property (nonatomic) NSArray *gameObjs;
-(id)init;
-(void) initializeWorld;
-(void) goForward;
-(void) goBackward;
-(void) goLeft;
-(void) goRight;
#end
#implementation GameWorld
-(id)init
{
if (self) {
self = [super init];
GameObject *player = [[GameObject alloc] initWithImageNamed:#"pCar.png" andPosition:CGPointMake(200, 85)];
GameObject *otherCar = [[GameObject alloc]initWithImageNamed:#"AICar.png" andPosition:CGPointMake(200, 100)];
_worldNode = [SKNode node];
// now create the objects, put them in an SKNode
// create the _worldNode with a size equal to the virtual world size
// then add the game objects to that node
_worldNode.scene.size = CGSizeMake(320, 480);
_gameObjs = #[player, otherCar];
}
return self;
}
- (void) initializeWorld
{
// add skSpriteNodes to the worldNode
SKSpriteNode *player = [[SKSpriteNode alloc] initWithImageNamed:#"pCar.png"];
[_worldNode addChild:player];
SKSpriteNode *otherCar = [[SKSpriteNode alloc] initWithImageNamed:#"AICar.png"];
[_worldNode addChild:otherCar];
}
-(void) goForward
{
GameObject *thePlayer = _gameObjs[1];
thePlayer.node.position = CGPointMake(thePlayer.node.position.x, thePlayer.node.position.y + 10);
}
-(void) goBackward
{
GameObject *thePlayer = _gameObjs[1];
thePlayer.node.position = CGPointMake(thePlayer.node.position.x, thePlayer.node.position.y - 10);
}
-(void) goLeft
{
GameObject *thePlayer = _gameObjs[1];
thePlayer.node.position = CGPointMake(thePlayer.node.position.x - 10, thePlayer.node.position.y);
}
-(void) goRight
{
GameObject *thePlayer = _gameObjs[1];
thePlayer.node.position = CGPointMake(thePlayer.node.position.x + 10, thePlayer.node.position.y);
}
#end
There is no need to use NSObject is most cases. Use SKNode and SKSpriteNode instead. By subclassing these classes you can add your custom properties to them.
I suggest you the following structure of the project:
-- GameScene (SKScene)
---- BackgroundLayer (SKNode)
-------- BackgroundNode (SKSpriteNode)
---- UiLayer (SKNode)
-------- your buttons here... (SKSpriteNode)
---- CarLayer (SKNode)
-------- PlayerNode (SKSpriteNode)
-------- OtherCarNode (SKSpriteNode)
In this case you don't have to store objects in manually created arrays, such as gameObjs. For example, all buttons can be accessed from this property: uiLayer.children.
I have created a template, which I use for my Sprite Kit games, take a look on it:
https://github.com/andrew8712/spritekit-game-template

Not accessing function in Objective-C

I'm currently trying to access a function on another file.(another layer, to be more precise).
Both layers are on a scene.
Third layer is trying to get a function from first layer...
Here's how I'm doing this:
Here's my scene in scene.h
#import "firstLayer.h"
#import "secondLayer.h"
#import "thirdLayer.h"
#interface myScene : CCScene
{
// custom instance vars here...
}
#end
Here's how I cast my scene in scene.m
-(id)init {
self = [super init];
if(self != nil){
firstLayer *firstLayerz = [firstLayer node];
[firstLayerz setTag:111];
[self addChild:firstLayerz z:0];
secondLayer *secondLayerz = [secondLayer node];
[secondLayer setTag:112];
[self addChild:secondLayer z:2];
thirdLayer *thirdLayerz = [thirdLayer node];
[thirdLayerz setTag:113];
[self addChild:thirdLayerz z:4];
Here's how I cast the function in thirdLayer.m
#import "scene.h"
#implementation thirdLayer.m
-(id)init {
self = [super init];
if(self != nil){
firstLayer* firstLayerz = (firstLayer*)[self.parent getChildByTag:111];
[firstLayerz functionNeeded];
}
Here's functionNeeded in firstLayer.m (right below init(
-(void)functionNeeded {
NSLog(#"inside fnnction needed");
}
Of course the log ain't showing...
I do the proper cast in firstLayer.h
#interface firstLayer : CCLayer {
}
-(void)functionNeeded;
#end
In your firstLayer's init method write
self.tag = 111;
Now in your ThirdLayer where you want to call method of first layer write :
CCScene *current = [[CCDirector sharedDirector] runningScene];
if (current) {
id layer = [current getChildByTag:111];
if (layer) {
[layer functionNeeded];
}
}

Resources