EXEC_BAD_ACCESS on SKAction - ios

I'm developing a Spritekit game.
I recevie an EXEC_BAD_ACCESS exception when touchind a node i try to run an action
This function catches the touch
-(void)selectNodeForTouch:(CGPoint)location {
SKNode *touchedNode = (SKNode *)[self nodeAtPoint:location];
NSLog(#"touched : %#",touchedNode.name);
if([[touchedNode name] isEqualToString:#"piece"]){
Piece *piece = (Piece *)[self nodeAtPoint:location];
if ([piece isRotated])
[piece rotateToFront];
else
[piece rotateToBack];
}
}
The object Piece Method rotateBack:
-(void)rotateToBack{
SKAction *rotationToBack = [SKAction animateWithTextures:[self animationFrames] timePerFrame:0.11];
[self runAction:rotationToBack withKey:#"rotation-to-back"];
[self setRotated:YES];
};
[self animationFrames] is a NSMutable array converted into NSArray with
[self setAnimationFrames:[NSArray arrayWithArray:frames]];
Finally frames is an the NSMutableArray containing some SKTextures
The executions brokes on animation execution.

In the Piece class
Change this line:
NSString *imageName = [NSString stringWithFormat:#"%#-1%#",[self baseImageName],imageResolution];
to
NSString *imageName = [NSString stringWithFormat:#"%#-1",[self baseImageName]];

Related

iOS SpriteKit animation not working

I am new to the iOS native game development. I am trying to create animation using some frames for Ideal state for the character. I am following tutorial from the Ray's Website. Look like I am doing everything fine but I can't see animation in working. Only first frame (default) is visible all the time. I debug the code & it's indeed visiting blinking player method where all 3 frames are also present, but nothing happening on screen.
It will be great if someone can guide/help me identify the issue.
#implementation GameScene
{
NSArray *_playerBlinkFrames;
}
// Player
SKNode *_player;
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
[self createPlayer];
}
return self;
}
- (void) createPlayer
{
SKTextureAtlas *playerAnimatedAtlas = [SKTextureAtlas atlasNamed:#"Assets"];
_player = [SKNode node];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Idle"];
[_player addChild:sprite];
[sprite setName:#"Ball"];
NSMutableArray *blinkFrames = [NSMutableArray array];
SKTexture *temp = [playerAnimatedAtlas textureNamed:#"Idle.png"];
SKTexture *temp2 = [playerAnimatedAtlas textureNamed:#"Blink.png"];
SKTexture *temp3 = [playerAnimatedAtlas textureNamed:#"LookRight.png"];
[blinkFrames addObject:temp];
[blinkFrames addObject:temp2];
[blinkFrames addObject:temp3];
_playerBlinkFrames = blinkFrames;
_player.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:_player];
[self blinkingPlayer];
}
-(void)blinkingPlayer
{
[_player runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:_playerBlinkFrames
timePerFrame:0.3f
resize:NO
restore:YES]] withKey:#"blinkingInPlacePlayer"];
return;
}
Thanks.
Try this:
// Player
SKSpriteNode *_player;
//..//
SKTextureAtlas *playerAnimatedAtlas = [SKTextureAtlas atlasNamed:#"Assets"];
NSMutableArray *blinkFrames = [NSMutableArray array];
SKTexture *temp = [playerAnimatedAtlas textureNamed:#"Idle.png"];
SKTexture *temp2 = [playerAnimatedAtlas textureNamed:#"Blink.png"];
SKTexture *temp3 = [playerAnimatedAtlas textureNamed:#"LookRight.png"];
[blinkFrames addObject:temp];
[blinkFrames addObject:temp2];
[blinkFrames addObject:temp3];
_playerBlinkFrames = blinkFrames;
SKTexture *tempSprite = _playerBlinkFrames[0];
_player = [SKSpriteNode spriteNodeWithTexture:tempSprite];
I've not tested it, but i think you have create a SKNode player covered by a spritenode.
let me know

Code only working on iOS 7

Hi I've been making a game with the SpriteKit framework and I set a collision bit mask when 2 objects collide. One of those objects, let's say the object A, can have 2 states: black or normal so when the two objects collide and the A object is on normal state it adds a point but when the two objects collide and the A object is on black state the game is over. This code is working fine for me on iOS 7 but when I run it on iOS 8 and if the A object state is black it acts like if it's on normal state and adds a point. Why this is happening? Is different the code for iOS 7 and 8? Please can anyone help me, here's the code:
-(void)didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody;
SKPhysicsBody *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}else {
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (firstBody.categoryBitMask == objectACategory && secondBody.categoryBitMask == objectBCategory) {
NSLog(#"OBJECT A CAUGHT");
[firstBody.node removeFromParent];
[GameState sharedInstance].score++;
_scoreLabel.text = [NSString stringWithFormat:#"%d", [GameState sharedInstance].score];
_scoreLabel_2.text = [NSString stringWithFormat:#"%d", [GameState sharedInstance].score];
gameUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"sound " ofType:#"wav"]];
_gameSound = [[AVAudioPlayer alloc] initWithContentsOfURL:gameUrl error:nil];
_gameSound.delegate = self;
[_gameSound play];
if ([[firstBody.node.userData valueForKey:#"Black"] boolValue]) {
[self removeActionForKey:origamiFallKey];
NSLog(#"YOU LOSE");
[self gameOver];
[self runAction:[SKAction sequence:#[
[SKAction waitForDuration:0.5],
[SKAction performSelector:#selector(removeWhenLose) onTarget:self]]]];
}
}
For setting the state I used this code in the method which adds a objectA:
// Black
if(arc4random_uniform(6) == 0) {
_objectA.texture = [SKTexture textureWithImageNamed:#"blackImage.png"];
_objectA.physicsBody.collisionBitMask = objectACategory;
_objectA.userData = [[NSMutableDictionary alloc] init];
[_objectA.userData setValue:#YES forKey:#"Black"];
blackObjectA = YES;
}
I finally solved this problem. First I had to create the collision variable:
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
And with write the collisions using this:
-(void)didBeginContact:(SKPhysicsContact *)contact {
if (collision == (objectACategory | objectBCategory)) {
if ([[firstBody.node.userData valueForKey:#"Black"] boolValue]){
[self removeActionForKey:fallKey];
NSLog(#"YOU LOSE");
[self gameOver];
[self runAction:[SKAction sequence:#[
[SKAction waitForDuration:0.5],
[SKAction performSelector:#selector(removeWhenLose) onTarget:self]]]];
} else {
[firstBody.node removeFromParent];
[GameState sharedInstance].score++;
_scoreLabel.text = [NSString stringWithFormat:#"%d", [GameState sharedInstance].score];
_scoreLabel_2.text = [NSString stringWithFormat:#"%d", [GameState sharedInstance].score];
gameUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"origami " ofType:#"wav"]];
_gameSound = [[AVAudioPlayer alloc] initWithContentsOfURL:gameUrl error:nil];
_gameSound.delegate = self;
[_gameSound play];
}
}

game crashes with SIGKILL when running animation on custom SKSpriteNode

I'm encountering a very weird error causing my app to crash. I have created a custom SKSpriteNode called heroSpriteNode. Here is my initialization:
(id) init
{
if (self = [super init])
{
[self loadAnimations];
SKTexture *temp = self.gorillaStandingFrames[0];
self = [heroSpriteNode spriteNodeWithTexture:temp];
[self setUpHeroDetails];
}
return self;
}
The error is occurring when attempting to run an action on "self" the custom SkSpriteNode, like this:
-(void)jump
{
NSLog(#"Jump Tap!");
if(self.isOnTheGround)
{
self.physicsBody.velocity = CGVectorMake(0, 0);
self.physicsBody.angularVelocity = 0;
self.timedJump=NO;
[self.physicsBody applyImpulse:CGVectorMake(0, 350)];
[self removeActionForKey:#"walkingInPlaceBear"];
[self jumpingGorilla2]; // HERE IS THE ERROR
self.isOnTheGround=NO;
self.isRunning=NO;
self.isStanding=NO;
self.jumpFixTimer = [NSTimer scheduledTimerWithTimeInterval:0.4 target:self selector:#selector(flipTimedJumpBoolean) userInfo:nil repeats:NO];
}
}
I know this animation (and other animations I run) is the issue, because when I comment out that line the game runs fine.
Here is how I am loading the textures and creating an action:
-(void)loadJumpAnimation2
{
NSMutableArray *jumpFrames = [NSMutableArray array];
SKTextureAtlas *gorillaAnimatedAtlas = [SKTextureAtlas atlasNamed:#"mainCharJump"];
int numImages = gorillaAnimatedAtlas.textureNames.count;
for (int i=2; i <= numImages; i++) {
NSString *textureName = [NSString stringWithFormat:#"j%d", i];
SKTexture *temp = [gorillaAnimatedAtlas textureNamed:textureName];
[jumpFrames addObject:temp];
}
self.gorillaFlyingJump = jumpFrames;
}
-(void)jumpingGorilla2
{
[self runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:self.gorillaFlyingJump
timePerFrame:0.25f
resize:NO
restore:YES]] withKey:#"jump"];
return;
}
When it crashes there is no crash report in the logger, only a popup message in Xcode that is of type: 9 SIGKILL. It seems like this could be a memory issue related to how the animation frames are being stored? The animations ran fine on a standard SkSpriteNode, then after refactoring much of the code into a custom SkSpriteNode, I can no longer apply animations. Any ideas? Thank you.
UPDATE:
I've created another class to try to isolate the issue I'm having, I will post the code below with comments.
Here is the entire test class I created:
#import "heroTestNode.h"
#implementation heroTestNode
- (id) init
{
if (self = [super init])
{
[self loadAnimations];
SKTexture *temp = self.gorillaWalkingFrames[0];
self = [heroTestNode spriteNodeWithTexture:temp];
}
return self;
}
-(void)loadAnimations
{
NSMutableArray *walkFrames = [NSMutableArray array];
SKTextureAtlas *gorillaAnimatedAtlas = [SKTextureAtlas atlasNamed:#"mainCharRun2"];
int numImages = gorillaAnimatedAtlas.textureNames.count;
NSLog(#"Here are how many animation frames: %d", numImages);
for (int i=1; i <= numImages; i++) {
NSString *textureName = [NSString stringWithFormat:#"r%d", i];
SKTexture *temp = [gorillaAnimatedAtlas textureNamed:textureName];
[walkFrames addObject:temp];
}
self.gorillaWalkingFrames = walkFrames;
}
-(void)walkingGorilla // running this is causing the SIGKILL 9 crash
{
[self runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:self.gorillaWalkingFrames
timePerFrame:0.1
resize:NO
restore:YES]] withKey:#"test"];
return;
}
#end
Here is the interface:
#import <SpriteKit/SpriteKit.h>
#interface heroTestNode : SKSpriteNode
#property NSMutableArray *gorillaWalkingFrames;
-(void)walkingGorilla;
#end
And finally, here is how I am instantiating it:
heroTestNode *test = [[heroTestNode alloc] init];
test.position = CGPointMake((self.frame.size.width/2)+30,self.frame.size.height/3);
[self addChild:test];
[test walkingGorilla]; // this is causing the SIGKILL 9 crash
This causes the it to freeze "compressing" the view into the bottom left corner of my iPad and going unresponsive. I have no idea what would be causing this.

end game not working [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm using a tutorial to create a game where if the rocketship is hit by an asteroid, the ship will show an image using SKEmitterNode and will blink then play a sound. This part of the game is working, however the game will not end. How do I fix this error?
Here's my code where I believe the error is
- (void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (asteroidCategory | shipCategory)) {
SKNode *asteroid, *spaceship;
if (contact.bodyA.categoryBitMask == shipCategory) {
spaceship = contact.bodyA.node;
asteroid = contact.bodyB.node;
[self runAction:[SKAction playSoundFileNamed:#"explosion_large.caf" waitForCompletion:NO]];
SKAction *blink = [SKAction sequence:#[[SKAction fadeOutWithDuration:0.1],
[SKAction fadeInWithDuration:0.1]]];
SKAction *blinkForTime = [SKAction repeatAction:blink count:6];
[self.spaceship runAction:blinkForTime];
NSString *explosionPath = [[NSBundle mainBundle] pathForResource:#"RocketFlame" ofType:#"sks"];
SKEmitterNode *explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:explosionPath];
CGVector emitterVector = CGVectorMake(spaceship.frame.size.width * 2.0, 0);
explosion.particlePositionRange = emitterVector;
[self.spaceship addChild:explosion];
}
else {
spaceship = contact.bodyB.node;
asteroid = contact.bodyA.node;
[self runAction:[SKAction playSoundFileNamed:#"explosion_large.caf" waitForCompletion:NO]];
SKAction *blink = [SKAction sequence:#[[SKAction fadeOutWithDuration:0.1],
[SKAction fadeInWithDuration:0.1]]];
// SKAction *blinkForTime = [SKAction repeatAction:blink count:6];
[self.spaceship runAction:blink];
NSString *explosionPath = [[NSBundle mainBundle] pathForResource:#"RocketFlame" ofType:#"sks"];
SKEmitterNode *explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:explosionPath];
CGVector emitterVector = CGVectorMake(spaceship.frame.size.width * 2.0, 0);
explosion.particlePositionRange = emitterVector;
[self.spaceship addChild:explosion];
}
[self asteroid:(SKSpriteNode *) asteroid didCollideWithasteroid:(SKSpriteNode *) spaceship];
}
else if (collision == (projectileCategory| asteroidCategory)) {
SKNode *Projectile, *asteroid;
if (contact.bodyA.categoryBitMask == asteroidCategory) {
Projectile= contact.bodyA.node;
asteroid = contact.bodyB.node;
}
else {
Projectile = contact.bodyB.node;
asteroid = contact.bodyA.node;
}
[self projectile:(SKSpriteNode *) Projectile didCollideWithasteroid:(SKSpriteNode *) asteroid];
self.asteroidsDestroyed++;
if (self.asteroidsDestroyed > 30) {
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:NO];
[self.view presentScene:gameOverScene transition: reveal];
}
}
}
The game is not ending because you are not calling the end-game transition in case of the collsion between the spaceship and asteroid as you do in the case where the collision is between the projectile and the asteroid.
- (void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (asteroidCategory | shipCategory)) {
SKNode *asteroid, *spaceship;
if (contact.bodyA.categoryBitMask == shipCategory) {
spaceship = contact.bodyA.node;
asteroid = contact.bodyB.node;
}
else {
spaceship = contact.bodyB.node;
asteroid = contact.bodyA.node;
}
[self runAction:[SKAction playSoundFileNamed:#"explosion_large.caf" waitForCompletion:NO]];
SKAction *blink = [SKAction sequence:#[[SKAction fadeOutWithDuration:0.1],
[SKAction fadeInWithDuration:0.1]]];
SKAction *blinkForTime = [SKAction repeatAction:blink count:6];
[self.spaceship runAction:blinkForTime];
NSString *explosionPath = [[NSBundle mainBundle] pathForResource:#"RocketFlame" ofType:#"sks"];
SKEmitterNode *explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:explosionPath];
CGVector emitterVector = CGVectorMake(spaceship.frame.size.width * 2.0, 0);
explosion.particlePositionRange = emitterVector;
[self.spaceship addChild:explosion];
[self asteroid:(SKSpriteNode *) asteroid didCollideWithasteroid:(SKSpriteNode *) spaceship]; //What does this do?
[self runAction:[SKAction sequence:#[[SKAction waitForDuration:3.0], [SKAction runBlock:^{
[self gameEndedWithSuccess:NO];
}]]]];
}
else if (collision == (projectileCategory| asteroidCategory)) {
SKNode *Projectile, *asteroid;
if (contact.bodyA.categoryBitMask == asteroidCategory) {
Projectile= contact.bodyA.node;
asteroid = contact.bodyB.node;
}
else {
Projectile = contact.bodyB.node;
asteroid = contact.bodyA.node;
}
[self projectile:(SKSpriteNode *) Projectile didCollideWithasteroid:(SKSpriteNode *) asteroid];
self.asteroidsDestroyed++;
if (self.asteroidsDestroyed > 30) {
[self gameEndedWithSuccess:YES];
}
}
}
-(void) gameEndedWithSuccess:(BOOL)success
{
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:success];
[self.view presentScene:gameOverScene transition: reveal];
}

Game crashes sporadically around checking for sprites

My game keeps crashing around this particular block of code.
The error message is Thread1: EXC_Bad_ACCESS(code =1) and the highlighted code is the following:
-(void)updateForArrays:(ccTime)delta
{
for (CCSprite *child in [self children]){
if (child.tag==2) {
if (CGRectIntersectsRect(child.boundingBox, _ship.boundingBox)) {
[self removeChild:child cleanup:YES];
_score += 1;
[_scoreLabel setString:[NSString stringWithFormat:#"Score : %d",_score]];
}
}if (child.tag ==3){
if (CGRectIntersectsRect(child.boundingBox, _ship.boundingBox)) {
CCScene *gameOverScene = [GameOverLayer gameOverScene];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}
}
}
}
You shouldn't modify ([self removeChild:child cleanup:YES]) collections (the [self children] array) while iterating. One way to go around this is to add objects for removal in a separate array and remove them after you're done checking for collisions.
Edit:
NSMutableArray *cleanupArray = [NSMutableArray array];
for (CCSprite *child in [self children]) {
// ...
[cleanupArray addObject:child]; // instead of [self removeChild:child cleanup:YES];
// ...
}
// actual removal of children
for (CCSprite *child in cleanupArray) {
[self removeChild:child cleanup:YES];
}
[cleanupArray removeAllObjects];
If all the children of self is of type CCSprite then your code will work. If not then you will face a crash. Because there is a chance that you might be forcefully typecasting a child which is of no CCSprite class. See if this code helps
-(void)updateForArrays:(ccTime)delta
{
for (id item in [self children]){
if((item isKindOfClass:(CCSprite class)])
{
CCSprite *child = (CCSprite *)item;
if (child.tag==2) {
if (CGRectIntersectsRect(child.boundingBox, _ship.boundingBox)) {
[self removeChild:child cleanup:YES];
_score += 1;
[_scoreLabel setString:[NSString stringWithFormat:#"Score : %d",_score]];
}
}if (child.tag ==3){
if (CGRectIntersectsRect(child.boundingBox, _ship.boundingBox)) {
CCScene *gameOverScene = [GameOverLayer gameOverScene];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}
}
}
}
}

Resources