I've implemented 3 classes.
-Scene.m & .h
Scene.m: (has HudLayer & BackgroundLayer properties on the header file)
-(id)init{
self = [super init];
if(self != nil){
//Level1Layer
_level1GameplayLayer = [Level1Layer node];
[self addChild:_level1GameplayLayer z:0];
//Hud Layer
_hudLayer = [HUDLayer node];
[self addChild:_hudLayer z:1];
}
return self;
}
-Which holds these 2 layers
BackgroundLayer.m & .h && HUDLayer.m & .h
Everytime I click the screen I get a Log notice like this "Touched Screen"(called on backgroundlayer.m) which is followed by a function which is implemented on HUDLayer.m & .h
I call it like this:
[_hud getAmmo:self.ammoLeft magsLeft:self.magsLeft];
_hud is stated on the Scene.m like this:(and its imported on backgroundlayer.m)
HUDLayer *hudLayer = [HUDLayer node];
[self addChild:hudLayer z:1];
background is z:0.
Also BackgroundLayer.m has the property under #interface:
(I realized this is nil because its not initialized, How do I initialize this???)
#property (strong) HUDLayer *hud;
Between the call of getAmmo: I make 3 CLOGS, one BEFORE "We're about to getAmmo:"
one INSIDE the function on HUDLayer.m that calls "Inside getAmmo:"
and one AFTER "We gotTheAmmo:"
BackgroundLayer.m:
CCLOG(#"We're about to getAmmo");
_hud getAmmo:self.ammoLeft magsLeft:self.magsLeft];
CCLOG(#"We got the ammo:%d, mags:%d",self.ammoLeft,self.magsLeft);
HUDLayer.m:
-(void)getAmmo:(int)ammo magsLeft:(int)magsLeft
{
CCLOG(#"We did this");
hudMagsLeft=magsLeft;
hudAmmoLeft = ammo;
CCLOG(#"HUD MAGS: %d, AMMO:%d", hudMagsLeft,hudAmmoLeft);
}
Im only getting the one before and the one after, there's no warnings on the way the function is being called but for some reason it isn't being called. There's no if statements or anything..what is it Im doing wrong???
So to make the question more simple, how do I access properties/functions from other classes?
Thank you for your time, have a good one.
On the scene.h I added this function:
-(id)initWithHUD:(HUDLayer *)hud;
Then on the scene.m I made this:
_backgroundLayer = [[[BackgroundLayer alloc] initWithHUD:_hudLayer] autorelease];
on Backgroundlayer.m I changed regular init with
-(id)initWithHUD:(HUDLayer *)hud{
:D
Related
I want to test to make sure there is actually a finger on the screen every few seconds. Currently, I have things happening when ccTouchesBegan, though, what happens is you can turn off your device and turn it back on and it still thinks a finger is on the screen, or at least the functions are still going. Is there a way to prevent this? Like creating an #selctor to test this?
This is what I would do....
In my implementation, I would create a bool called 'isItStillBeingTouched'
HelloWorldLayer.h
#interface HelloWorldLayer
#property bool isItStillBeingTouched; // Should be fine anywhere between interface & end
#end
Although you haven't said, I'm guessing that you have loops that are performing the task(s) you want to achieve, hence why they are not stopping of their own accord. What you may try is to wrap it all in a Do While loop:
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
isItStillBeingTouched = YES;
while (isItStillBeingTouched) {
// Your current code....
}
}
You'll need to access the isItStillBeingTouched bool of your HelloWorldLayer class, from the AppDelegate, so alter said class to include this method within the helloWorldLayer.m
+ (id)sharedManager {
static HelloWorldLayer *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
Then in the HelloWorldLayer.h, insert +(id)sharedManager; anywhere tidy between your #interface HelloWorldLayer and your #end statements. Not inside any { or }.
The next step is to change the bool isItStillBeingTouched to NO when the application goes into the background.
Goto your AppDelegate.m, and find the:
-(void) applicationDidEnterBackground:(UIApplication*)application
Alter this to include setting your isItStillBeingTouched bool to NO (within the if statement), and your methods should stop running.
You do this by first calling an instance of the HelloWorldLayer, and then changing the property to NO. For that you'll need to add #import "HellowWorldLayer.h"
Assuming you've never previously altered the AppDelegate, it should look something like this:
#import "AppDelegate.h"
#import "IntroLayer.h"
#import "HellowWorldLayer.h"
//...The stock appDelegate code...
-(void) applicationDidEnterBackground:(UIApplication*)application
{
if( [navController_ visibleViewController] == director_ )
{
[director_ stopAnimation];
HelloWorldLayer *myLocalInstanceOfTheHelloWorldLayerClass;
myLocalInstanceOfTheHelloWorldLayerClass.levelToLoadUp = NO;
}
}
Here's my class with my custom init method:
// Piece.h
#import <Foundation/Foundation.h>
#interface Piece : CCSprite
#property (nonatomic) int pieceNumber;
+(Piece *)initWithPieceImage:(UIImage*)piece pieceName:(int)pName;
#end
// Piece.m
#import "Piece.h"
#implementation Piece
#synthesize pieceNumber = _pieceNumber;
+(id)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
return [[[self alloc] initWithPieceImage:piece pieceName:pName] autorelease];
}
-(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
CCSprite *bgImage = nil;
if ( (self=[super init]) )
{
bgImage = [CCSprite spriteWithCGImage:piece.CGImage
key: [NSString stringWithFormat:#"%i",pName]];
}
return (Piece*)bgImage;
}
#end
I instantiated the Piece class like this to add it to the layer:
Piece *newPiece = [Piece initWithPieceImage:myUIImage pieceName:1];
[newPiece setPieceNumber:2]; //Error in this line
[self addChild: newPiece z:1];
However I have tried it like this and it perfectly works:
Piece *newPiece = [[Piece alloc] init];
[newPiece setPieceNumber:2];
but this is not what I want.
and here is the error I get:
[CCSprite setPieceNumber:]: unrecognized selector sent to instance 0x85f1050
Terminating app due to uncaught exception NSInvalidArgumentException, reason: -[CCSprite setPieceNumber:]: unrecognized selector sent to instance 0x85f1050
Aparently it looks like the problem is how Im trying to init my object.
I'm a newcomer to objective-c so I cant figure out what is wrong here.
any idea of what am I doing wrong here?
How can I achieve this approach and access the properties of my instantiated object with custom init method?
You have a mess in your code. In -(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName you return a CCSprite object instead of a Piece. You assign self with an object but return another, of an incorrect type.
init returns the correct type (because you haven't reimplemented it), so it works, but you haven't actually initialized the image correctly.
You need to change your method like so:
-(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
return [super initWithCGImage:piece.CGImage key:[NSString stringWithFormat:#"%i",pName]];
}
It is because in your init method, you are for some reason creating a CCSprite object and returning that instead of the Piece object. Because of that, it will not be an object of your Piece class and will not respond to any of Piece's methods.
Instead of creating a new CCSprite object to set those properties to, you want to set those on self or super.
I have a "bomb" CCSprite in its own class(If people who doesn't use cocos2d reads this, CCSprite is a NSObject pretty much).
The CCSprite file looks like this:
Bomb.h:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import <OpenAL/al.h>
#class HelloWorldLayer;
#interface Bomb : CCSprite {
#private
int length;
}
#property (readwrite) int length;
#end
Bomb.m:
#import "Bomb.h"
#implementation Bomb
#synthesize length = _length;
#end
I add it in my game layer (HelloWorldLayerlike a pro) with #class Bomb; in .h, and I import the Bomb.h in my HWLayer.m aswell and where I use it in my code, is here:
Bomb *bombe = [[Bomb alloc] init];
bombe.position = explosionPoint;
bombe.length = player.explosionLength; //player is another CCSprite class. This one is from the method. ....fromPlayer:(PlayerSprite *)player
//Logging here works, tested and the bombe.position is valid and .length is valid
[currentBombs addObject:bombe];
NSLog(#"%#",currentBombs); //Here doesn't, guessing crash is at ^
As said, it crashes on the addObject:line. I can't really see why, as I just replaced a not-classed CCSprite with a Bombclass.
The crash is just a (lldb) and the thing on the left outputs a few thousand of these:
Which says description, so I would assume its in my CCSprite subclass the error is. BUT the bombe.* logging worked fine!
Does anyone understand why it doesn't work?
Edit:
EDIT:
NSLog(#"%#",currentBombs); //Here doesn't, guessing crash is at ^
Your %# implies NSString. currentBombs is likely an int. Try
NSLog(#"%i",currentBombs); //Here doesn't, guessing crash is at ^
CCSprite requires a texture. You can (maybe?) have a CCSprite without one, but that's not what CCSprite is for.
You will want to use CCNode for this purpose:
CCNode* node = [CCNode new];
This is a full-fledged Cocos2d object that you can move, etc. You'd add your Bomb to it, and move the CCNode around, like this:
Bomb *myBomb = [Bomb new]; //or whatever
CCNode* bombNode = [CCNode new];
//add the bomb to the node
[bombNode addChild:myBomb];
//move the node
bombNode.position = CGPointMake(10, 20)
This allows you to remove the myBomb from your node, effectively having something that you can add whatever you want without needing to display anything, but when you want to, it can be done easily.
Good luck
try this method :
Bomb *bombe = [Bomb spriteWithFile:#"yourFile.png"];
bombe.position = explosionPoint;
bombe.length = player.explosionLength;
[currentBombs addObject:bombe];
NSLog(#"%#",currentBombs);
Most of the tutorials that I've read only explain Cocos2D examples in the HelloWorld class, but as I've started to build a simple game I need to know how to send an event to different classes, and for them to respond whenever it happens.
I have GameSceneLayer, a CCLayer class which loads in my different CCLayers of Sprites:
#implementation GameSceneLayer
+ (CCScene *)scene {
CCScene *scene = [CCScene node]; // Create a container scene instance
GameSceneLayer *gameLayer = [GameSceneLayer node]; // Create an instance of the current layer class
[scene addChild:gameLayer]; // Add new layer to container scene
return scene; // Return ready-made scene and layer in one
}
-(id)init
{
self = [super init];
if (self != nil)
{
Background *background = [Background node];
[self addChild:background z:0];
Player *player = [player node];
[self addChild:player z:1];
MainMenu *mainMenu = [MainMenu node];
[self addChild:mainMenu z:2];
}
return self;
}
#end
However, when my MainMenu CCLayer START sprite is touched I would like it to spawn in the PLAYER sprite from the Player CCLayer.
I'm guessing that I need a GlobalVariables.h with something like:
#define gameStart #"0"
So when the START sprite is pressed it changes gameStart to 1, and somewhere in the PLAYER sprite there is
if (gameStart == 1)
{
[self addChild:PLAYER];
}
However I'm not sure how to set up the code so that the PLAYER sprite is always looking for that information.
You have objects (instances of classes). You want one object to communicate with another. In Objective-C, that's called sending messages. In other languages it's simply calling a method.
You don't need global variables. Instead, the receiving object MainMenu needs to send a message to (call a method on) the Player object. How do you get two objects to know each other? You could put them in a stinky, loud, overcrowded discotheque and hope for the best.
Or you could simply let them talk to each other, but alas, they shouldn't. Since both are siblings of GameSceneLayer, they shouldn't hold references to each other themselves (danger of creating retain cycles unless you're using weak references).
But both have the same parent. So what does a good parent do when two siblings won't talk to each other? It relays the message!
In MainMenu, send a message to the parent GameSceneLayer:
[(GameSceneLayer*)self.parent gameWillStart];
The GameSceneLayer implements that selector, and forwards the message to any other object that should be informed about starting the game:
-(void) gameWillStart
{
[player gameWillStart];
[ingameUI gameWillStart];
// etc.
}
And Player also implements said selector:
-(void) gameWillStart
{
[self addChild:PLAYER];
}
Just one other thing: in GameSceneLayer, make player and all other objects ivars (instance variables) so that GameSceneLayer has these references readily available. The alternative would be to tag (less often used) objects and then use getChildByTag:
PS: adding PLAYER as child looks dubious to me. If you have already created whatever node PLAYER is, you should add it right away and if necessary, set it to be invisible and/or paused while the game hasn't started yet.
You can use a singleton GameState Manager, which keeps information on the game state.
Here is a little snippet:
+(GameManager*) sharedGameManager {
if (!_sharedGameManager) {
_sharedGameManager = [[self alloc] init];
}
return _sharedGameManager;
}
+(id) alloc {
NSAssert(_sharedGameManager == nil, #"Cannot create second instance of singleton Game Manager");
return [super alloc];
}
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
in that game manager you can have an enum with the game states, and set a property for it.
//Header
typedef enum {
kGameStarted,
kGameEnded
}GameState
#interface GameManager : CCNode {
}
#property (nonatomic) GameSate gameState;
Then back in your implementation file, you synthesize that GameState property, and create your own setter
//Implementation
#synthesize gameState = _gameState;
//Create your own setter, so you can notify all listeners
-(void) setGameState:(GameState) newGameState {
if (newGameState != _gameState) {
_gameState = newGameState;
//Notify listeners via NSNotification
[[NSNotificationCenter defaultCenter] postNotificationName:#"gameState" object:nil];
}
}
in the classes where you want to get the messages you just have to subscribe to the "gameState" notification like so:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(onGameStateChange:)
name:#"gameState"
object:nil];
-(void) onGameStateChange{
if ([GameManager sharedManager].gameState == kGameStarted){
//start game
}
}
In my game, which is using cocos2d, there is going to be many different types of enemies, which all look different, and move all in different ways. Also, there is going to be a couple of different gamemodes, which both use the same enemies. As there will be different gamemodes, I decided to make each of my enemies have their own CCSprite class. In those there will be the way that the sprites move, the animation, etc. When one of these sprite is needed in my game, they will be spawned in to the scene. The only thing is, how do I do this? How do I call for one of the sprites to be create on the screen when they are using a class of their own?
If you want to tell me another way than having these sprites having their own classes, that is fine, but keep in mind that I will be having a couple of different gamemodes. If I do the code for the sprites in the CCLayer class of that gamemode, well I will have to write the code twice, which will take time.
Thanks.
You can just subclass CCSprite and override the default initializer initWithTexture:rect:
example taken from here
#implementation MySprite
-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
{
if( (self=[super initWithTexture:texture rect:rect]))
{
// initialize your ivars here
//ivar1 = xxx;
//ivar2 = yyy;
//ivar3 = zzz;
}
return self;
}
#end
// And to create an instance of MySprite you simply do:
MySprite *sprite = [MySprite spriteWithFile...];
// or any of the supported CCSprite methods.
you can have a super class say EnemySprite that looks like this
#interface EnemySprite : CCSprite
- (void)addToLayer:(CCLayer *)layer;
- (void)removeFromLayer:(CCLayer *)layer;
#end
than create a subclass for each type of enemy for example:
#inteface BigEnemySprite : EnemySprite
#end
#implementation BigEnemySprite
- (void)addToLayer:(CCLayer *)layer {
[layer addChild:self];
// animation code for your big enemy
}
- (void)removeFromLayer:(CCLayer *)layer {
[layer removeChild:self];
// animation code
}
#end
than you can use them like
EnemySprite *enemy = [BigEnemySprite spriteFromFile:file];
[enemy addToLayer:self];