I am currently coding a game for the iPhone and I sometimes get random crashes with the error in the title.
I've researched quite a bit and it probably has to do with leaking(?) memory issues. The people I asked told me, that it may has to do with adding a nil object. I set up exception breakpoints and NSZombies but neither of those recognize the crash, nor give me the exact code line where the crash occurs.
After some crash-testing I noticed that most of the time it happens when either the touchesBegan method or the didBeganContact method is active.
Here's the code of the touchesBegan method and the didBeginContact method:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (touchEnabled == YES){
firstTouch++;
if (firstTouch == 1){
SKAction* removeFade = [SKAction removeFromParent];
SKAction* startFade = [SKAction fadeAlphaTo:0 duration:0.5];
SKAction* fadeSeq = [SKAction sequence:#[startFade, removeFade]];
[startScreen runAction:fadeSeq completion:^{
startScreenRemoved = YES;
[self gameCountdown];
touchToShoot = YES;
}];
}
if (firstTouch == 2){
weaponActivated = YES;
}
if (gameOver == YES){
[self removeAllActions];
[self removeAllChildren];
[bgPlayer stop];
touchToShoot = NO;
SKScene* gameScene = [[GameScene alloc] initWithSize:self.size];
SKTransition *doors = [SKTransition doorsOpenVerticalWithDuration:1];
[self.view presentScene:gameScene transition:doors];
gameOver = NO;
}
}
if (touchToShoot == YES) {
[self weaponParticle];
touchToShoot = NO;
[self performSelector:#selector(enableTouchToShoot) withObject:nil afterDelay:0.3]; //-enableTouchToShoot{} = touchToShoot = YES;
}
//Pause Button
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"pauseButton"]) {
[self pauseMenu];
}
else if ([node.name isEqualToString:#"continue"]) {
self.paused = NO;
pauseBG.hidden = YES;
pauseText.hidden = YES;
backm.hidden = YES;
cont.hidden = YES;
//Damit unsichtbare Buttons nicht während dem Spiel gedrückt werden
backm.zPosition = -100;
cont.zPosition = -100;
}
else if ([node.name isEqualToString:#"backtomenu"]) {
self.paused = NO;
[self displayAlert];
}
}
-(void)didBeginContact:(SKPhysicsContact *)contact{
if (contact.bodyA.categoryBitMask == shipCategory) {
//Explosion Animation
SKAction* wait =[SKAction waitForDuration:0.5];
SKAction* fadeOut = [SKAction scaleTo:0.0 duration:1];
remove = [SKAction removeFromParent];
explodeSequenceGO =[SKAction sequence:#[fadeOut,wait,remove]];
ExplosionPath = [[NSBundle mainBundle] pathForResource:#"Explosion" ofType:#"sks"];
Explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:ExplosionPath];
Explosion.position = CGPointMake(contact.bodyA.node.position.x, contact.bodyA.node.position.y);
[self addChild:Explosion];
[Explosion runAction:explodeSequenceGO];
[contact.bodyA.node removeFromParent];
[contact.bodyB.node removeFromParent];
[shipSmoke removeFromParent];
[player1 removeFromParent];
touchEnabled = NO;
[self gameOver];
}
if (contact.bodyA.categoryBitMask == enemyCategory || contact.bodyB.categoryBitMask == enemyCategory) {
//Explosion Animation
SKAction* wait =[SKAction waitForDuration:0.5];
SKAction* fadeOut = [SKAction scaleTo:0.0 duration:1];
remove = [SKAction removeFromParent];
explodeSequenceGO =[SKAction sequence:#[fadeOut,wait,remove]];
ExplosionPath = [[NSBundle mainBundle] pathForResource:#"Explosion" ofType:#"sks"];
Explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:ExplosionPath];
Explosion.position = CGPointMake(contact.bodyA.node.position.x, contact.bodyA.node.position.y);
[Explosion runAction:explodeSequenceGO];
[self addChild:Explosion];
[contact.bodyA.node removeFromParent];
[contact.bodyB.node removeFromParent];
hitCount++;
[self scoreChange:100];
}
if (hitCount>39) {
[self eLevel2];
}
}
Does anyone see a fault? I greatly appreciate any tip, since I am searching for this bug for weeks....
EDIT: The crash just points to the "main" function, which doesn't help at all
And in each of the Thread "actions" it just points to Assembly(?) code:
And like I said, I tried to analyze the crashes by various debug tools (NSZombies, MemoryTool, Exceptional Breakouts, etc.) but none of them give me useful infos. When the app crashes, the debugging tools just stop recording, but they don't show me any faults or crash results.
I know that this is a few months old, but I too was experiencing the EXC_BAD_ACCESS error when transitioning from one scene to another. After reading multiple posts, and commenting out almost every line of code in my game, what fixed it for me was the removing all of the objects from the existing scene before returning from the method and bang error was gone.
- (void)returnToMenuScene {
SKTransition *reveal = [SKTransition revealWithDirection:SKTransitionDirectionDown duration:1.0];
MenuScene *menuScene = [MenuScene sceneWithSize:self.scene.size];
[self.scene.view presentScene:menuScene transition:reveal];
[self.gameScene removeAllChildren];
return;
}
- (GameScene *)gameScene {
return (GameScene *)self.scene;
}
I hope this helps someone else in the future
One thing I found early on is that transitioning between scenes caused crashes if the original scene was killed too early by SpriteKit. - Crazy talk I know.
To help this I created a base class scene which included a cleanup op.
-(void)cleanUp
{
[self cleanUpChildrenAndRemove:self];
}
- (void)cleanUpChildrenAndRemove:(SKNode*)node {
for (SKNode* child in node.children) {
[self cleanUpChildrenAndRemove:child];
}
[node removeFromParent];
}
Then when i transition scenes, I let the old scene live for a while before destroying it.
Notice the use of an action on the old scene to clean itself up later on.
Call me paranoid, delusional, but it fixed my crash. I also needed to call cleanup when I received Terminate events on the App.
if( oldscene!=nil )
{
SKTransition* doors = [SKTransition doorsOpenVerticalWithDuration:1.5];
doors.pausesIncomingScene = NO;
[[self view] presentScene:newscene transition:doors];
SKNode* dummynode = [SKNode node];
// wait for the transition to complete before we give up the referene to oldscene
[dummynode runAction:[SKAction waitForDuration:2.0] completion:^{
if ([oldscene isKindOfClass:[MyBaseScene class]])
{
// failing to clean up nodes can result in crashes... nice.
[((MyBaseScene*)oldscene) cleanUp];
}}];
[oldscene addChild:dummynode];
}
else
{
[[self view] presentScene:newscene];
}
Related
My sprite kit game has been having a memory problem and i have tried everything i know of to fix it but still nothing. A little background on the issue; the first time you play the game, memory is fine, it stays completely stable. But when you die and hit play again, that's when the memory starts creeping up. It seems that the more you play again, the faster the memory creeps up. You can be on any scene and the memory being used will still climb. Here's a picture showing instruments, http://postimg.org/image/5olu8h7tp/.
Here's the code i have:
creates the object:
-(void)createObstacle0 {
int yMin = (CGRectGetMidY(self.frame)+190);
int yMax = (CGRectGetMidY(self.frame)+270);
CGPoint startPoint = CGPointMake(-20, yMin + arc4random_uniform(yMax - yMin));
SKSpriteNode *obstacle = [SKSpriteNode spriteNodeWithImagedNamed:#"obstacle"];
obstacle.position = CGPointMake(startPoint.x, startPoint.y);
obstacle.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:21];
obstacle.physicsBody.categoryBitMask = enemyCategory;
obstacle.physicsBody.contactTestBitMask = playerCategory;
obstacle.physicsBody.dynamic = NO;
obstacle.name = #"obstacle";
[self addChild:obstacle];
SKAction *move = [SKAction moveTo:CGPointMake(340, startPoint.y) duration:minTime +arc4random_uniform(maxTime - minTime)];
SKAction* removeObstacle = [SKAction removeFromParent];
[obstacle runAction:[SKAction sequence:#[move, removeObstacle]] completion:^{
score++;
scorelabel.text = [NSString stringWithFormat:#"%lu",(unsigned long)score];
}];
float randomNum = arc4random_uniform(3.0) + 0.2;
[self performSelector:#selector(createObstacle0) withObject:nil afterDelay:randomNum];
}
Collision detector and scene transition:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (playerCategory | enemyCategory)) {
playerAlive = NO;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:score forKey:#"playerScore"];
[defaults synchronize];
int highScore = [[[NSUserDefaults standardUserDefaults] objectForKey:#"High Score"] integerValue];
if (highScore){
if (highScore < score){
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:score] forKey:#"High Score"];
}
}
else{
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:score] forKey:#"High Score"];
}
[[NSUserDefaults standardUserDefaults] synchronize];
SKTransition *reveal = [SKTransition fadeWithDuration:0.75];
MGLCreateGameOverScene *scene = [MGLCreateGameOverScene sceneWithSize:self.view.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[self.view presentScene:scene transition:reveal];
}
}
To remove remaining nodes:
-(void)update:(CFTimeInterval)currentTime {
if (playerAlive == NO) {
[self enumerateChildNodesWithName:#"obstacle" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];
}
}
As i said before, the first time you play, memory stays completely stable. It's only after the first time that memory starts to rise. It appears that the more you revisit the main game scene, the faster the memory climbs. I really need help solving this, my game has been down for two weeks now and i really want it back up. I appreciate all help.
Your problem is with the line:
[self performSelector:#selector(createObstacle0) withObject:nil afterDelay:randomNum];
This method actually retains self (by creating a timer) until after the selector is performed. What you need to do is add to the method didBeginContact: the following line before changing scenes:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(createObstacle0) object:nil];
This will destroy the timer that is retaining self and calling that method unendingly.
You can check that this works by adding a call to NSLog(#"%s", __PRETTY_FUNCTION__); in the dealloc method of your scene.
Trying to diagnose why my game is building up memory, causing it to eventually slow down to the point of unresponsiveness. Based on previously asked questions, i think i might know what's causing it, not sure though, so i will just post what i think it might be.
[self performSelector:#selector(createObstacle0) withObject:nil afterDelay:1.0];
[self performSelector:#selector(score0) withObject:nil afterDelay:1];
....
-(void)createObstacle0 {
int yMin = (CGRectGetMidY(self.frame)+190);
int yMax = (CGRectGetMidY(self.frame)+270);
CGPoint startPoint = CGPointMake(-20, yMin + arc4random_uniform(yMax - yMin));
SKSpriteNode *obstacle = [SKSpriteNode spriteNodeWithImageNamed:#"obstacle"];
obstacle.position = CGPointMake(startPoint.x, startPoint.y);
obstacle.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:21.5];
obstacle.physicsBody.categoryBitMask = enemyCategory;
obstacle.physicsBody.contactTestBitMask = playerCategory;
obstacle.physicsBody.dynamic = NO;
obstacle.name = #"obstacle0";
[self addChild:obstacle];
[obstacle runAction:[SKAction moveTo:CGPointMake(340, startPoint.y) duration:minTime + arc4random_uniform(maxTime - minTime)]];
float randomNum = arc4random_uniform(3.0) + 0.1;
[self performSelector:#selector(createObstacle0) withObject:nil afterDelay:randomNum];
}
-(void)score0 {
[self enumerateChildNodesWithName:#"obstacle0" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x > 330) {
score++;
scorelabel.text = [NSString stringWithFormat:#"%lu",(unsigned long)score];
[node removeFromParent];
}
}];
[self performSelector:#selector(score0) withObject:nil afterDelay:.1];
}
I read here that perferomSelector can cause some memory problems. For me, after playing my game just once, i can sit at the main menu and both cpu and memory will just continue to rise. No idea what's going on and appreciate all help. Is the code i have here to blame?
This is the orbivoid tutorial GameScene.m. So my problem is that, enemies spawning is just located somewhere in the lower left due to the CGPointMake. I want to make them already spawned outside of the screen at the start of the game with a definite number, randomly located(ex. 10 enemies already spawned outside of the screen at different locations). Can somebody please help me (some code, tips or something to implement for this)? I did not change anything in the GameScene.m so for the convenience of those who wants to help me. Thanks!
#implementation GameScene
{
BOOL _dead;
SKNode *_player;
NSMutableArray *_enemies;
SKLabelNode *_scoreLabel;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
self.physicsWorld.contactDelegate = self;
_enemies = [NSMutableArray new];
_player = [SKNode node];
SKShapeNode *circle = [SKShapeNode node];
circle.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-10, -10, 20, 20)].CGPath;`
circle.fillColor = [UIColor blueColor];
circle.glowWidth = 5;
SKEmitterNode *trail = [SKEmitterNode orb_emitterNamed:#"Trail"];
trail.targetNode = self;
trail.position = CGPointMake(CGRectGetMidX(circle.frame), CGRectGetMidY(circle.frame));
_player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
_player.physicsBody.mass = 100000;
_player.physicsBody.categoryBitMask = CollisionPlayer;
_player.physicsBody.contactTestBitMask = CollisionEnemy;
[_player addChild:trail];
_player.position = CGPointMake(size.width/2, size.height/2);
[self addChild:_player];
}
return self;
}
- (void)didMoveToView:(SKView *)view
{
[self performSelector:#selector(spawnEnemy) withObject:nil afterDelay:1.0];
}
-(void)spawnEnemy
{
[self runAction:[SKAction playSoundFileNamed:#"Spawn.wav" waitForCompletion:NO]];
SKNode *enemy = [SKNode node];
SKEmitterNode *trail = [SKEmitterNode orb_emitterNamed:#"Trail"];
trail.targetNode = self;
trail.particleScale /= 2;
trail.position = CGPointMake(10, 10);
trail.particleColorSequence = [[SKKeyframeSequence alloc] initWithKeyframeValues:#[
[SKColor redColor],
[SKColor colorWithHue:0.1 saturation:.5 brightness:1 alpha:1],
[SKColor redColor],
] times:#[#0, #0.02, #0.2]];
[enemy addChild:trail];
enemy.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:6];
enemy.physicsBody.categoryBitMask = CollisionEnemy;
enemy.physicsBody.allowsRotation = NO;
enemy.position = CGPointMake(50, 50);
[_enemies addObject:enemy];
[self addChild:enemy];
if(!_scoreLabel) {
_scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Courier-Bold"];
_scoreLabel.fontSize = 200;
_scoreLabel.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
_scoreLabel.fontColor = [SKColor colorWithHue:0 saturation:0 brightness:1 alpha:0.5];
[self addChild:_scoreLabel];
}
_scoreLabel.text = [NSString stringWithFormat:#"%02d", _enemies.count];
// Next spawn
[self runAction:[SKAction sequence:#[
[SKAction waitForDuration:5],
[SKAction performSelector:#selector(spawnEnemy) onTarget:self],
]]];
}
-(void)dieFrom: (SKNode*)killingEnemy
{
_dead = YES;
SKEmitterNode *explosion = [SKEmitterNode orb_emitterNamed:#"Explosion"];
explosion.position = _player.position;
[self addChild:explosion];
[explosion runAction:[SKAction sequence:#[
[SKAction playSoundFileNamed:#"Explosion.wav" waitForCompletion:NO],
[SKAction waitForDuration:0.4],
[SKAction runBlock:^{
// TODO: Revove these more nicely
[killingEnemy removeFromParent];
[_player removeFromParent];
}],
[SKAction waitForDuration:0.4],
[SKAction runBlock:^{
explosion.particleBirthRate = 0;
}],
[SKAction waitForDuration: 1.2],
[SKAction runBlock:^{
ORBMenuScene *menu = [[ORBMenuScene alloc] initWithSize:self.size];
[self.view presentScene:menu transition:[SKTransition doorsCloseHorizontalWithDuration:0.4]];
}],
]]];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
-(void)touchesMoved: (NSSet *) touches withEvent:(UIEvent *)event
{
[_player runAction:[SKAction moveTo:[[touches anyObject] locationInNode:self] duration:.01]];
}
-(void)update:(CFTimeInterval)currentTime
{
CGPoint playerPos = _player.position;
for(SKNode *enemyNode in _enemies)
{
CGPoint enemyPos = enemyNode.position;
/* Uniform speed: */
CGVector diff = TCVectorMinus(playerPos, enemyPos);
CGVector normalized = TCVectorUnit(diff);
CGVector force = TCVectorMultiply(normalized, 4);
[enemyNode.physicsBody applyForce:force];
}
_player.physicsBody.velocity = CGVectorMake (0, 0);
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
if(_dead)
return;
[self dieFrom:contact.bodyB.node];
contact.bodyB.node.physicsBody = nil;
}
#end
This is only an educated guess, since I can’t test my code.
I think in your didMoveToView method you can do this
for (int i = 0; i < 10; i++)
[self performSelector:#selector(spawnEnemy) withObject:nil afterDelay:1.0];
to spawn 10 enemies at once.
In your spawnEnemy method you set the position of the enemies similar to this:
CGFloat x = arc4random() % 100 + size.width/2;
CGFLoat y = arc4random() % (int)size.height;
if (arc4random() % 2 == 0)
x = size.width/2 + x;
else
x = size.width/2 - x;
enemy.position = CGPointMake(x, y);
This should spawn the enemies beyond the left and right side of the screen, but within 100 points of the edge of the screen.
Of course you need to tweak this to your needs.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I changed the custom circular shapes (SKShapeNode *circle) of the player and enemy to a .png file. Before changing the player and enemy to a .png file, there was this explosion effect after collision. However, after changing to .png file, the explosion effect does not happen anymore. Yes, the images bump each other which basically shows that there's collision. But the explosion effect after colliding with each other does not show anymore. How can I implement it again, I need some help please. Thank you very much!
#import "GameScene.h"
#import "CGVector+TC.h"
#import "ORBMenuScene.h"
enum {
CollisionPlayer = 1<<1,
CollisionEnemy = 1<<2,
};
#interface GameScene () <SKPhysicsContactDelegate>
#end
#implementation GameScene
{
BOOL _dead;
SKNode *_player;
NSMutableArray *_enemies;
SKLabelNode *_scoreLabel;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
self.physicsWorld.contactDelegate = self;
_enemies = [NSMutableArray new];
_player = [SKNode node];
_player = [SKSpriteNode spriteNodeWithImageNamed:#"Player.png"];
_player.position = CGPointMake(size.width/2, size.height/2);
_player.physicsBody.dynamic = YES;
_player.physicsBody.mass = 0.2;
_player.physicsBody.categoryBitMask = CollisionPlayer;
_player.physicsBody.contactTestBitMask = CollisionEnemy;
_player.physicsBody.collisionBitMask = CollisionEnemy;
_player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
_player.position = CGPointMake(size.width/2, size.height/2);
_player.physicsBody.allowsRotation = YES;
_player.physicsBody.usesPreciseCollisionDetection = YES;
_player.name = #"Player";
[_player setScale:1];
[self addChild:_player];
}
return self;
}
- (void)didMoveToView:(SKView *)view
{
[self performSelector:#selector(spawnEnemy) withObject:nil afterDelay:1.0];
}
-(void)spawnEnemy
{
[self runAction:[SKAction playSoundFileNamed:#"Spawn.wav" waitForCompletion:NO]];
SKNode *enemy = [SKNode node];
enemy = [SKSpriteNode spriteNodeWithImageNamed:#"Enemy.png"];
enemy.physicsBody.categoryBitMask = CollisionEnemy;
enemy.physicsBody.contactTestBitMask = CollisionPlayer;
enemy.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:6];
enemy.physicsBody.collisionBitMask = CollisionPlayer;
enemy.physicsBody.allowsRotation = YES;
enemy.physicsBody.dynamic = YES;
enemy.name = #"Enemy";
[enemy setScale:0.5];
CGFloat x = arc4random() % 70 + self.size.width/2;
CGFloat y = arc4random() % (int) self.size.height;
if (arc4random() % 2 == 0)
x = self.size.width/2 + x;
else
x = self.size.width/2 - x;
enemy.position = CGPointMake(x, y);
[_enemies addObject:enemy];
[self addChild:enemy];
if(!_scoreLabel) {
_scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Courier-Bold"];
_scoreLabel.fontSize = 200;
_scoreLabel.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
_scoreLabel.fontColor = [SKColor colorWithHue:0 saturation:0 brightness:1 alpha:0.5];
[self addChild:_scoreLabel];
}
_scoreLabel.text = [NSString stringWithFormat:#"%02d", _enemies.count];
if (_enemies.count == 18) {
[self runAction:[SKAction sequence:#[
[SKAction waitForDuration:99999],
[SKAction performSelector:#selector(spawnEnemy) onTarget:self],
]]];
}
else {
[self runAction:[SKAction sequence:#[
[SKAction waitForDuration:1],
[SKAction performSelector:#selector(spawnEnemy) onTarget:self],
]]];
}
}
-(void)dieFrom: (SKNode*)killingEnemy
{
_dead = YES;
SKEmitterNode *explosion = [SKEmitterNode orb_emitterNamed:#"Explosion"];
explosion.position = _player.position;
[self addChild:explosion];
[explosion runAction:[SKAction sequence:#[
[SKAction playSoundFileNamed:#"Explosion.wav" waitForCompletion:NO],
[SKAction waitForDuration:0.4],
[SKAction runBlock:^{
// TODO: Revove these more nicely
[killingEnemy removeFromParent];
[_player removeFromParent];
}],
[SKAction waitForDuration:0.4],
[SKAction runBlock:^{
explosion.particleBirthRate = 0;
}],
[SKAction waitForDuration: 1.2],
[SKAction runBlock:^{
ORBMenuScene *menu = [[ORBMenuScene alloc] initWithSize:self.size];
[self.view presentScene:menu transition:[SKTransition doorsCloseHorizontalWithDuration:0.4]];
}],
]]];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
-(void)touchesMoved: (NSSet *) touches withEvent:(UIEvent *)event
{
[_player runAction:[SKAction moveTo:[[touches anyObject] locationInNode:self] duration:.01]];
}
-(void)update:(CFTimeInterval)currentTime
{
CGPoint playerPos = _player.position;
for(SKNode *enemyNode in _enemies)
{
CGPoint enemyPos = enemyNode.position;
/* Uniform speed: */
CGVector diff = TCVectorMinus(playerPos, enemyPos);
CGVector normalized = TCVectorUnit(diff);
CGVector force = TCVectorMultiply(normalized, 4);
[enemyNode.physicsBody applyForce:force];
}
_player.physicsBody.velocity = CGVectorMake (0, 0);
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
if(_dead)
return;
[self dieFrom:contact.bodyB.node];
contact.bodyB.node.physicsBody = nil;
}
#end
It seems to me you did not initialize physicsBody before setting its properties.
After creating a player or enemy you should first initialize a body to use. Otherwise it will be nil indicating that the node does not participate in the physics simulation at all. And all property setting will be useless.
This should do for you, dont forget to move that line for the enemy as well!
_player = [SKSpriteNode spriteNodeWithImageNamed:#"Player.png"];
_player.position = CGPointMake(size.width/2, size.height/2);
_player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
_player.physicsBody.dynamic = YES;
_player.physicsBody.mass = 0.2;
I'm building an app that has a main menu layer and a background layer (for gameplay). I am loading the exact same CCParallax node code in both places, using the same images to create an infinite scrolling background.
On the gameplay background layer, the background displays and scrolls appropriately. However, on the main menu layer, the backgrounds are scaled weird (they don't take up the entire screen), and also don't scroll appropriately (probably because the scale is off somehow, so the offset isn't working).
Here is a screenshot of what the background looks like on the main menu:
And here it is on the background layer (green lines are part of the gameplay):
It almost looks like the menu layer is adding the non-retina display version of the files, but if I load the simulator into the iPhone (non-retina) mode, the meny background is still way off:
The code that loads the two is pretty much identical.
Here is the main menu loader code:
-(id)init {
self = [super init];
if (self != nil) {
CGSize winSize = [CCDirector sharedDirector].winSize;
_backgroundNode = [CCParallaxNode node];
[self addChild:_backgroundNode z:-1];
_backgroundGrid1 = [CCSprite spriteWithFile:#"grid.png"];
_backgroundCircuits1 = [CCSprite spriteWithFile:#"bg-circuits.png"];
_backgroundGrid1.anchorPoint = CGPointMake(0,0);
_backgroundCircuits1.anchorPoint = CGPointMake(0,0);
CGPoint gridSpeed = ccp(0.05, 0.05);
CGPoint circuitSpeed = ccp(0.1, 0.1);
[_backgroundNode addChild:_backgroundGrid1 z:1 parallaxRatio:gridSpeed positionOffset:ccp(0,-winSize.height)];
[_backgroundNode addChild:_backgroundCircuits1 z:0 parallaxRatio:circuitSpeed positionOffset:ccp(0,-winSize.height)];
[self scheduleUpdate];
}
return self;
}
- (void)update:(ccTime)dt {
CGPoint backgroundScrollVel = ccp(0, 1000);
_backgroundNode.position = ccpAdd(_backgroundNode.position, ccpMult(backgroundScrollVel, dt));
NSArray *backgroundGrids = [NSArray arrayWithObjects:_backgroundGrid1, nil];
for (CCSprite *b in backgroundGrids) {
if ([_backgroundNode convertToWorldSpace:b.position].y > 0) {
[_backgroundNode incrementOffset:ccp(0,-(b.contentSize.height/3)) forChild:b];
}
}
NSArray *backgroundCircuits = [NSArray arrayWithObjects:_backgroundCircuits1, nil];
for (CCSprite *bc in backgroundCircuits) {
if ([_backgroundNode convertToWorldSpace:bc.position].y > 0) {
[_backgroundNode incrementOffset:ccp(0,-(bc.contentSize.height/3)) forChild:bc];
}
}
}
And here is the code for the background layer:
- (id)init
{
self = [super init];
if (self != nil) {
CGSize winSize = [CCDirector sharedDirector].winSize;
_backgroundNode = [CCParallaxNode node];
[self addChild:_backgroundNode z:-1];
_backgroundGrid1 = [CCSprite spriteWithFile:#"grid.png"];
_backgroundCircuits1 = [CCSprite spriteWithFile:#"bg-circuits.png"];
_backgroundGrid1.anchorPoint = CGPointMake(0,0);
_backgroundCircuits1.anchorPoint = CGPointMake(0,0);
CGPoint gridSpeed = ccp(0.05, 0.05);
CGPoint circuitSpeed = ccp(0.1, 0.1);
[_backgroundNode addChild:_backgroundGrid1 z:1 parallaxRatio:gridSpeed positionOffset:ccp(0,-winSize.height)];
[_backgroundNode addChild:_backgroundCircuits1 z:0 parallaxRatio:circuitSpeed positionOffset:ccp(0,-winSize.height)];
[self scheduleUpdate];
return self;
}
- (void)update:(ccTime)dt {
CGPoint backgroundScrollVel = ccp(0, 1000);
_backgroundNode.position = ccpAdd(_backgroundNode.position, ccpMult(backgroundScrollVel, dt));
NSArray *backgroundGrids = [NSArray arrayWithObjects:_backgroundGrid1, nil];
for (CCSprite *b in backgroundGrids) {
if ([_backgroundNode convertToWorldSpace:b.position].y > 0) {
[_backgroundNode incrementOffset:ccp(0,-(b.contentSize.height/3)) forChild:b];
}
}
NSArray *backgroundCircuits = [NSArray arrayWithObjects:_backgroundCircuits1, nil];
for (CCSprite *bc in backgroundCircuits) {
if ([_backgroundNode convertToWorldSpace:bc.position].y > 0) {
[_backgroundNode incrementOffset:ccp(0,-(bc.contentSize.height/3)) forChild:bc];
}
}
}