Reuse Same Sprite Cocos2d 3.0 - objective-c

I am wanting to use the same sprite multiple times in Cocos2d 3.0
at the moment when i do this i get the following error
"reason: 'child already added to another node. It can't be added again'"
i have looked at other responses to similar questions here on stacker overflow and they state that spriteBatchNode is required. However this has depreciated in version 3.0.
Make local instances of the sprites *( i have used nodes here as i get an error when trying to add them to the array (Incompatible pointer types assigning to 'CCSprite *' from 'CCNode '))
CCNode *_sprite1;
CCNode *_sprite2;
CCNode *_sprite3;
CCNode *_sprite4;
I currently Have an array where sprites(CCNodes)are added
_array1 = #[_sprite1,_sprite2,_sprite3,_sprite4];
_array2 = #[_sprite1,_sprite2,_sprite3,_sprite4];
I would then make a call to spawn the sprites
-(void)spawnSprites1
{
int randomNumber = [self generateRandomNumberBetweenMin:0 Max:_array1.count-1];
CCSprite *sprite = [_array1 objectAtIndex:randomNumber];
sprite.position = ccp(_size.width +100 , _size.height *0.26);
sprite.zOrder = DrawingOrderPlayer;
[_physicsNode addChild:sprite];
CCLOG(#"content size = %f",_size.height);
}
-(void)spawnSprites2
{
int randomNumber = [self generateRandomNumberBetweenMin:0 Max:_array2.count-1];
CCSprite *sprite = [_array2 objectAtIndex:randomNumber];
sprite.position = ccp(_size.width +100 , _size.height *0.26);
sprite.zOrder = DrawingOrderPlayer;
[_physicsNode addChild:sprite];
CCLOG(#"content size = %f",_size.height);
}
if the same sprite is drawn from either i receive the above crash log.
Does anybody know how to resolve this issue in Cocos2d v3.0
Thanks

Each new sprite needs to be a new CCSprite instance. Since cocos2d doesn't implement NSCopying yet, the easiest way is to initialize new sprites using one template instance's texture, like so:
CCSprite* newSprite = [CCSprite spriteWithTexture:templateSprite.texture];
Then assign any property from the templateSprite to newSprite if the copy should have the same value(s).

Problem resolved, rather the pulling sprites out of an array randomly, it was just as easy to randomly pull them using a switch statement.

Related

SKSpriteNode pools in iOS 8 seem to be allocated to overlapping memory

I might be missing something. But my current app on the appstore works in iOS 7, but in iOS 8 completely fails because it won't create a preallocated pool of sprites. They appear to be written to the same address unless the sprites have specifically different properties.
In iOS 7 the following code produces a set with 4 unique objects. In iOS 8, the same code produces a set with only 1 object:
NSMutableSet *aSet = [NSMutableSet set];
SKColor *sameColor = [SKColor redColor];
CGSize sameSize = CGSizeMake(10, 10);
for (int i = 0; i < 4; i++) {
//allocate a brand new sprite
SKSpriteNode *thisSprite1 = [[SKSpriteNode alloc] initWithColor:sameColor size:sameSize];
[aSet addObject:thisSprite1];
}
NSLog(#"aSet Count: %i", aSet.count);
iOS8 Result:
2014-09-09 15:06:43.065 MSM[383:27490] aSet Count: 1
Am I going crazy? Amazingly, pretty much my entire app is based on this code concept repeated over and over again. If I do the same thing, but use something like NSObject, then the problem goes away, so it appears to be a new change to SKSprite. I know I can work around it with some crazy stuff, but is a huge pain, since I shouldn't have to do that, and I was hoping to avoid another version submission.
Thanks to Josh for the direction on how to solve this new bump in the road.
I subclassed SKSpriteNode, overriding -isEqual and -hash, to both be what my best guess at the NSObject implementation is. Then just did a Find/Replace All in Project for "SKSpriteNode" for my subclass name, and all is back to it was in the iOS 7 build:
-(BOOL)isEqual:(id)object{
return self == object;
}
- (NSUInteger)hash
{
return (NSUInteger)self;
}

How to switch two objects in objective-c

I am trying to find if collision occurs between two rectangles in objective-c. I thought one way to accomplish this would be detect the rectangle that is closest to 0,0 point then do rest of the work.
I wrote a function that takes two rectangle objects as parameters and does the math to calculate area, distance to origin etc....
So lets say rect1 is at (100,200) and rect1's width is 100 height 200, rect2 is at 150,150 and rect2's width is 100 height 200 this is calculated by function well enough.
If I switch rec1 and rect2 properties, so rect1 will be at 150,150 while rect2 will be at 100,200. And call following function
-(Rectangle*)intersect:(Rectangle*)rectA:(Rectangle*)rectB{
//check closest rectangle to 0,0 and switch rectangles
if (rectA.origin.x>rectB.origin.x) {
Rectangle *temporary = [[Rectangle alloc] init];
temporary=rectA;
rectA=rectB;
rectB=temporary;
[temporary release];
}
float rectAX = rectA.origin.x;
float rectAY = rectA.origin.y;
float rectBX = rectB.origin.x;
float rectBY = rectB.origin.y;
When I enable guard malloc and zombies I get following error:
-[Rectangle origin]: message sent to deallocated instance 0x100acffd0
As soon as rectA.origin.x; is called I get the error.
So Howcome rectA or rectB is deallocated? What is the correct way to switch two objects that has bunch of properties ?
There is a built in function for comparing CGRects CGRectIntersectsRect(rectA, rectB) that you can use to check your rectangle's frames :)
As far as your code for switching you have created a third object by allocing temporary. Then you set the temporary pointer at rectA and then you release rectA at the end since its pointing to temporary. Leaving the newly created object as a leak and then sending messages to the released rectA.
You don't really want to swap object pointers like that if you can help it in my experience. But if you absolutely have to and understand what's going on you could do it like this:
// Create copies of your objects
Rectangle *rectACopy = [rectA copy];
Rectangle *rectBCopy = [rectB copy];
// release the originals.
[rectA release];
[rectB release];
// Set your copies to the original pointers.
rectA = rectBCopy;
rectB = rectACopy;
NSCopying Protocol
First you need to implement the protocol.
#interface Rectangle : NSObject <NSCopying>
Then you need to create the new method. This will create a new object but with all the same values.
- (id)copyWithZone:(NSZone *)zone
{
id copy = [[[self class] alloc] init];
if (copy) {
// Copy NSObject based properties like UIViews, NSArrays, Subclassed objects.
[copy setObjectProperty:[self.objectProperty copy]];
// Set primitives like ints, CGRects, Bools.
[copy setPrimitiveProperty:self.primitiveProperty];
}
return copy;
}
You don't need to allocate a new object instance for temporary (and therefore you don't need to release it either). You are just taking your 2 existing pointers and switching them around. You're correct to use a 3rd variable (temporary) but you don't need to allocate any new space because you're not moving anything in memory, just swapping which variables point to the existing objects.
-(Rectangle*)intersect:(Rectangle*)rectA:(Rectangle*)rectB{
//check closest rectangle to 0,0 and switch rectangles
if (rectA.origin.x>rectB.origin.x) {
//Rectangle *temporary = [[Rectangle alloc] init]; // here you don't need to allocate as you are not using this object
// So use
Rectangle *temporary=rectA;
rectA=rectB;
rectB=temporary;
//[temporary release]; //you don't need this. Here you were releasing rectA not the temp rect that you allocated just below if, as you assign rectA to temporary
}
float rectAX = rectA.origin.x;
float rectAY = rectA.origin.y;
float rectBX = rectB.origin.x;
float rectBY = rectB.origin.y;

CCMenuItemImage - setSelectedImage and setNormal Image

Ran across this while working with CCMenuItemImage. It seems like I have to set the SelectedImage and the NormalImage to different CCSprites, otherwise it crashes my application. (I plan on using unique assets later on for both states) CCMenuItemImage *ItemButton; is defined / initialized.
The following does not work:
CCSprite *updatedSprite = [CCSprite spriteWithFile:#"1_button.png"];
[ItemButton setNormalImage:updatedSprite];
[ItemButton setSelectedImage:updatedSprite];
The following does work:
CCSprite *updatedSpriteNormal = [CCSprite spriteWithFile:#"1_button.png"];
[ItemButton setNormalImage:updatedSpriteNormal];
CCSprite *updatedSpriteSelected = [CCSprite spriteWithFile:#"1_button.png"];
[ItemButton setSelectedImage:updatedSpriteSelected];
Curious to know why that would happen, I've done some digging but couldn't find anything definitive. Any insight would be great.
When you setSelectedImage, the sprite is added as a child to the ItemButton, thus it has a parent. You must create a second instance of CCSprite to setNormalImage, because the node hierarchy of cocos2d will always prevent adding as a child an object that already has a parent.

cocos2d sub-classing

I know that cocos2d has scheduling callbacks to do nice things but when you need to use one CCAction (like CCMoveTo one) in order to move a sprite from position a to b, you do not have the ability to make small position arrangements to the sprite position for as long as the action is in effect.
The only possible way I found is by making a sub-class of CCMoveTo in order to check for obstacles and therefore provide some kind of movement to the left or right to a sprite that was moving from top to the bottom of the iPhone screen. The problem is that the sub-class does not have access to the parent class' instance variables (like the startPosition_ one) because they have not been declared as properties.
So I used the following snippet to overcome this situation but I wonder if I am doing something wrong...
- (void)myUpdate:(ccTime)time {
if(delegate && method_) {
NSNumber *num = (NSNumber *)[delegate performSelector:method_ withObject:ownTarget];
if(num) {
double xpos = [num doubleValue];
[num release];
CCMoveTo *parent = [super retain];
parent->startPosition_.x += xpos;
[parent release];
}
[super update:time];
}
Is it correct to retain/release the super-class? The "[super update:time];" at the bottom of the code will make the final positioning.
CCMoveTo *parent = [super retain];
Ouch! This statement makes absolutely no sense. It is the same as writing:
[self retain];
As for accessing the super class' instance variables: unless they're declared #private you can access them. I just checked: they're not #private. You should be able to write in your subclass:
startPosition_.x += xpos;
If that doesn't work make sure your class is really a subclass of CCMoveTo, and not some other class.
Finally, I'd like to say that actions are very limited when it comes to implementing gameplay. You're probably much better off to simply animate your game objects by modifying their position property every frame, based on a velocity vector. You have much more freedom over the position and position updates, and none of the side effects of actions such as a one-frame delay every time you run a new action.
-(void) update:(ccTime)delta
{
// modify velocity based on whatever you need, ie gravity, or just heading in one direction
// then update the node's position by adding the current velocity to move it:
self.position = CGPointMake(self.position.x + velocity.x, self.position.y + velocity.y);
}

Collision detection using mulitple subviews within a moving view

I am working on a game in OBJ C that has a ball view and a stage view. The stage view has 4 subviews. All views are UIImageViews. I have a method for collision detection that is working. I would like to expand it to more than 4 subviews without simply creating more lines of code. Looking at the code below, is there a way to simplify this into loops instead. Thanks!
// convert each square to be relevant to ball's superview in order to collision detect
CGRect square_01Frame = [ball.superview convertRect:square_01.frame fromView:square_01.superview];
CGRect square_02Frame = [ball.superview convertRect:square_02.frame fromView:square_02.superview];
CGRect square_03Frame = [ball.superview convertRect:square_03.frame fromView:square_03.superview];
CGRect square_04Frame = [ball.superview convertRect:square_04.frame fromView:square_04.superview];
// convert CGRects to NSStrings for storage in square_frames array
NSString *square_01FrameString = NSStringFromCGRect(square_01Frame);
NSString *square_02FrameString = NSStringFromCGRect(square_02Frame);
NSString *square_03FrameString = NSStringFromCGRect(square_03Frame);
NSString *square_04FrameString = NSStringFromCGRect(square_04Frame);
// load array of NSStrings
[square_frames replaceObjectAtIndex:0 withObject:square_01FrameString];
[square_frames replaceObjectAtIndex:1 withObject:square_02FrameString];
[square_frames replaceObjectAtIndex:2 withObject:square_03FrameString];
[square_frames replaceObjectAtIndex:3 withObject:square_04FrameString];
// create a for loop
for (int i=0; i<4; i++) { // 4 squares
// create test frame
CGRect test_frame = CGRectFromString([square_frames objectAtIndex:i]);
if (CGRectIntersectsRect(test_frame,ball.frame)) { // collision detection
// do something
}
}
Well, I would do a number of things.
First, I would create a ball "model", just an NSObject subclass to represent the Ball. Probably, that would have a property "location" or something, which is the CGRect.
Then, your current view could have an array of ball objects on the screen, and just loop through them.
Overall, though, I don't think using UIView's rects is the best way to manage collision detection. I think you'd be better off defining that in some other way, and then simply updating the UI accordingly.
Generally, it's not a good idea to rely on your UI implementation for game design. It makes it hard to change (as you note in your question).