Accessing variables from another class when importing a .ccbi file - ios

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.

Related

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.

iOS. SpriteKit: How to use the same emitter in all game scenes

I'm using SpriteKit and Xcode 6 to develop a game. I have a single sks file that I want to use in all game scenes. If I add the emitter into each scene after I present it, the emitter stops in the old scene and stars new in the new scene, because it is created in each scene.
Therefore I tried to use a singleton class for creating the emitter only once and using it in all game scenes. I created a super class for all game scenes and create the emitter in that super class for all child scenes. But, what I tried to get with this steps is not working. the emitter still stops and starts new in every new scene, because it still created new in each scene.
How can I use the same emitter bei creating it only once and use it in all my game scenes whithout stoping and new starting the emitter?
Here my code. What I'm missing?
P.S. The same technique is working well with an audio player! http://www.galloway.me.uk/tutorials/singleton-classes/
//MyEmitterNode.h
#import <SpriteKit/SpriteKit.h>
#interface MyEmitterNode : SKEmitterNode
+(instancetype)sharedEmitterNode;
- (void)addEmitterNodeIntoScene:(SKScene *)scene;
#end
//--
//MyEmitterNode.m
#import "MyEmitterNode.h"
#interface MyEmitterNode ()
#property (strong, nonatomic) SKEmitterNode *mEmitterNode;
#end
#implementation MyEmitterNode
+ (instancetype)sharedEmitterNode {
static MyEmitterNode *sharedEmitterNode = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedEmitterNode = [[self alloc] init];
});
return sharedEmitterNode;
}
- (void)addEmitterNodeIntoScene:(SKScene *)scene {
self.mEmitterNode = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"MyParticle" ofType:#"sks"]];
[scene addChild:self.mEmitterNode];
}
#end
// -- MyScene: super classe for all game scenes
//MyScene.m
- (void)didMoveToView:(SKView *)view {
[[MyEmitterNode sharedEmitterNode] addEmitterNodeIntoScene:self];
}
// -- MyFirstScene: child class from MyScene
//MyFirstScene.m
- (void)didMoveToView:(SKView *)view {
[super didMoveToView:view];
// do other stuff
}
// -- MySecondScene: child class from MyScene
//MySecondScene.m
- (void)didMoveToView:(SKView *)view {
[super didMoveToView:view];
// do other stuff
}

No visible #interface error using enumerateChildNodesWithName?

I have ran this project in the past without errors and all the sudden, red flags go up when I try to use:
-(void)aimRocketLauncher:(CGPoint)location
{
__block CGPoint handleLocation;
[self enumerateChildNodesWithName:#"handle" usingBlock: ^(SKNode *node, BOOL *stop) {
SKSpriteNode *handle = (SKSpriteNode *) node;
handle.name = #"handle";
if(CGRectContainsPoint(handle.frame, location)) {
handle.position = location;
handleLocation = location;
}
}];
[self enumerateChildNodesWithName:#"rocketLauncher" usingBlock: ^(SKNode *node, BOOL *stop) {
SKSpriteNode *rocketLauncher = (SKSpriteNode *) node;
CGPoint launcherPosition = rocketLauncher.position;
CGFloat slope = (handleLocation.y - launcherPosition.y) / (handleLocation.x - launcherPosition.x);
//NSLog(#"%f", slope);
CGFloat angle = tan(slope);
angle *= angle;
SKAction *rotate = [SKAction rotateToAngle:degToRad(angle + 90) duration:0.05];
// if (angle < 270 && angle < 180) {
// [rocketLauncher removeAllActions];
// [rocketLauncher runAction:rotate];
//
// }
}];
}
Not just this one, but all over the project. I tried undoing and cleaning the project.
The exact error:
ARC Semantic Issue No visible #interface for 'GameScene' declares the selector 'enumerateChildNodesWithName:usingBlock:'
It really doesn't matter what the rest of the code is because I know this works. When I start to type [self the autocomplete finds it and when I right click on enumerateChildNodesWithName then Jump to Definition, it's there. This means that the Sprite Kit is imported correctly. I tried closing out the project and restarting the Mac, still nothing happened.
I tried using this code in another class to see what would happen. Same error.
The only difference I see in the 2 different projects that have this similar code is that one was started in Xcode 5 and this one Xcode 6.
I also do not want to use any sks file whatsoever.
.h
#import <SpriteKit/SpriteKit.h>
#import "Settings.h"
#import "TopHud.h"
#import "Hud.h"
#import "Menu.h"
#import <AVFoundation/AVFoundation.h>
#import <Foundation/Foundation.h>
#class Menu;
#class Settings;
#class TopHud;
#class Hud;
#interface GameScene : SKScene
{
}
#end
Top of the .m file
#import "GameScene.h"
#implementation GameScene
I really need to work out this problem because it's killing my entire project that is fairly large. I'm working on it alone.
I changed the Deployment Target from 8 to 7.1

Obj-C Inheriting from SKSpriteNode

I have a class defined as
#interface Board : SKSpriteNode
+ (Board *) initWithScreenSize:(CGRect)screen;
#end
#implementation Board
+ (Board *) initWithScreenSize:(CGRect)screen
{
Board *board = (Board *) [SKSpriteNode spriteNodeWithImageNamed:#"turn4_board"];
return (Board *)board;
}
#end
There are other methods/properties but this is the relevant portion. My problem is that after initializing board (via the SKSpriteNode call), the variable is of type SKSpriteNode. I can't make it of type "Board", thus later run-time calls to an instance of "Board" cause a crash saying that my method doesn't exist in the class SKSpriteNode. I am apparently making a rookie mistake, but I'm starting to go bald over this one. Please provide the requisite smack upside my head, and then please follow up with a helpful fix/suggestion. Thanks!
I should, perhaps, mention that I'm using Xcode 5.1.1 developing for iOS 7 under MacOS 10.9.4.
Because you created a SKSpriteNode not Board. Cast fool the compiler but it won't make it working.
#interface Board : SKSpriteNode
+ (instancetype) boardWithScreenSize:(CGRect)screen;
#end
#implementation Board
+ (instancetype) boardWithScreenSize:(CGRect)screen
{
return [self spriteNodeWithImageNamed:#"turn4_board"];
}
#end
You need to create a proper init method and conventionally a convenience constructor will start with boardWith...:
#interface Board : SKSpriteNode
+ (Board *)boardWithScreenSize:(CGRect)screen;
- (instancetype)initWithScreenSize:(CGRect)screen;
#end
#implementation Board
+ (Board *)boardWithScreenSize:(CGRect)screen
{
return [[Board alloc] initWithScreenSize:screen];
}
- (instancetype)initWithScreenSize:(CGRect)screen
{
self = [super initWithImageNamed:#"turn4_board"];
if (self) {
// Additional init
}
return self;
}
#end

Why is contactDelegate not being set?

I'm getting the following warning from the line
self.physicsWorld.contactDelegate = self;
Assigning to 'id' from incompatible type 'PlayLevel *__strong'
-(id)initWithSize:(CGSize)size level:(int)level{
if (self = [super initWithSize:size]) {
_level = level;
self.physicsWorld.gravity = CGVectorMake(0,0);
self.physicsWorld.contactDelegate = self;
Not at all sure what's causing it.
This is what my header looks like:
#import <SpriteKit/SpriteKit.h>
#interface PlayLevel : SKScene
-(id)initWithSize:(CGSize)size level:(int)level;
#end
Any idea's what's causing it? I kind of need a contactDelegate. Thanks!
You should declarer on your class interface (inside <>) that your class implement the delegate.
you will have to add the delegate reference in your header file. For example if we are using the UINavigationController delegates we add <UINavigationControllerDelegate> in the header file. Similary you will have to add the contact delegate
In your header where you declare that PlayLevel extends SKScene, you need to also declare that it implements the SKContactDelegate interface like so:
#interface PlayLevel : SKScene <SKPhysicsContactDelegate>

Resources