Inheriting CCSprite in Cocos2d-iPhone - iphone

I've ready many places that tell me I shouldn't inherit the CCSprite class and rather just add a sprite to my class as a member variable.
Here is what I've done:
Created a CCNode class,
Added a CCSprite variable to my node class,
Initialized my sprite and added it as a child to my node.
I now have a CCLayer where I want to draw these sprites but when I add my node as a child nothing is drawn. If I add the nodes sprite as the child rather than the node it will draw.
My question is, is there anyway to get my sprites to draw from adding the node as a child to the layer or is adding the sprites the only way?
I'm new to Objective-c and iPhone dev so any help will be greatly appreciated.
Edit
Here is my CCNode Class - animalSprite is my CCSprite.
#implementation Animal
#synthesize animalSprite = _animalSprite;
-(Animal *) initWithFile:(NSString *)texture position:(CGPoint) position
{
self.animalSprite = [CCSprite spriteWithFile:texture];
self.animalSprite.position = position;
self.animalSprite.scale = 0.25f;
[self addChild:self.animalSprite];
return self;
}
-(void) dealloc{
[super dealloc];
}
#end
Now in my CCLayer class I have a level variable that contains a list of my nodes.
//Load Level
Level *level = [LoadLevel loadLevel];
for (Animal *animal in level.animals)
{
[self addChild:animal];
}
Edit I create my node objects and then add them to the array in the levels class the following way.
Animal *animal = [[[Animal alloc] initWithFile:animalFileName position:position] autorelease];
[level.animals addObject:animal];

Try reading this thread http://www.cocos2d-iphone.org/forum/topic/11566
Hopefully you have the same issue where the sprite is being released before you use it.
Cheers,
Taz
Latest game Chained: http://www.silverbacktechsol.co.uk/stschained.html

Related

Singleton function not retaining value

I'll preface this question by saying that I am a total noob when it comes to Objective-C. So please, be patient with my question. :)
So here is my issue. I am basically allowing the user to 'rub out' an image by using alpha blending and such, and then converting the created texture to a CCSprite. I am then able to store the CCSprite in a function within my singleton class. Like so:
erasedTextureSprite = [CCSprite spriteWithTexture:darknessLayer.sprite.texture];
[[MySingleton sharedMySingleton] setRevealTexture:erasedTextureSprite];
Here is the setRevealTexture & getRevealTexture function as well as the revealTexture variable initialisation in my MySingleton.h file:
#interface MySingleton : NSObject
{
CCSprite *revealTexture;
}
...
-(void) setRevealTexture: (CCSprite *) texture;
-(CCSprite *) getRevealTexture;
And here are both functions in my MySingleton.m file:
-(void) setRevealTexture: (CCSprite *) texture
{
NSLog(#"set reveal texture.");
revealTexture = texture;
NSLog(#"%f", [revealTexture boundingBox].size.width);
}
-(CCSprite *) getRevealTexture
{
NSLog(#"got reveal texture.");
NSLog(#"%f", revealTexture.contentSize.width);
return revealTexture;
}
If I set the reveal texture, and then get it right away, it seems to return the sprite correctly. However, if I set the texture and then transition to another scene, it seems to lose it's value, and throws me an error when I try and call the getRevealTexture function.
Question: Why is my function not retaining it's value when I transition between scenes?
If any more clarification is needed please let me know!
Practically there is no point in using your own setter and getter if your not doing anything fancy.
What you are doing is using an iVar which should have been a strong property.
you could achieve the same by doing:
#property (nonatomic, strong) CCSprite *revealTexture;
in your header file.
You would then get the following 2 functions by default:
[[MySingleton sharedMySingleton] revealTexture];
[[MySingleton sharedMySingleton] setRevealTexture:texture];
My next point of failure that I would presume if this doesn't work is that your singleton function is not working properly and your actually getting a new instance each time.
You can easily find this out by doing in the debugger:
po [MySingleton sharedMySingleton]
And seeing the instance that is returned each time.
example for a basic singleton code:
static MySingleton *__sharedMySingleton = nil;
+ (id)sharedMySingleton {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__sharedMySingleton = [MySingleton new]; // or a specific alloc init function
});
return __sharedMySingleton;
}
Due to the request - here is a short image to explain how to enter debugger commands in xcode 5:

C4: Add panning to an object other than "self"

I watched the C4 tutorial on adding a pan gesture to an object and animating it to return to its original position when the panning is finished. I'm trying to add this to three individual objects. I have it working with one object so far to move it and reset it to a CGPoint, but for it to work, I have to add the pan gesture to "self", not the object. For reference, I'm pretty much using the code from here:
http://www.c4ios.com/tutorials/interactionPanning
If I add the gesture to the object itself, sure, it pans around, but then it just leaves itself at the last touch location. However, I'm assuming that leaving the gesture on "self" will affect more than just the object I want to move, and I want to be able to move the three objects individually.
I'm using roughly the same modification to the "move" method that's used in the example:
-(void)move:(UIPanGestureRecognizer *)recognizer {
[character move:recognizer];
if (recognizer.state == UIGestureRecognizerStateEnded) {
[character setCenter: charStartOrigin];
}
}
And then a new method to spawn the object:
-(void)createCharacters {
character = [C4Shape ellipse:charStart];
[character addGesture:PAN name:#"pan" action:#"move:"];
[self.canvas addShape:character];
}
The example link you are working from is sneaky. Since I knew that there was only going to be one object on the canvas I knew I could make it look like I was panning the label. This won't work for multiple objects, as you have already figured out.
To get different objects to move independently, and recognize when they are done being dragged, you need to subclass the objects and give them their own "abilities".
To do this I:
Subclass C4Shape
Add custom behaviour to the new class
Create subclassed objects on the canvas
The code for each step looks like the following:
subclassing
You have to create a subclass that gives itself some behaviour. Since you're working with shapes I have done it this way as well. I call my subclass Character, its files look like this:
Character.h
#import "C4Shape.h"
#interface Character : C4Shape
#property (readwrite, atomic) CGPoint startOrigin;
#end
I have added a property to the shape so that I can set its start origin (i.e. the point to which it will return).
Character.m
#import "Character.h"
#implementation Character
-(void)setup {
[self addGesture:PAN name:#"pan" action:#"move:"];
}
-(void)move:(UIGestureRecognizer *)sender {
if(sender.state == UIGestureRecognizerStateEnded) {
self.center = self.startOrigin;
} else {
[super move:sender];
}
}
#end
In a subclass of a C4 object, setup gets called in the same way as it does for the canvas... So, this is where I add the gesture for this object. Setup gets run after new or alloc/init are called.
The move: method is where I want to override with custom behaviour. In this method I catch the gesture recognizer, and if it's state is UIGestureRecognizerStateEnded then I want to animate back to the start origin. Otherwise, I want it to move: like it should so I simply call [super move:sender] which runs the default move: method.
That's it for the subclass.
Creating Subclassed Objects
My workspace then looks like the following:
#import "C4WorkSpace.h"
//1
#import "Character.h"
#implementation C4WorkSpace {
//2
Character *charA, *charB, *charC;
}
-(void)setup {
//3
CGRect frame = CGRectMake(0, 0, 100, 100);
//4
frame.origin = CGPointMake(self.canvas.width / 4 - 50, self.canvas.center.y - 50);
charA = [self createCharacter:frame];
frame.origin.x += self.canvas.width / 4.0f;
charB = [self createCharacter:frame];
frame.origin.x += self.canvas.width / 4.0f;
charC = [self createCharacter:frame];
//5
[self.canvas addObjects:#[charA,charB,charC]];
}
-(Character *)createCharacter:(CGRect)frame {
Character *c = [Character new];
[c ellipse:frame];
c.startOrigin = c.center;
c.animationDuration = 0.25f;
return c;
}
#end
I have added a method to my workspace that creates a Character object and adds it to the screen. This method creates a Character object by calling its new method (I have to do it this way because it is a subclass of C4Shape), turns it into an ellipse with the frame I gave it, sets its startOrigin, changes its animationDuration.
What's going on with the rest of the workspace is this (NOTE: the steps are marked in the code above):
I #import the subclass so that I can create objects with it
I create 3 references to Character objects.
I create a frame that I will use to build each of the new objects
For each object, I reposition frameby changing its origin and then use it to create a new object with the createCharacter: method I wrote.
I add all of my new objects to the canvas.
NOTE: Because I created my subclass with a startOrigin property, I am able within that class to always animate back to that point. I am also able to set that point from the canvas whenever I want.

How to point to specific CCSprite without passing it to the method?

This is pretty simple question, but I'm having really hard time with it.
I have made a method which takes int variable. With it, it would need to use it to do action with CCSprite.
For example I call it with this: [_hud hideThisActionLed:2]; and it should then hide CCSprite named actionLed2.
I can't pass the actual CCSprite to the method, because I call it from another class which don't have access to that particular sprite.
I can make the sprite name with this: [NSString stringWithFormat:#"actionLed%d", actionLedNumber], but can't come up with a way to use that to point to that specified CCSprite.
Here's how I declared the sprites in hud class:
actionLed1 = [CCSprite spriteWithFrameName:#"actionLed1.png" setScale:TRUE resetAnchor:TRUE];
[actionLed1 setOpacity:0];
[self addChild: actionLed1 z:11 tag:1];
That x4 for all 4 leds.
This depends on how can access the different leds.
If they are properties inside your class, then you can access them like this:
NSString *actionLedName = [NSString stringWithFormat:#"actionLed%d", actionLedNumber];
CCSprite *actionLed = [self valueForKey:actionLedName];
If they are stored in an array, then you can access them like this:
CCSprite *actionLed = [self.actionLeds objectAtIndex:actionLedNumber];
If you have set up a tag for each actionLed when adding it, then you can access them like this:
CCSprite *actionLed = [self getChildByTag:actionLedNumber];
When you add the CCSprite objects to your layer, use the withTag option. Then you can reference the sprites by the tag number which is the number you pass in to the hideThisActionLed method.
[_hud addChild:ledSprite withTag:1];
[_hud addChild:ledSprite2 withTag:2];
etc...
-(void)hideThisActionLed:(int)ledNum {
CCSprite *theSprite = [_hud getChildByTag:ledNum];
... hide the sprite ...

CCSprite is not displaying on the iPhone

I am trying to make a sprite display on the screen in Cocos2d. But, I don't want to use a CCSprite directly. I have a class Unit which will have some additional properties that I will need later on in my game. The class declaration of Unit is as follows:
#interface Unit : CCSprite {
CCSprite *sprite;
}
-(void)init;
#property(nonatomic, retain) NSNumber *type;
#property(nonatomic, retain) CCSprite *sprite;
#end
And my init method for it looks like this:
-(void)init {
self.sprite = [CCSprite spriteWithFile:#"BasicUnit.png"];
self.sprite.position = ccp(50, 100);
}
Now what I need to do is apply it to the screen. So, I have another class called Playscene which is the scene where I want to display sprites and things. Here is what the init method (the method that should draw the sprites) looks like in Playscene:
-(id) init {
if( (self=[super init] )) {
self.isTouchEnabled = YES;
[army init];
[self addChild:army.sprite];
}
return self;
}
But, when I run this I get a ton of error data including: "terminate called after throwing an instance of 'NSException'" and probably of more importance: "Assertion failure in -[PlayScene addChild:]". I don't know how I can solve this. Any help would be appreciated.
Based on your snippets it is very difficult to know what goes wrong. That said I assume that army in your last snipped is of type Unit. But because that is inside the init() method it could be that it is nil because it is not created here like in army = [[Unit] alloc] initXXX];.
That said I am not sure what you want to accomplish with subclassing CCSprite in your Unit class because you are referencing CCSpirit and so there is not need to subclass it.
Finally your Assertion is probably because your army.spirit is either NIL or it is already added and the assertion inside Coco2d throws the exception (I am assuming that the last snippet is from a subclass of CCNode).
My suggestions:
Don't extend CCSpirit in Unit (not needed as far as I can see)
Don't have a method - (void) init but overwrite - (id) init
Make sure army is properly instantiated using [[Unit alloc] init] (see point above)
Using alloc you need to make sure that if it is assigned to a retaining property that you also release it to offset the alloc.
Hope that helps.

How to create Enemies class in cocos2d

I am using cocos2d for the iphone
I want to create a class for Enemies
see my code
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Enemies : CCSprite {
float imageHeight;
}
#property float imageHeight;
+(id) enemies;
#end
#implementation Enemies
#synthesize imageHeight;
+(id) enemies{
return [[[self alloc] initWithFile:#"enemy.png"] autorelease];
}
#end
I want the to set the imageHeight
float imageHeight = [enemy texture].contentSize.height;
so whenever I create an instance of the class Enemies I it should have imageHeight set properly?
Thanks
note: I am trying to improve this code
ahmed, keep the enemies method the same, and add this to your class:
-(id) init {
if (self = [super init]) {
float textureHeight = [enemy texture].contentSize.height;
[self setImageHeight:textureHeight];
}
return self;
}
This will set imageHeight for all new instances of the Enemies class.
Override initWithFile in Enemies class.
Then Inside it call, [super initWithFile passing the same Filename string]
After which you can set the imageHeight variable in the next line as the texture would have been loaded and height will be accessible.
Cocos2d automatically sets the height of some things based on the image itself. Look for the sprite's setHeight property, it should be easy to find in the docs.
However, your question looks like it could use rewording. Your "Enemy" should be data, not related to any display (sprites) whatsoever, unless you're going to make a really small app.
Check out Beginning XNA books for nice examples of how to abstract superclass enemy and players, if you can use C# and translate between iOS. They will work very much the same. (I wish I knew a good game programming book for iPhone, but all of the ones I open have flaws or bad design that only experienced programmers can get past.
Anywho, if you mix data and images, it usually means to redesign your code