NSMutableArray, add sprite and remove last object (last added sprite)? - cocoa

-(void)createSprite{
CCSprite *shotV = [CCSprite spriteWithFile:#"green.png"];
[self addChild:shotV z:1];
[shotVArray addObject:shotV];
NSlog(#"%#",shotV);
}
-(void)aSpecialCase{
[self removeChild:[shotVArray lastObject] cleanup:YES];
}
I'm not getting this to work.
The function "createSprite" spams out sprites. "In aSpecialCase" I want to remove the last sprite that was created. Also hoping that removing it will end the current CCSequence for that instance.

-(void)aSpecialCase{
[self removeChild:[shotVArray lastObject] cleanup:YES];
}
This only removes the sprite from layer.. Doesn't remove it from array itself...
So better way is..
-(void)aSpecialCase{
CCSprite *sprite = [pshotVArray lastObject];
[self removeChild:sprite cleanup:YES];
[pshotVArray removeObject:sprite];
}
Hope this helps.. :)

Related

Whats the correct way to calculate point in view that mouse was clicked

I have a custom subclass of NSView in my app.
I would like to know the exact point in the view, relative to it's origin, that was clicked with the mouse. (i.e. Not relative to the Window origin, but relative to the custom-view origin).
I have always used this, which has worked perfectly:
-(void)mouseDown:(NSEvent *)theEvent
{
NSPoint screenPoint = [NSEvent mouseLocation];
NSPoint windowPoint = [[self window] convertScreenToBase:screenPoint];
NSPoint point = [self convertPoint:windowPoint fromView:nil];
_pointInView = point;
[self setNeedsDisplay:YES];
}
But now I get a warning that convertScreenToBase is deprecated and to use convertRectFromScreen instead. However I cannot get the same results from convertRectFromScreen, and anyway, I'm interested in a point, not a rect!
What should I use as the correct replacement for the deprecated code above?
Thanks in advance!
This line from your code:
NSPoint screenPoint = [NSEvent mouseLocation];
gets the location of the mouse cursor out of sync with the event stream. It's not the position of the event you're currently handling, which was a short time in the past; it's the position of the cursor right now, which means you're potentially skipping past some important stuff. You should almost always use the position in sync with the event stream.
To do that, use the theEvent parameter that your method receives. NSEvent has a locationInWindow property, which has already been translated to the coordinates of the window which receives it. That eliminates the need for you to convert it.
NSPoint windowPoint = [theEvent locationInWindow];
Your code to convert the window location to the view's coordinate system is fine.
I found the solution:
NSPoint screenPoint = [NSEvent mouseLocation];
NSRect screenRect = CGRectMake(screenPoint.x, screenPoint.y, 1.0, 1.0);
NSRect baseRect = [self.window convertRectFromScreen:screenRect];
_pointInView = [self convertPoint:baseRect.origin fromView:nil];
I have made a sample project with a window and tested the 'old' and new scenario. The result is the same in both cases.
You have to make one additional step: Create a simple rect with the screenPoint as origin. Then use the origin of the new, returned rect.
Here is the new code:
-(void)mouseDown:(NSEvent *)theEvent
{
NSPoint screenPoint = [NSEvent mouseLocation];
NSRect rect = [[self window] convertRectFromScreen:NSMakeRect(screenPoint.x, screenPoint.y, 0, 0)];
NSPoint windowPoint = rect.origin;
NSPoint point = [self convertPoint:windowPoint fromView:nil];
_pointInView = point;
[self setNeedsDisplay:YES];
}
I hope I was able to help you!
Simply use convert(_:from:) can be inaccurate, this can happen when event's window and view's window are not the same. Please check my answer in another question for a more robust way.
https://stackoverflow.com/a/69784415/3164091

Cocos2D - When I animate second sprite, first stops animating

So the issue here is when I create one animated projectile, everything is fine. As soon as the user creates a second, the first stops animating.
Alright, here's how I'm setting it up in my init:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
#"acorns.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode
batchNodeWithFile:#"acorns.png"];
[self addChild:spriteSheet];
NSMutableArray *flyingFrames = [NSMutableArray array];
for(int i = 1; i <= 4; ++i) {
[flyingFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"acorn%d.png", i]]];
}
CCAnimation *flying = [CCAnimation
animationWithFrames:flyingFrames delay:0.5f];
self.flying = [CCRepeatForever actionWithAction:
[CCAnimate actionWithAnimation:flying restoreOriginalFrame:NO]];
Then, in my create bullets method which is called when the user taps the screen, I do:
CCSprite *bullet = [CCSprite spriteWithSpriteFrameName:#"acorn1.png"];
bullet.position = CGPointMake(140.0f, FLOOR_HEIGHT+145.0f);
[bullet runAction:_flying];
[self addChild:bullet z:9];
[bullets addObject:bullet];
So the first time the user taps, everything works fine. The second time, an animated bullet is created, but the existing one stops animating, and so on.
I believe each sprite should have its own actions, ie you cant reuse an action while the action is in progress. Something like:
CCSprite *bullet = [CCSprite spriteWithSpriteFrameName:#"acorn1.png"];
bullet.position = CGPointMake(140.0f, FLOOR_HEIGHT+145.0f);
id _fly=[CCAnimation animationWithFrames:flyingFrames delay:0.5f];
id _flyForever = [[CCRepeatForever _fly];
[bullet runAction:_flyForever];
[self addChild:bullet z:9];
[bullets addObject:buller];
The flyingFrames can be referenced by multiple animations, so you must take care of retaining the array for reuse.

Sprite not visible at times

I have a cocos2d based iphone app with a problem. When the user pauses the game and then hits the resume button, some CCSprites will disappear from the screen.
This behavior is random, no pattern followed. I just know that this only happens when the user resumes the game.
Here is the code, what am I doing wrong?
I first thought it might be a memory management problem, but I never get any EXC_BAD_ACCESS when the user hits resume... So the sprites probably still exist.
The sprites are a property within an object I'll call "myobject".
In myObject.h I have:
#interface myObject : CCNode{
CCSprite *_sprite1,*_sprite2;
// some other code
}
#property (nonatomic,retain) CCSprite *sprite1,*sprite2;
And in myObject.m file:
#synthesize sprite1=_sprite1;
#synthesize sprite2=_sprite2;
+(id)create:(CCLayer*)scene{
myobject.sprite1 = [CCSprite spriteWithFile:spriteFile];
[scene addChild:myobject.sprite1];
// same for sprite2
}
-(void) move:(ccTime)dt{
//SOMECODE
self.sprite1.position=ccp(self.x,self.y); // same for sprite2
}
then they get moved around with a function called on the myobject.
In the main scene, here is how these objects are created and moved around:
myObject *myObject;
NSMutableArray *_myObjects;
#implementation HelloWorld
+(id) scene
{
CCScene *scene = [CCScene node];
HelloWorld *layer = [HelloWorld node];
[scene addChild:layer z:0 tag:33];
return scene;
}
-(id) init
{
if( (self=[super init] )) {
_myObjects = [[NSMutableArray alloc] init];
// some other code
}
}
-(void) addObject(){
myObject=[myObject create:self];
[_fallingObjects addObject:fallingObject];
}
-(void) nextFrame:(ccTime) dt{
for(myObject *theObject in _myObjects){
[theObject move:dt];
}
}
// And here is the function that does the pause/unpause, here it is:
- (void) pauseGame{
if(pauseStatus==0){
[[CCDirector sharedDirector] pause];
pauseStatus=1;
// some code to display menu etc... such as:
[self addChild:pauseMenu z:10];
}
else{
[self removeChild:pauseMenu cleanup:YES];
[self removeChild:scoreLabel cleanup:YES];
[self removeChild:highscoreLabel cleanup:YES];
[self removeChild:titleLabel cleanup:YES];
[self removeChild:pauseLayer cleanup:YES];
[[CCDirector sharedDirector] resume];
pauseStatus=0;
}
}
==EDIT===
I have discovered that this problem is also true for sprites that I add directly to my scene, such as the sprite clown added as shown below:
CCSprite *clown;
#implementation HelloWorld
-(id) init
{
if( (self=[super init] )) {
// some code
clown = [[CCSprite spriteWithFile:#"clown.png"] retain];
[self addChild:clown z:2];
// some more code
}
}
#end
==END OF EDIT===
There's something odd about your setup. FallingObject is a CCNode that contains two sprites. Yet instead of adding the sprites to the FallingObject class (self), and then adding FallingObject to the scene, you are adding the sprites directly to the scene. This leaves the FallingObject node outside of the scene hierarchy.
That's also why you have to do things like updating sprite positions manually which would otherwise require no code at all:
self.sprite1.position=ccp(self.x,self.y);
For what it's worth, I suppose that due to the nonconformity of this setup you may be accidentally moving the sprites outside the screen, or the node either isn't properly paused or resumed.

xcode Removing Some Subviews from view

Greetings all,
I am a noob and I have been trying to work through this for a few days.
I am adding images to a view via UItouch. The view contains a background on top of which the new images are add. How do I clear the images I am adding from the subview, without getting rid of the UIImage that is the background. Any assistance is greatly appreciated. Thanks in Advance.
here is the code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event {
NSUInteger numTaps = [[touches anyObject] tapCount];
if (numTaps==2) {
imageCounter.text =#"two taps registered";
//__ remove images
UIView* subview;
while ((subview = [[self.view subviews] lastObject]) != nil)
[subview removeFromSuperview];
return;
}else {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
CGRect myImageRect = CGRectMake((touchPoint.x -40), (touchPoint.y -45), 80.0f, 90.0f);
UIImageView *myImage = [[UIImageView alloc] initWithFrame:myImageRect];
[myImage setImage:[UIImage imageNamed:#"pg6_dog_button.png"]];
myImage.opaque = YES; // explicitly opaque for performance
[self.view addSubview:myImage];
[myImage release];
[imagesArray addObject:myImage];
NSNumber *arrayCount =[self.view.subviews count];
viewArrayCount.text =[NSString stringWithFormat:#"%d",arrayCount];
imageCount=imageCount++;
imageCounter.text =[NSString stringWithFormat:#"%d",imageCount];
}
}
What you need is a way of distinguishing the added UIImageView objects from the background UIImageView. There are two ways I can think of to do this.
Approach 1: Assign added UIImageView objects a special tag value
Each UIView object has a tag property which is simply an integer value that can be used to identify that view. You could set the tag value of each added view to 7 like this:
myImage.tag = 7;
Then, to remove the added views, you could step through all of the subviews and only remove the ones with a tag value of 7:
for (UIView *subview in [self.view subviews]) {
if (subview.tag == 7) {
[subview removeFromSuperview];
}
}
Approach 2: Remember the background view
Another approach is to keep a reference to the background view so you can distinguish it from the added views. Make an IBOutlet for the background UIImageView and assign it the usual way in Interface Builder. Then, before removing a subview, just make sure it's not the background view.
for (UIView *subview in [self.view subviews]) {
if (subview != self.backgroundImageView) {
[subview removeFromSuperview];
}
}
A more swiftly code for approach #1 in only one functional line of code :
self.view.subviews.filter({$0.tag == 7}).forEach({$0.removeFromSuperview()})

Changing NSApplicationIcon across a running application?

I'd like to adjust the NSApplicationIcon image that gets shown automatically in all alerts to be something different than what is in the app bundle.
I know that it's possible to set the dock icon with [NSApplication setApplicationIconImage:] -- but this only affects the dock, and nothing else.
I'm able to work around this issue some of the time: I have an NSAlert *, I can call setIcon: to display my alternate image.
Unfortunately, I have a lot of nibs that have NSImageView's with NSApplicationIcon, that I would like to affect, and it would be a hassle to create outlets and put in code to change the icon. And for any alerts that I'm bringing up with the BeginAlert... type calls (which don't give an NSAlert object to muck with), I'm completely out of luck.
Can anybody think of a reasonable way to globally (for the life of a running application) override the NSApplicationIcon that is used by AppKit, with my own image, so that I can get 100% of the alerts replaced (and make my code simpler)?
Swizzle the [NSImage imageNamed:] method? This method works at least on Snow Leopard, YMMV.
In an NSImage category:
#implementation NSImage (Magic)
+ (void)load {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// have to call imageNamed: once prior to swizzling to avoid infinite loop
[[NSApplication sharedApplication] applicationIconImage];
// swizzle!
NSError *error = nil;
if (![NSImage jr_swizzleClassMethod:#selector(imageNamed:) withClassMethod:#selector(_sensible_imageNamed:) error:&error])
NSLog(#"couldn't swizzle imageNamed: application icons will not update: %#", error);
[pool release];
}
+ (id)_sensible_imageNamed:(NSString *)name {
if ([name isEqualToString:#"NSApplicationIcon"])
return [[NSApplication sharedApplication] applicationIconImage];
return [self _sensible_imageNamed:name];
}
#end
With this hacked up (untested, just wrote it) jr_swizzleClassMethod:... implementation:
+ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ {
#if OBJC_API_VERSION >= 2
Method origMethod = class_getClassMethod(self, origSel_);
if (!origMethod) {
SetNSError(error_, #"original method %# not found for class %#", NSStringFromSelector(origSel_), [self className]);
return NO;
}
Method altMethod = class_getClassMethod(self, altSel_);
if (!altMethod) {
SetNSError(error_, #"alternate method %# not found for class %#", NSStringFromSelector(altSel_), [self className]);
return NO;
}
id metaClass = objc_getMetaClass(class_getName(self));
class_addMethod(metaClass,
origSel_,
class_getMethodImplementation(metaClass, origSel_),
method_getTypeEncoding(origMethod));
class_addMethod(metaClass,
altSel_,
class_getMethodImplementation(metaClass, altSel_),
method_getTypeEncoding(altMethod));
method_exchangeImplementations(class_getClassMethod(self, origSel_), class_getClassMethod(self, altSel_));
return YES;
#else
assert(0);
return NO;
#endif
}
Then, this method to illustrate the point:
- (void)doMagic:(id)sender {
static int i = 0;
i = (i+1) % 2;
if (i)
[[NSApplication sharedApplication] setApplicationIconImage:[NSImage imageNamed:NSImageNameBonjour]];
else
[[NSApplication sharedApplication] setApplicationIconImage:[NSImage imageNamed:NSImageNameDotMac]];
// any pre-populated image views have to be set to nil first, otherwise their icon won't change
// [imageView setImage:nil];
// [imageView setImage:[NSImage imageNamed:NSImageNameApplicationIcon]];
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert setMessageText:#"Shazam!"];
[alert runModal];
}
A couple of caveats:
Any image view already created must have setImage: called twice, as seen above to register the image changing. Don't know why.
There may be a better way to force the initial imageNamed: call with #"NSApplicationIcon" than how I've done it.
Try [myImage setName:#"NSApplicationIcon"] (after setting it as the application icon image in NSApp).
Note: On 10.6 and later, you can and should use NSImageNameApplicationIcon instead of the string literal #"NSApplicationIcon".

Resources