CCSprite is not displaying on the iPhone - 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.

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:

objective-c: Delegate object argument getting overwritten when i create multiple instances of custom class

EDIT: I apologize for wasting time, the erorr had nothing to do with what I'm taking about but rather some logic in my code that made me believe this was the cause. I'm awarding Kevin with the correct answer since using his idea to pass the whole AuthorSelectionView, and his note on correcting the NSNumer mistake. Sorry about that.
I've been trying to figure this out for hours, and even left it alone for a day, and still can not figure it out...
My situation is as follows:
I've created a custom class that implements 'UIView' and made this class into a protocol as follows:
custom UIView h file
#protocol AuthorSelectionViewDelegate <NSObject>
-(void)AuthorSelected:(NSNumber *)sender;
#end
#import <UIKit/UIKit.h>
#interface AuthorSelectionView : UIView
#property (nonatomic,assign) id<AuthorSelectionViewDelegate> delegate;
#property (strong,retain) NSNumber *authorID;
- (id)initWithFrame:(CGRect)frame withImage:(UIImage *)img withLabel:(NSString *)lbl withID:(int)authorID ;
#end
the implementation...
- (id)initWithFrame:(CGRect)frame withImage:(UIImage *)img withLabel:(NSString *)lbl withID:(int)authorID
{
self = [super initWithFrame:frame];
if (self) {
self.authorID = [[NSNumber alloc] initWithInt:authorID]; //used to distinguish multiple instances of this class in a view.
...
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, FRAMEWIDTH, FRAMEHEIGHT)];
[button addTarget:self action:#selector(CUSTOMBUTTONCLICK) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
}
return self;
}
- (void) CUSTOMBUTTONCLICK
{
[self.delegate performSelector:#selector(AuthorSelected:) withObject:self.authorID];
}
Now the method in my delegate object gets called just fine, but my major problem here is that something is going on with the object being pass through when i have multiple instances of the AuthorSelected class alloc'd.. (the NSNumber authorID). I'm getting some weird behavior with it. It seems almost random with the value being passed, but i'm detecting some pattern where the value passed through is coming up late..
thats confusing so ill try to explain:
I create two instances of the AuthorSelected view, one with authorID=1 and the other with authorID=2.
On the first press, lets say i press the first button, i'll get 1 as expected.
On the second press, if I press the 1st custom button, i'll get '1', but if i press the second i'll still get 1.
On the third go, either button will give me back '2'
I feel like this is some issue with pointers since that has always been a weak point for me, but any help would be greatly appreciated as I can not seem to figure this one out.
Thank you!
EDIT:
as requested here is how I create the AuthorSelectionView Objects...
AuthorSelectionView * asView01 = [[AuthorSelectionView alloc]
initWithFrame:CGRectMake(0, 0, FRAMEWIDTH, FRAMEHEIGHT)
withImage:userPic1
withLabel:randomUserName
withID:1];
asView01.delegate = self;
AuthorSelectionView * asView02 = [[AuthorSelectionView alloc]
initWithFrame:CGRectMake(0, 0, FRAMEWIDTH, FRAMEHEIGHT)
withImage:userPic2
withLabel:randomUserName2
withID:2];
asView02.delegate = self;
A detail that may be important:
As soon as i click on one of these custom views, my code is set to (for now) call the method that runs the above AuthorSelectionView alloc code, so that i can refresh the screen with the same layout, but with different userpic/userName. This is poor design, I know, but for now I just want the basic features to work, and will then worry about redrawing. I metion this tidbit, becuase I understand that objective-c 'layers' veiws on top of eachother like paint on a canvas, and had a thought that maybe when I click what I think may be my 2nd button, its really 'clicking' the layer beneath and pulling incorrect info.
Your description of the problem is a bit confusing, but this line in your init is very clearly wrong:
self.authorID = [self.authorID initWithInt:authorID];
In -init, your property self.authorID defaults to nil, so the expression [self.authorID initWithInt:authorID] is equivalent to [nil initWithInt:authorID], which evaluates back to nil. So you should actually be seeing nil in your action. You probably meant to say self.authorID = [NSNumber numberWithInt:authorID]
You're missing the alloc message, so this message:
self.authorID = [self.authorID initWithInt:authorID];
Is sent to a nil target, because self.authorID hasn't been allocated yet.
So first allocate it, then use the init method, or mix these two messages. A faster syntax allows to do it this way:
self.authorID= #(authorID);
EDIT
I don't see where you initialize the delegate, that method shouldn't even be called if you haven't initialized it. Show the code where you create the AuthorSelectionView objects and set the delegates.
instead of :
self.authorID = [self.authorID initWithInt:authorID];
put :
self.authorID = [NSNumber numberWithInt:authorID];
or
self.authorID = [[NSNumber alloc] initWithInt:authorID];
EDIT :
Don't you have errors or warnings in your code ? I can't see you returning self object in the init method ("return self;")

Call a method from another class when shake is detected

HERE IS THE CODE: http://min.us/mWdMO0n14
I'm a Obj C newbie, so I've searched quite a bit, but haven't found anything that can solve my problem.
I have CalculatorViewController.h and .m and then CalculatorBrain.h and.m (Stanford Lectures)
in CalculatorBrain.m, I have the following method, with all of the variables defined as private in the CalculatorBrain header.
- (void)clearEverythingOnShakeGesture{
operand = 0;
waitingOperation = #"";
waitingOperand = 0;
}
Then in CalculatorBrain.m , I have everything set up to detect shakes, as follows. I've included some of the code above the shake detection just so you have a general idea.
#interface CalculatorViewController()
#property(nonatomic, retain) CalculatorBrain *brain;
#end
#implementation CalculatorViewController
#synthesize brain;
- (CalculatorBrain *)brain {
if (!brain) {
brain = [[CalculatorBrain alloc] init];
}
return brain;
}
-(BOOL)canBecomeFirstResponder{
return YES;
}
-(void)viewDidAppear: (BOOL) animated{
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.subtype == UIEventSubtypeMotionShake)
{
NSLog(#"SHAKE IT!");
[brain clearEverythingOnShakeGesture]; //********** not sure how to call this.
}
}
I'm not sure how to call [brain clearEverythingOnShakeGesture]; , because I get the error "Class method +clearEverythingOnShakeGesture not found, defaults to return type id". However, if I make it a class method, the variables inside are instance variables, which provides another error. Any help greatly appreciated.
The project's AppDelegate posted in the comment above is building the calculator view controller from a nib, then releasing it immediately. The app functions partially, but the UILabel property to be cleared on the shake gesture is nulled at that point.
Also, it's a good practice to declare private properties in the private category, synthesize them with _underscore aliases, and refer to them as self.property outside of synthesized methods.
Are you #import-ing the CalculatorBrain.h? Also, you're using a nice lazy initialization pattern by building the CalculatorBrain in the getter, but you're not calling the getter in the motionBegan: method. Try [self.brain clearEverything ...] to get the brain instance.
I don't see anything in the code that would make the compiler think you have a class method. So that's mysterious. Please double check about the header import. You are correct that the clearEverything... should be an instance method.

"'CCLayer' may not respond to ... "

Playing with cocos2d and this seems to come up in some instances of message passing. I have to admit, I'm not well versed in Objective-C yet as I'm reading through the book as I write some code.
For people familiar with cocos2d, you have a CCLayer class. In my case, I have a GameLayer which has some children layers, like so:
#interface GameLayer : CCLayer {
CCSprite *_background;
CCPanZoomController *_controller;
CCLayer *_truckLayer;
}
I then have a special initWithLayers method to add my child layers within this layer:
-(id)initWithLayers:(TruckLayer *)truck
{
if( (self=[super init])) {
self.truckLayer = truck;
.....
}
Now if I ever send a message to self.truckLayer I get the following warning in Xcode:
'CCLayer' may not respond to 'getNextAvailableSpot'
An example might be doing something like so:
CGPoint nextSpot = [self.truckLayer getNextAvailbleSpot];
All this code compiles and runs successfully, so I'm curious as to what I am missing here? I don't want my code literred with these if I'm doing something wrong here.
It's because your declare *_truckLayer as CCLayer, but on your init you assign (TruckLayer *)truck to your CCLayer* _truckLayer. So CCLayer doesn't know about getNextAvailableSpot method, so the compiler gives you warning.
The code runs fine because _truckLayer is a pointing to correct TruckLayer, which knows about getNextAvailableSpot.
Simply change your declaration and the warning will be gone.
#interface GameLayer : CCLayer
{
CCSprite *_background;
CCPanZoomController *_controller;
CCLayer *_truckLayer;
}
to
#class TruckLayer;
#interface GameLayer : CCLayer
{
CCSprite *_background;
CCPanZoomController *_controller;
TruckLayer *_truckLayer;
}
Just as the warning says: the compiler can't guarantee that CCLayer has a method to handle the getNextAvailableSpot message. Unless you've somehow added in such a method dynamically, or have some other (very good) reason to believe that this method is present, this is probably a problem.

How reliable [CALayer animationForKey:] is?

I have a subclass of CALayer called MyLayer:
#interface MyLayer : CALayer
#property (nonatomic,readonly) BOOL busy;
-(void)performTask1;
-(void)performTask2;
-(void)performTask3;
#end
In performTask* functions I say:
-(void)performTask*
{
CAKeyframeAnimation *animation = [...];
...
animation.removedOnCompletion = YES; // "YES" is default anyway
...
[self addAnimation:animation
forKey:TASK*_KEY];
}
The busy property is for the caller and implemented like this:
#dynamic busy;
-(BOOL)busy
{
// i.e. for some specific ids
return ([self animationForKey:TASK1_KEY] != nil ||
[self animationForKey:TASK3_KEY] != nil);
}
What I see is that this approach is not reliable... I can see on the screen that animation is finished (nothing is moving, etc.), while animationForKey: does NOT return nil. The behavior is semi-random... Most of times it goes as expected. But sometimes it takes 1-2 seconds before I start getting nil-s.
This weird behavior disappears, if I set a delegate to animation animation.delegate = self and implement animationDidStop:finished:.
Someone experienced this as well?
Declaring #dynamic causes CALayer to implement the accessor method for you (see Properties on CALayer subclass aren't getting observed by CATransaction and CALayer and CAAnimation's dynamic resolution of unimplemented property accessors). So it's possible that you are just seeing confusion in which version of "busy" is getting called. Why did you declare "#dynamic" in the first place?
There could also be something funky happening with animationForKey, but I'd try removing the "#dynamic" first.