How to have my character "ride" along with moving platforms? - ios

Im experiencing a problem with my Hero Character, when he lands on a moving platform, rather then moving along with it, he literally stands on the same X position until the platform moves offscreen. I searched many answers with no avail.
Here is my methods for my hero character and the moving platforms.
-(void)HeroAdd
{
_Hero = [SKSpriteNode spriteNodeWithImageNamed:#"Hero-1"];
_Hero.name = #"Daniel";
_Hero.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_Hero.size];
_Hero.physicsBody.categoryBitMask = fPlayerCategory;
_Hero.physicsBody.contactTestBitMask = fPlatformCategory | fEnemyCategory;
_Hero.physicsBody.usesPreciseCollisionDetection = YES;
_Hero.physicsBody.affectedByGravity = YES;
_Hero.physicsBody.dynamic = YES;
_Hero.physicsBody.friction = .9;
_Hero.physicsBody.restitution = 0;
_Hero.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
_Hero.position = CGPointMake(_Hero.position.x - 252, _Hero.position.y + 50);
if (self.size.width == 480) {
_Hero.position = CGPointMake(_Hero.position.x + 44, _Hero.position.y);
}
[self addChild:_Hero];
}
My moving platform code
-(void)createPlatform {
SKTexture *objectTexture;
switch (arc4random_uniform(2)) {
case (0):
objectTexture = [SKTexture textureWithImageNamed:#"shortPlatform"];
break;
case (1):
objectTexture = [SKTexture textureWithImageNamed:#"highPlatform"];
default:
break;
}
SKSpriteNode *variaPlatform = [SKSpriteNode spriteNodeWithTexture:objectTexture];
variaPlatform.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
variaPlatform.position = CGPointMake(variaPlatform.position.x + 500, variaPlatform.position.y - 140);
variaPlatform.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:variaPlatform.size];
variaPlatform.physicsBody.usesPreciseCollisionDetection = YES;
variaPlatform.physicsBody.categoryBitMask = fPlatformCategory;
variaPlatform.physicsBody.contactTestBitMask = fPlatformCategory |fPlayerCategory | fEnemyCategory;
variaPlatform.physicsBody.dynamic = NO;
variaPlatform.physicsBody.affectedByGravity = NO;
SKAction *moveLeft = [SKAction moveTo:CGPointMake(180, variaPlatform.position.y) duration:3];
SKAction *moveDown = [SKAction moveTo:CGPointMake(180, -700) duration:4];
SKAction *removeFromParent = [SKAction removeFromParent];
SKAction *AllThree = [SKAction sequence:#[moveLeft, moveDown, removeFromParent]];
[self addChild:variaPlatform];
[variaPlatform runAction:AllThree];
}
Any type of information would be truly appreciated.

I ran into the same issue when I added conveyer belts into a game. Sprite Kit does not drag objects no matter how much resistance is added. You currently have 2 options.
Option 1 - Add a ledge at each end of your platform. This is the easiest to implement but is less graceful and still allows the player to slide off if he lands on the ledge.
Option 2 -
Step 1: Add code which makes the player move in sync with the horizontal moving platform. You can either use something like self.physicsBody.velocity = CGVectorMake(-50, self.physicsBody.velocity.dy); or use self.position = CGPointMake(self.position.x+10, self.position.y);. You will have to play around with the x values to sync them to the platform's speed.
Step 2: Activate the above code whenever the player makes contact with the platform and deactivate when contact is lost.
Step 3: In case the platform switches directions, set up left and right limits which notify you via contact when the platform switches direction. Depending on the platform's direction you apply either +x or -x movement values to your player.
I know this option sounds complicated but it is not. You just need to go step by step.
* EDIT to provide sample code *
This is the logic I have behind the horizontal moving platforms:
PLATFORMS
If you have more than 1 horizontal moving platform, you will need to store them in an array in the GameScene (my Levels). I have created my own class for them but you do not have to do this.
You will have to set left and right limits (invisible SKNodes with contacts) to set a BOOL property for the platform which tells the player class which way to push as each platform will probably not be the same length. This is why you need to keep a reference to each platform (hence the array).
PLAYER
When the player jumps on the platform, set a Player class property BOOL to TRUE which activates the constant left or right push depending on which way the platform is currently moving. On the flip side, losing the contact cancels the push.
// This code in my "Levels class" which is the default GameScene class.
- (void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (CategoryPlatformHorizontal | CategoryPlayer))
{
[_player setPlatformHorizontalContact:true];
for(Platform *platformObject in platformArray)
{
if(([platformObject.name isEqualToString:contact.bodyB.node.name]) || ([platformObject.name isEqualToString:contact.bodyA.node.name]))
{
_player.currentPlatform = platformObject;
}
}
}
}
- (void)didEndContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (CategoryPlatformHorizontal | CategoryPlayer))
{
[_player setPlatformHorizontalContact:false];
}
}
// This code is in Player.h
#property (strong) Platform *currentPlatform;
#property BOOL platformHorizontalContact;
// This code is in Player.m
- (void)update:(NSTimeInterval)currentTime
{
if(self.platformHorizontalContact == true)
{
if(self.currentPlatform.movingLeft == true)
{
self.physicsBody.velocity = CGVectorMake(-75, self.physicsBody.velocity.dy); // set your own value depending on your platform speed
} else {
self.physicsBody.velocity = CGVectorMake(75, self.physicsBody.velocity.dy); // set your own value depending on your platform speed
}
}
}

Related

Change Physical body frame in spritekit

I develop an iOS game using SpriteKit (such a helpful framework to quickly make a game). I add texture and configure a physical body for a main character as image
The green rectangle is the frame of the physical body. I'm using the following code to create it
#interface MainCharacter : SKSpriteNode
#end
#implementation MainCharacter
+ (instancetype)mainCharacterAtPosition:(CGPoint)pos {
MainCharacter* mainChar = [[MainCharacter alloc] initWithTexture:[SKTexture textureWithImageNamed:#"stand_up"]];
mainChar.position = pos;
mainChar.xScale = 0.5f;
mainChar.yScale = 0.5f;
return mainChar;
}
- (instancetype)initWithTexture:(SKTexture *)texture {
if (self = [super initWithTexture:texture]) {
self.name = kCharacterName;
self.anchorPoint = CGPointMake(0.5f, 0.0f);
[self standup];
CGSize spriteSize = self.size;
CGPoint center = CGPointMake(spriteSize.width*(self.anchorPoint.x-0.5f), spriteSize.height*(0.5f-self.anchorPoint.y));
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spriteSize center:center];
self.physicsBody.dynamic = NO;
self.physicsBody.categoryBitMask = kCharacterCategory;
self.physicsBody.contactTestBitMask = 0x0;
self.physicsBody.collisionBitMask = 0x0;
}
return self;
}
- (void)standup {
SKAction* standupAction = [SKAction setTexture:self.standupTexture resize:YES];
[self runAction:standupAction];
}
- (void)standdown {
SKAction* standownAction = [SKAction setTexture:self.standdownTexture resize:YES];
[self runAction:standownAction completion:^{
}];
[self performSelector:#selector(standup) withObject:nil afterDelay:1.0f];
}
MainCharacter is a class that inherits from SKSPriteNode, just an convienient class to manage a main character. Stand Up is a first state of the character. I have another state, temporarily called stand down (demonstrate as following image)
I add a swipe down gesture to make character stand down.
The green rectangle also the physical body but it's too large for the character. I want to make a physical body frame as the red rectangle.
Can anyone help me how to make the physical body smaller when my character stand down and enlarge the physical body after it stands up
You can destroy the current physics body self.physicsBody = nil; and then simply create a new one with the new size requirements.
I solve this problem by using 2 nodes for 2 states (as a suggestion): stand up state and stand down state. I named it
standupNode and standdownNode
First, add the standupNode to the game scene. If swipe donw gesture recognize, I remove the standupNode from game scene and add the standdownNode instead. On contrary, removing the standdownNode from the game scene then add the standupNode if character stands up

How can one make only one side of a physics body active in SpriteKit?

I have a hero, ground and a table. I want to make a table-top active for collision and contact with hero. But hero should be able not to jump on the top and just run "through" and jump on it, if player wants it, wherever he wants. For better example of what i'm trying to achieve - think about Mario. When you are running on ground, some sky platforms appearing. You could jump on it in the middle of a platform and stay there. So I need physics body to not stop hero when he is contacting it from the bottom, but hold him if he is on top of it.
By now i'm using body with texture for table:
self.table.physicsBody = SKPhysicsBody(texture:table.texture, size:self.table.size)
self.table.physicsBody?.dynamic = false
self.table.physicsBody?.categoryBitMask = ColliderType.Table.rawValue
self.table.physicsBody?.contactTestBitMask = ColliderType.Hero.rawValue
self.table.physicsBody?.collisionBitMask = ColliderType.Hero.rawValue
It obviously, is not working. How can I implement such a thing?
The answer to this question is actually not too difficult but the implementation into a full fledged game will be much more difficult for you. This is not something for a novice programmer to start out with.
First the same code project (tap/click on screen to jump up):
#import "GameScene.h"
typedef NS_OPTIONS(uint32_t, Level1PhysicsCategory) {
CategoryPlayer = 1 << 0,
CategoryFloor0 = 1 << 1,
CategoryFloor1 = 1 << 2,
};
#implementation GameScene {
int playerFloorLevel;
SKSpriteNode *node0;
SKSpriteNode *node1;
SKSpriteNode *node2;
}
-(void)didMoveToView:(SKView *)view {
self.backgroundColor = [SKColor whiteColor];
node0 = [SKSpriteNode spriteNodeWithColor:[SKColor grayColor] size:CGSizeMake(400, 10)];
node0.position = CGPointMake(300, 200);
node0.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node0.size];
node0.physicsBody.dynamic = NO;
node0.physicsBody.categoryBitMask = CategoryFloor0;
node0.physicsBody.collisionBitMask = CategoryPlayer;
[self addChild:node0];
node1 = [SKSpriteNode spriteNodeWithColor:[SKColor grayColor] size:CGSizeMake(400, 10)];
node1.position = CGPointMake(300, 300);
node1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node1.size];
node1.physicsBody.dynamic = NO;
node1.physicsBody.categoryBitMask = CategoryFloor1;
node1.physicsBody.collisionBitMask = CategoryPlayer;
[self addChild:node1];
node2 = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(50, 50)];
node2.position = CGPointMake(300, 250);
node2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node2.size];
node2.physicsBody.categoryBitMask = CategoryPlayer;
node2.physicsBody.collisionBitMask = CategoryFloor0;
[self addChild:node2];
playerFloorLevel = 0;
}
-(void)update:(CFTimeInterval)currentTime {
if(((node2.position.y-25) > (node1.position.y+10)) && (playerFloorLevel == 0)) {
node2.physicsBody.collisionBitMask = CategoryFloor0 | CategoryFloor1;
playerFloorLevel = 1;
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
//SKNode *node = [self nodeAtPoint:touchLocation];
// change 75 value to 50 to see player jump half way up through floor 1
[node2.physicsBody applyImpulse:CGVectorMake(0, 75)];
}
}
The gist of the code is the player node (node2) has to keep checking its y position (update method) in relation to the other floors. In the example, the player jumps up through floor1. Once the player is higher than floor1, the player node's physics body modifies its collision bit mask to include floor1.
Sounds easy enough. However, in a real game you will have a large number of floors and all floors might not be evenly spaced y distances. You have to keep all that in mind when coding.
I am not sure how your platforms looks like (edge based or volume based bodies) but you can consider some of these:
1. Checking positions
Check if the hero's position.y is beneath/above the platform and ignore/handle the collision.
2. Checking velocity
Or to check if player node if falling, which is indicated by a negative velocity.dy value.
I can't say if any of these can fully help you with your game or is it possible with your setup, but you can get some basic idea on where to start.
Enabling/disabling collisions can be done by changing player's and platform's collision bitmasks. If possible try to avoid tracking states like isInTheAir, isOnPlatform, isFaling, isJumping and similar because it can become messy as number of states grows. For example, instead of adding custom boolean variable called "isFalling" and constantly maintaining its state, you can check if velocity.dy is negative to see if player is falling.
I tried changing the platforms collision bitmask but wasn't working fine. I found a different solution.
Inside the update() function you can check the following
if player.physicsBody?.velocity.dy <= 0 {
player.physicsBody?.collisionBitMask = PhysicsCategory.Platform
} else {
player.physicsBody?.collisionBitMask = PhysicsCategory.None
}
In this way, every time the player is going up, it can pass through rocks, and every time it is falling, it can stand.
Using swift you can create a sprite node subclass like this:
class TableNode: SKSpriteNode {
var isBodyActivated: Bool = false {
didSet {
physicsBody = isBodyActivated ? activatedBody : nil
}
}
private var activatedBody: SKPhysicsBody?
init(texture: SKTexture) {
super.init(texture: texture, color: SKColor.clearColor(), size: texture.size())
// physics body setup. Assuming anchorPoint = (0.5, 0.5)
let bodyInitialPoint = CGPoint(x: -size.width/2, y: +size.height/2)
let bodyEndPoint = CGPoint(x: +size.width/2, y: +size.height/2)
activatedBody = SKPhysicsBody(edgeFromPoint: bodyInitialPoint, toPoint: bodyEndPoint)
activatedBody!.categoryBitMask = ColliderType.Table.rawValue
activatedBody!.collisionBitMask = ColliderType.Hero.rawValue
physicsBody = isBodyActivated ? activatedBody : nil
name = "tableNode"
}
}
Then, update all tableNodes in the gameScene:
override func didSimulatePhysics() {
self.enumerateChildNodesWithName("tableNode") {
node, stop in
if let tableNode = node as? TableNode {
// Assuming anchorPoint = (0.5, 0.5) for table and hero
let tableY = tableNode.position.y + tableNode.size.height/2
let heroY = hero.position.y - hero.size.height/2
tableNode.isBodyActivated = heroY > tableY
}
}
}

SKSpriteNode ignoring physics deactivation

I have a node which is placed under my screen and works as a platform. As soon as another node touches the node a boolean is set to NO.
When I define my node with the SKPhysicsBody properties for the collision the node ignores the affectedByGravity property.
My code:
+ (void)addNewNodeTo:(SKNode *)parentNode
{
//Correct image size
SKSpriteNode *desertBottom = [SKSpriteNode node];
desertBottom = [[SKSpriteNode alloc] initWithImageNamed:#"Giraffe.png"];
desertBottom.position = CGPointMake(0, -200);
desertBottom.zPosition = 2;
desertBottom.physicsBody.collisionBitMask = lionType;
desertBottom.physicsBody.categoryBitMask = terrainType;
desertBottom.physicsBody.contactTestBitMask = lionType;
desertBottom.zPosition = 2;
desertBottom.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(desertBottom.size.width, desertBottom.size.height)];
desertBottom.physicsBody.dynamic = YES;
desertBottom.physicsBody.affectedByGravity = NO;
[parentNode addChild:desertBottom];
}
My collision methods:
- (void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (lionType | terrainType)) {
self.lionNode.lionIsJumping = NO;
NSLog(#"%i", self.lionNode.lionIsJumping);
}
}
- (void)didEndContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (lionType | terrainType)) {
self.lionNode.lionIsJumping = YES;
NSLog(#"%i", self.lionNode.lionIsJumping);
}
}
A physics body has a velocity. Gravity changes velocity over time. If the body is already traveling at a certain velocity due to gravity, and you disable gravity, it will continue to move according to its current velocity but no longer gain additional speed from gravity.
My guess is that you expect the body to stop when disabling gravity. If that's what you want you can do this manually by setting the y component of velocity to zero:
SKPhysicsBody* body = desertBottom.physicsBody;
body.velocity = CGVectorMake(body.velocity.x, 0.0);
If the platform should not move after a collision you have to set the dynamic property to false.
Maybe you have an issue with you bit masks?
Have you tried to log outside your if statement of the didBeginContact method?
I have created a new class with subclass of SKSpriteNode for the bottom and added it to my GameScene.

sprite kit removing specific nodes

I am trying to create a game where a character runs forever to the right (the game is landscape). On the ground there are spikes that the character can jump over. Currently, I am creating a new (and somewhat random) set of spikes in almost a checkpoint-like style where once the character reaches a certain distance, the next set of randomly organized spikes are created and the checkpoint distance gets pushed back and so on. Along with the spikes, I have a separate but very similar checkpoint-like system that is used to create the tiles that make up the ground.
This is my code for that portion, 'endlessX' and 'endlessGroundX' are the checkpoint value:
- (void) didSimulatePhysics {
if (player.position.x > endlessX) {
int random = player.position.x + self.frame.size.width;
[self createSpike:random];
endlessX += self.frame.size.width/2.2 + arc4random_uniform(30);
}
if (player.position.x + self.frame.size.width > endlessGroundX) {
[self createGround:endlessGroundX];
endlessGroundX += tile1.frame.size.width;
}
[self centerOnNode: player];
}
The parameter of the createSpike and createGround method is just the 'x' value for the SKSpriteNodes.
I am currently having it as the character itself is the one moving and the spikes and tiles are stationary. This is how I am creating the character:
-(void) createPlayer {
player = [SKSpriteNode spriteNodeWithImageNamed:#"base"];
player.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
player.name = #"player";
player.zPosition = 60;
player.xScale = 0.8;
player.yScale = 0.8;
player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:player.frame.size.height/2];
player.physicsBody.mass = 1;
player.physicsBody.linearDamping = 0.0;
player.physicsBody.angularDamping = 0.0;
player.physicsBody.friction = 0.0;
player.physicsBody.restitution = 0.0;
player.physicsBody.allowsRotation = NO;
player.physicsBody.dynamic = YES;
player.physicsBody.velocity = CGVectorMake(400, 0);
player.physicsBody.categoryBitMask = playerCategory;
player.physicsBody.collisionBitMask = wallCategory;
player.physicsBody.contactTestBitMask = wallCategory | spikeCategory;
[myWorld addChild:player];
}
With that, the character will never lose any of its kinetic energy to friction or any other force like that. Then, I am using the 'center on node' method that apple used in their adventure game so that the character will always remain in the same x-position on the screen:
- (void) centerOnNode: (SKSpriteNode *) node {
CGPoint cameraPositionInScene = [node.scene convertPoint:node.position fromNode:node.parent];
node.parent.position = CGPointMake(self.frame.size.width/5 + node.parent.position.x - cameraPositionInScene.x, node.parent.position.y);
}
I am calling this method in 'didSimulatePhysics.'
When I run this for some time, the programs gets slower and slower. I am guessing that that is due to the fact that I am never removing these nodes and they are always being added. However, to fix this problem, I tried doing something like this:
-(void)update:(CFTimeInterval)currentTime {
[self enumerateChildNodesWithName:#"*" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x + 50 < player.position.x) {
[node removeFromParent];
}
}];
}
(the +50 would be just to make sure that the node is off the screen before removing it)
However, when I did this, instead of removing the specific node that satisfies the 'if' statement, the program removes all of the sprite nodes. Is there a different method or something that I am missing to fix this? Or are there any other simple ways to remove the specific nodes?
Lacking quite a few details, like how you are animating the spikes for instance, makes it a bit hard to be too specific. Nevertheless, from what you are sharing I guess you might be looking for something a little like this:
SKAction *moveSpikeAction = [SKAction moveToX:-50 duration:5];
SKAction *removeSpikeAction = [SKAction removeFromParent];
SKAction *spikeSequence = [SKAction sequence:#[moveSpikeAction, removeSpikeAction]];
[yourSpikeSpriteNode runAction:spikeSequence];
The idea simply being that when the spike has animated to the off screen position you use the removeFromParent action to clear it.

My spritekit game crashes when I am reloading the game?

In my spritekit game I am creating Stone randomly through update method. Here is my code for random creation of stone
//Create random island
-(void)createRandomStone:(NSMutableArray *)imageArray
{
int getRandomNumberCoordinate = [self getRandomNumberBetween:(int)0 to:(int)768];
int getRandomStoneImage = [self getRandomNumberBetween:0 to:(int)([imageArray count] - 1)];
NSLog(#"Stone Image name = %d", getRandomStoneImage);
SKSpriteNode *createStone = [SKSpriteNode spriteNodeWithTexture:[imageArray objectAtIndex:getRandomStoneImage]];
if((getRandomNumberCoordinate + createStone.size.height / 2) > 768 )
createIsland.position = CGPointMake(_myScreenSize.width + createStone.size.width, 768 - createStone.size.height / 2);
else if((getRandomNumberCoordinate - createStone.size.height / 2) < 0 )
createStone.position = CGPointMake(_myScreenSize.width + createStone.size.width, 0 + createIsland.size.height / 2);
else
createStone.position = CGPointMake(_myScreenSize.width + createStone.size.width, getRandomNumberCoordinate);
createStone.name = #"Stone";
createStone.zPosition = 3;
[self addChild:createStone];
//Apply physics on the Stone
createStone.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize: CGSizeMake(createStone.size.width - createStone.size.width / 4, createStone.size.height - createStone.size.height / 6)];
createStone.physicsBody.categoryBitMask = CollisionTypeStone;
createStone.physicsBody.contactTestBitMask = CollisionTypeMan;
createStone.physicsBody.usesPreciseCollisionDetection = YES;
createStone.physicsBody.collisionBitMask = 0;
}
Stones are moving from the coordinate 1024 to 0 and if any stone cross the 0 coordinate the the stone will remove by using the code
-(void)updateStonePosition:(NSString *)whichDirection andMoveAmount:(float)speed
{
for (SKNode* node in self.children)
{
if([node.name isEqualToString:#"Stone"])
{
node.position = CGPointMake(node.position.x - speed, node.position.y);
if(node.position.x < -node.frame.size.width / 2)
[node removeFromParent];
}
}
}
Both of the above two methods are calling from the update method. And if the man hit by any 5 stone then the game will again reload. The reloading code is:
[self.view presentScene:[[MyScene alloc] initWithSize:self.size] transition:[SKTransition doorsCloseHorizontalWithDuration:0.5f]];
the game is reloaded but after few second an error message shows EXC_BAD_ACCESS(code=2, address = 0x0) on the line
SKSpriteNode *createStone = [SKSpriteNode spriteNodeWithTexture:[imageArray objectAtIndex:getRandomStoneImage]];
Please help. Thanks in advance.
To me, and with the code/information you are providing, it seems that when your scene is reloaded the array you are passing to "createRandomStone:" is nil.
Try checking for nil before calling the method:
if (imageArray != nil) {
// call createRandomStone
} else {
NSLog(#"imageArray is nil")
}
see if that helps identifying the root cause of the error.
I was also trying to find out some information how to reset/restart the SpriteKit game but I wasn't successful. I didn't spend enough time to find out why this is happening in my case, probably should try to do some cleaning before the transition happens like removing all actions and maybe all nodes...but I found a workaround for myself..
So it seems the crashing is happening when you call/do a transition to the same scene where you are at the moment so what I did is that:
1) I make a transition to my ResetScene which is just an empty scene
- with some parameters so I could identify a previous scene
2) In ResetScene in DidMoveToView I make transition back to my previous scene
This is how I fixed this problem but I don't think it is an ideal solution. Note, you can't really spot a difference that there are 2 transitions instead of one.
Let's see if somebody would join this thread with more wisdom :)
If your imageArray is not nil, and you've got more than one item, and it's still crashing, then you could be experiencing one of SpriteKit's bugs with the physics bodies (or skshapenodes elsewhere).
See this thread..: https://stackoverflow.com/a/25007468/557362
I've had this sort of problem myself, and I've got code in my common scene base class to clean up scenes, but I don't use physics bodies - which could be your problem (see link).
-(void)cleanUp
{
[self cleanUpChildrenAndRemove:self];
}
- (void)cleanUpChildrenAndRemove:(SKNode*)node {
for (SKNode *child in node.children) {
[self cleanUpChildrenAndRemove:child];
}
[node removeFromParent];
}
I also found that crashes were happening on transitions - and that if I delayed cleanup for a second after transition (and kept the scene alive just long enough), I had no crashes.
E.g.
SKTransition* doors = [SKTransition fadeWithDuration:0.75];
[[self view] presentScene:newscene transition:doors];
SKNode* dummynode = [SKNode node];
// wait for the transition to complete before we give up the reference to oldscene
[dummynode runAction:[SKAction waitForDuration:2.0] completion:^{
// failing to clean up nodes can result in crashes... nice.
[((MySceneBaseClass*)oldscene) cleanUp];
}];
[newscene addChild:dummynode];

Resources