In my cocos2d game, I have a "powerup" of sorts where, once attained, the character becomes blue, the platform becomes blue, and so does the background. I'm not sure if I am implementing this in the best way so if you have other suggestions please do share!:)
I created a class Blue that has an instance variable called blueCalled
I imported this class to both my "Platform" class as well as my "Player" class.
The powerup and player colliding code is inside my Platform class. This is the code for when the powerup is hit:
// try remove powerup
- (void) tryRemovePowerup
{
NSMutableArray * currentPowerUpArray = [self getcurrentPowersArr];
if(currentPowerUpArray)
{
int playerY = ((CCLayer*)(self.player)).position.y;
for(int x=0; x<[currentPowerUpArray count];x++)
{
CCSprite *powerup = [currentPowerUpArray objectAtIndex:x];
if(abs(powerup.position.x+powerup.parent.position.x-[Player initX])<50)
{
if(abs(powerup.position.y+blueberry.parent.position.y-playerY)<30 && powerup.visible && powerup.visible)
{
[power.parent powerup cleanup:YES];
CGSize winSize = [[CCDirector sharedDirector] winSize];
[[SimpleAudioEngine sharedEngine] playEffect:#"yay.wav" pitch:1 pan:0 gain:1];
// THIS IS WHERE ALL OF EFFECTS SHOULD HAPPEN
NSLog(#"powerup hit");
}
}
}
}
[self hitTestOB];
}
The place of // THIS IS WHERE ALL OF EFFECTS SHOULD HAPPEN is where I want to make everything blue. Right now, I plan to do this by setting the instance variable in the Blue class to YES.
In the methods that take care of the 1. frames of the main player, 2. background, and 3. platform frames, I have an if statement checking if the instance variable of the Blue class is YES or NO and it acts accordingly.
However, I don't know how to use the instance variable from the Blue class in my platform and player class, also, I'm not 100% confident this is the best way to implement a powerup.
Thank you for any help you can provide me :)
Go iOS!
As a general rule, if you want to access an iVar of another object, you should make a property to access it. This is not an exception to that rule, but I'll tell you how to do it anyways.
Objective C objects are inherently C structures. This means that the -> operator works on them. In this case, you would want to use blue->blueCalled, where blue is the instance of your Blue class.
Visibility is the key to this working (or failing). You can only use this if the iVar is declared within the #interface in the header file and not following and #private or #protected declaration.
Related
Is it possible to call an instance method without using an instance variable or #property?
Here is how I create an instance of a class. Within the method, I try to call on the class's instance movement method to force the instance to move:
-(void)createCharacterNPC
{
int randomness = [self getRandomNumberBetweenMin:1 andMax:20];
for (int i = 0; i < randomness; i += 1)
{
NSString *npcName = [NSString stringWithFormat:#"anNPC%i", randomness];
NPCclass *NPC = [[NPCclass alloc] initWithName:npcName];
NPC.position = CGPointMake(self.size.width/2, self.size.height/2);
NPC.zPosition = 1.0;
[_worldNode addChild:NPC];
// THIS OBVIOUSLY WORKS. But I can't use this technique outside this method.
[NPC beginMovement];
// THIS IS WHAT I WANT, BUT XCODE DOESN'T ALLOW ME TO WRITE CODE THIS WAY.
[[_worldNode childNodeWithName:#"anNPC1"] beginMovement];
}
}
Is there a way to allow [[_worldNode childNodeWithName:#"anNPC1"] beginMovement]; to work? Or some way similar to this so I wouldn't have to have create an instance variable of NPC (like so: _NPC)?
I'm asking because all of this is happening inside a mini-game scene and NPCclass will be initialized a random number amount of times (with arc4random() method). NPCclass moves on its own using vector (physics in a platformer) movement but I need to initialize its movement method right after creation then I need to periodically access each individually created instance of NPCclass using its name in other methods of the scene. Since I don't know how many NPCclass instances will be created each time the mini-game is played, I CAN'T use IVAR's or something like #property NPCclass *anNPC;
Please help.
Xcode complains about
[[_worldNode childNodeWithName:#"anNPC1"] beginMovement];
because the method -childNodeWithName returns an SKNode object. Instances of the SKNode class do not respond to the selector -beginMovement (or as Xcode puts it, no visible #interface declares the selector -beginMovement). Xcode shows this to you to force you to make sure you wrote what you wanted to write. Since you are sure, you can tell Xcode that the returned object is of the type NPCclass.
(NPCclass *)[_worldNode childNodeWithName:#"anNPC1"]
Now you can expand the statement to call -beginMovement.
[(NPCclass *)[_worldNode childNodeWithName:#"anNPC1"] beginMovement];
Note
There are a few concepts which you might be confusing. NPCclass is a class. +node is a class method of SKNode, which you can call with [NPCclass node];. -beginMovement is an instance method, called with:
NPCclass *npc = [NPCclass node];
[npc beginMovement];
Or:
[(NPCclass *)anyObject beginMovement];
// make sure anyObject responds to this selector though, or you app will crash.
Class methods are prefixed with a +, instance methods with -.
Class methods do not use an instance, just the class name.
As an example consider the
NSString` class method: `+ (id nullable)stringWithContentsOfFile:(NSString * nonnull)path
and a usage:
NSString *fileData = [NSString stringWithContentsOfFile:filePath];
I am having trouble getting objects added to my NSMutableArray to log properly (which definitely means they won't process any of the appropriate functions correctly) with Spritebuilder [version 1.4.9, from the Apple App Store]. I am creating several objects using the same class, but each new one is overriding the older objects which exist. I thought an array would help keep things in order (and then on collision, I could call the array to check for which object was collided with), but it simply is not working that way - at all. Here is the relevant code.
Main.h
#property Coven *coven;
#property Nellie *nellie;
#property NSMutableArray *array;
//Physics, other things
Main.m
/Adding other things...
-(void) addCovenMember{
//This function is called on a RANDOM time interval
_array = [[NSMutableArray] alloc]init];
for (i = 0, i < 15, i++){
_coven = (Coven*) [CCBReader load:#"CovenMember"];
[_array addChild:_coven];
}
[_physicNode addChild:_coven];
}
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair nellie:(Nellie*)nellie coven:(Coven*)coven{
for (_coven in _array){
NSLog(#"%#",_coven.name)
if (CGRectIntersectsRect(_nellie.boundingBox, _coven.boundingBox){
NSLog(#"We're intersecting!");
}
}
Coven. h
//Nothing Important Here
Coven.m
-(void)didLoadFromCCB{
self.physicsBody.CollisionType = #"coven";
}
Nellie.h
//Nothing Here
Nellie.m
-(void) didLoadFromCCB{
self.physicsBody.CollisionType = #"nellie";
}
The collision is logging with every collision - but only as the name of the LATEST _coven member to be generated, no matter what I am colliding with. This also means that the _coven.boundingBox is solely on the latest _coven member and interaction only occurs when I hit the new member as soon as it generates on to the screen.
Any ideas? Any help?
Note: This is also posted on the Spritebuilder website - I decided to post it here as well because answers can be a little slow on those forums.
The -(void) addCovenMember overwrites (creates a new instance) of _array every time it's called. Thus, when you try to iterate in -ccPhysicsCollisionBegin: you'll only ever see 1 coven.
Add a nil check around your array creation:
if(_array == nil) {
_array = [[NSMutableArray] alloc]init];
}
The for loop in the -addCovenMember method looks broken (at least not a c loop). Reaplace the , with ;:
for (i = 0; i < 15 i++){
Also, using for(_coven in _array) seems wrong, you already have a property self.coven (presumably) with a backing _coven ivar. Try changing it to for(Coven * c in self.array) and use the local c in the loop:
for (Coven * c in _array){
NSLog(#"%#",c.name)
if (CGRectIntersectsRect(_nellie.boundingBox, c.boundingBox){
NSLog(#"We're intersecting!");
}
}
To everyone out in the world struggling with their ccPhysicsCollisions, arrays may not be the answer - this was a simple fix that left me incapacitated for days.
Using the basic ccPhysicsCollisionsBegan that ships with spritebuilder, try this without arrays first:
Scene.m
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair nellie:(Nellie*)nellie coven:(Coven*)coven{
[_coven stopAction:coven.path];
}
I initially created the method with:
[_coven stopAction:_coven.path];
Yes, that (underscore) set me back three weeks. Be sure you refer to the object interacting through the physics delegate, and not the object itself, which in my case, was constantly being overwritten by the new ones being generated.
Check your underscores.
Solved! :D
Thanks to #thelaws for your help! I'll get better at Obj C... eventually.
So I'm in the process of making a Objective-C Sprite-Kit based game. I'm fairly new to Objective-C so I seem to be stuck on something involving the update method.
Traditionally in the default Sprite-Kit project it generates for you create a View which contains a scene which contains all the nodes that you want to manipulate. It also comes with a pre-generated, empty update method which since then I have replaced with my own (which works great). But I do not want my update method to based in the MyScene class and this is where my problem lies. If I leave the update method in the MyScene class I can see using NSLog that is in fact continuously being called like it should be, but if I move it say to the ViewControler class it won't be called at all.
My question is how would I go about getting it to call the update method in a class other than MyScene..? You may be asking why I would want to do this so I'll explain below but its not fully necessary to answer the question, however if you do have any tips on my program please let me know!
Traditional Sprite-Kit Project
ViewController --> Creates a MyScene (which is just inheriting from SKScene)
MyScene --> Creates all the nodes, and has the update method to hand game logic/graphics.
My Ideal Structure for my Sprite Game
ViewController --> Creates a ForegroundScene and BackgroundScene(still inherits from SKScene)
ForegroundScene --> creates all the nodes/characters for the game
BackgroundScene --> Creates the Sky, clouds, general landscape stuff for the game.
GameController --> contains the update method which calls displayView and updateGame.
Edit: To clear up any confusion, ForgegroundScene and BackgroundScene are essentialy the same thing as the MyScene class which was originally generated, but now there are two classes re-named, one containing stuff for the background, the other for the foreground.
Update Game is still located in the GameController class and it just does things like collision test, updates position of nodes, deletes characters that were killed.
DisplayView is located in the ViewController and its name is self-explanatory it displays the view of the game which is composed of nodes from the BackgroundScene and the ForegroundScene.
Anyway, this is my idea set up for a game, not sure how pratical it is. I'm very open to suggestions.
Here is my intended update method incase you are interested in seeing it but like I said I test it with NSLog and it ticks accordingly but in any other class its never called.
-(void)update:(CFTimeInterval)currentTime
{
const int TICKS_PER_SECOND = 35;
const int SKIP_TICKS = 1000 / TICKS_PER_SECOND; //these ivars would be moved to top of intended class
const int MAX_FRAMESKIP = 10;
NSDate *referenceDate = [NSDate date];
double timePassed_ms = [referenceDate timeIntervalSinceNow] * -1000.0;
NSUInteger next_game_tick = timePassed_ms;
int loops;
bool game_is_running = true;
while( game_is_running ) {
loops = 0;
while([referenceDate timeIntervalSinceNow] * -1000.0 > next_game_tick && loops < MAX_FRAMESKIP) {
[self updateGame] //calls GameController to do logic updates/collision test, etc.
NSLog(#"update game");
next_game_tick += SKIP_TICKS;
loops++;
}
[self displayGame]; //calls viewcontroler to display game
}
}
You could make the class that you want to notify every time you call -update: an instance variable of your scene, then just forward the message to that class. For example, create an instance variable in your scene of the class you want to notify.
#interface MyScene ()
#property (nonatomic) ClassToNotify *notify;
#end
Then in your -update: method, just notify the instance variable.
- (void)update:(CFTimeInterval)currentTime
{
// pass the message on
[self.notify notifyThatUpdateWasJustCalledWithTime:currentTime];
}
In this case, -notifyThatUpdateWasJustCalledWithTime: would be a method of your ClassToNotify class.
I get the above message in XCode 4.6. I've done a pretty thorough search and but nothing seems to match the exact circumstances surrounding my issue. Admittedly, I'm relatively new to iOS dev, and memory-management has never been my strong suit, but this just has me completely miffed.
I have an instance variable theLink which is defined in the class Game as follows:
#interface Game : NSObject
// Class objects
#property(nonatomic,retain) NSMutableArray *queryItems;
#property(nonatomic,retain) NSMutableArray *theArray;
#property(nonatomic,retain) NSString *theLink;
#property(nonatomic,retain) NSString *thePath;
theLink is set in the makeGame method which is called in the method initialiseGame in my view controller:
- (void) initialiseGame
{
bool gameCreated = FALSE;
while (!gameCreated)
{
gameCreated = [theGame makeGame:#"ptl"];
}
[loadingIndicator stopAnimating];
[loading setText:#"Tap to Start"];
[self performSelector:#selector(setLabels) withObject:nil afterDelay:0.0];
}
(Note: the performSelector afterDelay is used to allow the view to update before continuing. Bit of a hack but I couldn't work out a better way!)
The app then loads the game, and when the user taps the screen to start, the next method which is called from the view controller is:
- (void) setupLink
{
...
for(int i=0; i<[theGame.theLink length]; i++) {
...
}
}
It is on this reference to theGame.theLink where I'm am getting the crash.
What has me most confused is that if I call theGame.theLink from inside the initialiseGame method, it is displays correctly, and also calling any other variable from the Game class (such as thePath or theArray works perfectly, so theGame object has not been deallocated in it's entirety, only the variable theLink.
It seems to me that the variable is being deallocated somewhere as the view controller is being updated. I haven't released the variable, and can't work out why only this variable is being deallocated. As I said at the start, memory-management is not my strength!
Any help/ideas would be hugely appreciated. Let me know if you require any more details.
Thanks heaps,
Andrew
EDIT: Setting of theLink within makeGame
- (bool) makeGame:(NSString*)gameType
{
...
[self getLink];
}
- (void) getLink
{
...
if (... && ((arc4random() % 10) > 8))
{
theLink = #"Animals";
}
}
There are many different ways theLink may be set, depending on random numbers and other factors. This is the most basic form which simply sets it to a static string. It doesn't matter how theLink is set or what it is set to, the program always crashes at the same point.
If theLink is being set to the parameter being passed to it ,#"ptl" or some similar temporary string, it will give you a problem, because it is just a pointer pointing at the current location that is holding #"ptl". After the makeGame method is completed, your system will assume that it is all done with #"ptl" and just free it up.
When you make an #"stringwhatever" in your code, it is supposed to be the equivalent of making an NSObject that is an immutable literal instance of #"stringwhataver". It should, in theory handle all the reference counting in a nice way, but when you are doing your own memory management, there are so many ways to lose count of your references.
There's a pretty simple rule to follow. If you've declared properties, access them via the property. To do otherwise (as you are doing above, with theLink = ...) bypasses all of the memory management built into the property accessors.
self.theLink = ...
Would have solved this problem under MRC. Switching to ARC has "solved" your problem without you understanding the root cause.
I am trying to implement a live/score system using cocos2d and box2d. my bodies are box2d bodies which collide as I want them to but the problem is that I have been trying to implement a score system where on each collision a life is removed or reduced from the lives left and after a certain number of collisions(say 3) the game is supposed to stop. On this occasion it doesn't but from the CCLOG I find out that it actually prints out the message I put in to display when the game manager is called but a weird thing is that it calls it forever(see the debugging window below)also it removes the object, "man" from the scene completely on starting the application.
I have a gameManager(singleton) class where in the .h file I have this declared
#interface GameManager : NSObject {
int lives;
}
-(void)removeLives : (int)val;
and in the gameManager.m file I have this method
-(void)removeLives : (int)val
{
lives -=val;
CCLOG(#"YOU HAVE LOST A LIFE MAN");
}
In my main gameLayer.mm file in the update method I have this code
if(kStateColliding){
if (lives > 0) {
man.visible = TRUE;
} else if (lives <= 0) {
[man stopAllActions];
man.visible = FALSE;
[[GameManager sharedGameManager] removeLives:1];
}
}
and the lives is initialised in gameManager init method thus
-(id)init { // 8
self = [super init];
if (self != nil) {
// Game Manager initialized
CCLOG(#"Game Manager Singleton, init");
lives = 3;
}
This is a screen shot of the debug console
Also note that I have a "Man" class which is Box2d class.
Am I doing the correct thing? Please can anyone help me out with how to go about implementing this system and where and how to make the appropriate calls.
Your call to [[GameManager sharedGameManager] removeLives:1] is within the else if (lives <= 0) block. It should only be called when lives > 0.
You will want to do something after the man has no lives left to prevent the if (kStateColliding) block from being called. Also, you will probably want to reset the mans position away from the object it is colliding with to prevent a single "collision" triggering multiple calls to removeLives.
Where are you declaring lives in gameLayer.mm? Make sure it is being set to the value stored in the singleton because with you current code, I dont see how it could possibly by set to 0 or below unless it is being initialized to 0 or you are changing its value elsewhere.
I would ask cocos2d questions like this in the cocos2d forums: http://www.cocos2d-iphone.org/forum/
There you will get answers more quickly, often within minutes.