Why the Cocos2D programming guide suggests to use initWithTexture? - iphone

I found that in the cocos2d Best Practices there is a suggestion to use initWithTexture to initialize sprite subclasses, instead in several books and tutorials I found that you can also use initWithSpriteFrameName. Why is that?

All of the initialization methods in CCSprite use initWithTexture:rect to create the sprite. For example,
-(id) initWithFile:(NSString*)filename
{
NSAssert(filename!=nil, #"Invalid filename for sprite");
CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename];
if( texture ) {
CGRect rect = CGRectZero;
rect.size = texture.contentSize;
return [self initWithTexture:texture rect:rect];
}
[self release];
return nil;
}
Other functions (initWithFile, initWithSpriteFrame, initWithSpriteFrameName, et cetera) also call initWithTexture:rect either directly or indirectly. If your CCSprite subclass has any special initialization that needs to take place (as it ostensibly will, since you're subclassing another class), doing it in initWithTexture:rect guarantees that it will be run.

Related

CCSprite doesn't go invisible -iphone

in .h file
CCSprite *backwheels;
in .m file
backwheels = [CCSprite spriteWithFile:#"wheels_back.png"];
backwheels.position = ccp(400,120);
[self addChild:backwheels];
-(void) showGameOver {
backwheels.visible = false;
}
but when Game is Over backWheels still appears on scene..? !
any help ?!
NOTE:i have synthesized backWheels too,but still doesn't work for me.
I can give you a quick fix. This is not the best approach, the best one is to find out what exactly causes such a behavior, but i can't do that without seeing the rest of your code. Anyway this is how you can access backwheels sprite in showGameOver method. When you create the sprite make it this way:
backwheels = [CCSprite spriteWithFile:#"wheels_back.png"];
backwheels.position = ccp(400,120);
backwheels.tag = 100; // whatever integer value you wish
[self addChild:backwheels];
Then you retrieve it in showGameOver :
backwheels = [self getChildByTag:100];
backwheels.visible = false;
I believe it's gonna work.
You could always just change the opacity of the sprite i.e.
-(void) showGameOver {
backwheels.opacity = 0.0f;
}
And then when you want it to reappear change it to
backwheels.opacity = 1.0f;
Use Remove Child :
[self removeChild:backwheels cleanup:YES];

Cocos2d NSMutable Array mutated while enumerated

I've been working on a game, and at a point I didn't have issues removing the enemy game objects (right now they are subclassed from CCSprite and I know that's not the best way)
But I'm not sure what I changed to make it crash when the program attempts to removeChild from _targets after they have been added to targetsToDelete.
I tried moving things around, I just don't know how I am adding or editing the array while its being created... Any help or advice would be great!
And actually if you had any pointers on how best to create game enemies, do you subclass NSObject or CCNode? I heard to divide them into component classes but I had no clue what they meant.
//Projectile Target collision
-(void)update:(ccTime)dt {
for (spygot *target in _targets) {
CGRect targetRect = CGRectMake(
target.position.x - (target.contentSize.width/2),
target.position.y - (target.contentSize.height/2),
target.contentSize.width,
target.contentSize.height);
//Collision Detection Player
CGRect playerRect2 = CGRectMake(
_controlledSprite.position.x - (_controlledSprite.contentSize.width/2),
_controlledSprite.position.y - (_controlledSprite.contentSize.height/2),
_controlledSprite.contentSize.width,
_controlledSprite.contentSize.height);
NSMutableArray *projectilesToDelete = [[NSMutableArray alloc] init];
for (Projectile *projectile in _projectiles)
{
NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init];
CGRect projectileRect = CGRectMake(
projectile.position.x - (projectile.contentSize.width/2),
projectile.position.y - (projectile.contentSize.height/2),
projectile.contentSize.width,
projectile.contentSize.height);
BOOL monsterHit = FALSE;
if (CGRectIntersectsRect(projectileRect, targetRect))
{
NSLog(#"hit");
target.mhp = target.mhp - 1;
monsterHit = TRUE;
if (target.mhp <= 0)
{
[targetsToDelete addObject:target];
}
}
for (spygot *target in targetsToDelete)
{
[self removeChild:target cleanup:YES];
[_targets removeObject:target];
}
if (monsterHit)
{
[projectilesToDelete addObject:projectile];
}
[targetsToDelete release];
}
for (Projectile *projectile in projectilesToDelete)
{
[_projectiles removeObject:projectile];
[self removeChild:projectile cleanup:YES];
}
[projectilesToDelete release];
}
It looks like the code that you've pasted is all from within a for loop iterating over _targets. How does the variable target get initialized?
Usually when I get this sort of error it's because I have the code in a block or am in some other way on a nebulous thread. How sure are you that this bit of code is not running more than once at the same time?
You could try wrapping it in the following:
dispatch_async(dispatch_get_main_queue(), ^{
// do everything here.
});
As for advice about using CCSprite for your game enemy objects, my advice is fix it when it becomes a problem. Are you seeing issues with it right now? Premature optimization is almost as bad as doing it wrong in the first place. You'll know better at the end of the project how you should have done it earlier. ;)
I guess you know that you cannot remove elements from the array while you iterate over it. That is why you have targetsToDelete array.
But it looks to me that you do remove targets to soon.
Try this:
finish iterating the main loop and finish collecting targets to the targetsToDelete array and only after main loop is done remove the targets.

accessing instance variable in cocos2d scheduled method crashes

fresh to objC and cocos2d :)
i'm following "learn cocos2d game development with iOS5", in chapter4, there is a "DoodleDrop" game.
define some variable in GameScene.h like this
#interface GameScene : CCLayer
{
CCSprite *player;
CGPoint playerVelocity;
CCArray *spiders;
CGSize screenSize;
int dropedSpidersCount;
float duration;
}
+ (CCScene *)scene;
#end
in GameScene.m the init method looks like this
- (id)init
{
if (self = [super init]) {
duration = 4.0;
[self createPlayer];
[self createSpiders]; // spiders were inited here.
[self resetSpiders];
[self schedule:#selector(chooseSpider:) interval:0.7];
}
return self;
}
while in chooseSpider, i cannot access spiders, xcode broke
in other methods, spiders or duration just behave normally, why does this happens?
gist code added
https://gist.github.com/2940466
After inspecting your code, I suggest you to try this fix:
- (void)createSpiders
{
CCSprite *tempSpider = [CCSprite spriteWithFile:#"spider.png"];
CGSize spiderSize = [tempSpider texture].contentSize;
int spiderCount = screenSize.width / spiderSize.width;
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
for (int i = 0; i < spiderCount; i++) {
CCSprite *spider = [CCSprite spriteWithFile:#"spider.png"];
[self addChild:spider];
[spiders addObject:spider];
}
}
where the only difference is in the line:
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
Indeed, if you do not retain you spiders object, it will be autoreleased at the next run loop iteration.
OLD ANSWER:
Without seeing more code it is not possible to say exactly what is happening, but it seems that in the interval between creating the spiders and the actual execution of chooseSpiders, your spiders array gets deallocated.
As a quick try, I would suggest adding:
[spiders retain];
before calling
[self schedule:#selector(chooseSpider:) interval:0.7];
and see wether the crash keeps happening.
if you provide more code, it could be possible to help you further.

Can I pass a function name as an argument ?

I want to make this class dynamically perform functions on itself by passing different names to it. Is that possible ? Or rather: How is it possible ?
-(id)initWithMethod:(NSString*)method{
if ((self = [super init])){
[self method];
}
return self;
}
-(void) lowHealth {
CCSprite *blackScreen = [CCSprite spriteWithFile:#"blackscreen.png"];
blackScreen.anchorPoint = ccp(0,0);
[self addChild:blackScreen];
id fadeIn = [CCFadeIn actionWithDuration:1];
id fadeOut = [CCFadeOut actionWithDuration:1];
id fadeInAndOut = [CCRepeatForever actionWithAction:[CCSequence actions:fadeIn, fadeOut, nil]];
[blackScreen runAction:fadeInAndOut];
}
You should use performSelector and get the selector from your NSString using NSSelectorFromString:
[self performSelector:NSSelectorFromString(method)];
instead of [self method];
The standard way is using Selectors as mentioned in Matteo's answer.
You can also look at Objective-C Blocks. They are becoming very common in the CocoaTouch APIs and you can do some very slick things with them. The resulting architecture of your class is often easier to understand IMO.
For example this method from UIView
+ (void)animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion
Takes two block, one that is runs the code for the actual animation, and one to for the code after the animation is complete. You could call this with block variables or by writing code inline:
...animations:^{
// animation code
}
completion:^(BOOL finished) {
// completion code
}
The receiving method (in this case animateWithDuration:...) would simply call these blocks at some point like so:
animations();

scaling a CCMenuItem in Cocos2d (Objective-C)

I'm trying to make a CCMenuItem that has scaled images. For example, I tried:
CCSprite* normalSprite = [CCSprite spriteWithFile:#"button_play.png"];
CCSprite* selectedSprite = [CCSprite spriteWithFile:#"button_play.png"];
selectedSprite.scale = 1.2;
CCMenuItem menuItem = [CCMenuItemSprite
itemFromNormalSprite:normalSprite
selectedSprite:selectedSprite
target:self
selector:#selector(onPlay:)];
But it looks like CCMenuItemSprite ignores the scale of the underlying sprites. Is there a way to do this (aside from just creating scaled versions of the underlying images)? Thanks.
Thyrgle is correct about how CCMenuItem works.
However, there certainly is a way to do what you want. All you need to do is subclass CCMenuItem and override the selected and unselected methods to achieve what you want. In fact, I'm pretty sure you could just cut and paste the code from CCMenuItemLabel, because scaling the item to 1.2 is exactly what it does. (In fact, it does it better, since it animates the scale change.)
-(void) selected
{
// subclass to change the default action
if(isEnabled_) {
[super selected];
[self stopActionByTag:kZoomActionTag];
CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:1.2f];
zoomAction.tag = kZoomActionTag;
[self runAction:zoomAction];
}
}
-(void) unselected
{
// subclass to change the default action
if(isEnabled_) {
[super unselected];
[self stopActionByTag:kZoomActionTag];
CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:1.0f];
zoomAction.tag = kZoomActionTag;
[self runAction:zoomAction];
}
}
CCMenuItemImage Class is also available for displaying image with its scale in CCMenu.Please Check this link http://www.cocos2d-iphone.org/forum/topic/8310
[mainMenu alignItemsVerticallyWithPadding:15.0f];
No there is not another way. The thing is that menuItem is only acknowledging the files part of the sprites. It is not looking at other properties such as the scale property.