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 have just started developing a game, new to Objective-c. Basically at the moment you can move the spaceship around and the spaceship fires continually and there are asteroids coming from the top of screen. But my app freezes after a minute every time why is this?
CODE
#import "AppDelegate.h"
#pragma mark - HelloWorldLayer
// HelloWorldLayer implementation
#implementation HelloWorldLayer
// Helper class method that creates a Scene with the HelloWorldLayer as the only child.
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
if( (self=[super init]) ) {
self.isTouchEnabled = YES;
moveLeft = NO;
moveRight = NO;
speed = 3;
fireSpeed = 8;
fireArray = [[NSMutableArray alloc] init];
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *bg = [[CCSprite alloc] initWithFile:#"space_bg.jpg"];
[bg setPosition:ccp(160, 240)];
CGSize imageSize = bg.contentSize;
bg.scaleX = winSize.width / imageSize.width;
bg.scaleY = winSize.height / imageSize.height;
bg.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:bg];
ship = [[CCSprite alloc] initWithFile:#"ship.png"];
[ship setPosition:ccp(100, 100)];
[self addChild:ship];
[self schedule:#selector(fireLoop:)];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(fireCreate)
userInfo:nil
repeats:YES];
int t = arc4random() % 5;
timer3 = [NSTimer scheduledTimerWithTimeInterval:t
target:self
selector:#selector(asteroidTimer)
userInfo:nil
repeats:YES];
}
return self;
}
NSTimer *timer3;
NSTimer *timer2;
NSTimer *tmpTimer;
-(void)asteroidTimer {
int t = arc4random() % 10;
timer2 = [NSTimer scheduledTimerWithTimeInterval:0
target:self
selector:#selector(asteroidCreate)
userInfo:nil
repeats:YES];
}
-(void)asteroidCreate {
[timer2 invalidate];
[tmpTimer invalidate];
CGSize winSize = [[CCDirector sharedDirector] winSize];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"asteroids.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"asteroids.png"];
NSMutableArray *asteroidAnimFrames = [NSMutableArray array];
for(int i=1; i <= 8; i++) {
[asteroidAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"asteroid%d.png", i]]];
}
CCAnimation *moveAsteroidAnim = [CCAnimation animationWithFrames:asteroidAnimFrames delay:0.1f];
CCSprite *asteroid = [CCSprite spriteWithSpriteFrameName:#"asteroid1.png"];
int x = arc4random() % 320;
int y = arc4random() % 480;
asteroid.position = ccp(x, 480);
CCAction *asteroidAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:moveAsteroidAnim restoreOriginalFrame:NO]];
int q = arc4random() % 320;
int r = arc4random() % 10;
CCAction *moveAction = [CCMoveTo actionWithDuration:r position:ccp(q, -50)];
[asteroid runAction:moveAction];
[asteroid runAction:asteroidAction];
[spriteSheet addChild:asteroid];
[self addChild:spriteSheet];
int t = arc4random() % 10;
tmpTimer = [NSTimer scheduledTimerWithTimeInterval:t
target:self
selector:#selector(asteroidTimer)
userInfo:nil
repeats:YES];
}
-(void)fireLoop:(ccTime)fl {
if(fireArray.count > 0) {
for(int i = 0; i < fireArray.count; i++){
CCSprite *tmpFire = [fireArray objectAtIndex:i];
if(tmpFire.position.y < 500){
[tmpFire setPosition:ccp([tmpFire position].x, [tmpFire position].y + fireSpeed)];
}else{
[fireArray removeObjectAtIndex:i];
}
}
} else
{
}
}
-(void)fireCreate {
int shootPositionX = [ship position].x;
int shootPositionY = ([ship position].y) + 35;
CCSprite *fire;
fire = [[CCSprite alloc] initWithFile:#"fire.png"];
[fire setPosition:ccp(shootPositionX, shootPositionY)];
[fireArray addObject:fire];
[self addChild:fire];
[fire release];
}
-(void)gameLoop:(ccTime)dt {
int shipPositionX = 41/2;
if([ship position].x > shipPositionX){
[ship setPosition:ccp([ship position].x - speed, [ship position].y)];
}
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
//if(point.x <= 160){
// moveRight = NO;
// moveLeft = YES;
//}else{
// moveRight =YES;
// moveLeft = NO;
//}
if(allowedToMove)
[ship setPosition:ccp(point.x, point.y + 76)];
}
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
int shipX = [ship position].x;
int shipY = [ship position].y;
if (CGRectContainsPoint(CGRectMake (shipX - 20.5, shipY - 96, 50, 50), point))
{
allowedToMove = true;
}
}
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
pointXLeft = point.x;
pointYLeft = point.y;
allowedToMove = false;
}
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
In general track down what seems to be causing it. What does the debugger say? Did you stop the execution or set a breakpoint somewhere that might be problematic? What about exception breakpoints? If those don't work take out your timers and see if you have any change. What about your fire methods?
Also your use of timers look weird and should be the first area you look. Side note, for the asteroids why are you not running the two actions you want to run on the new asteroid using CCSpawn?
In addition are you ever removing projectiles that you shoot from the screen or the asteroids that are no longer needed? If not you should.
But to answer your question the problem looks most likely a timer issue. Your use of timers are wrong. You have timer-3 call asteroid timer, which in turns creates timer-2 that calls asteroid create, which in turns invalidates timer-2 (that had an interval of 0 which makes it look a little unnecessary) and also creates a tmp-timer that calls asteroid create. Timer 3 is left to continuously do what it wants, which is all the steps above, at every x number of seconds. Even though tmp-timer seems to be trying to spawn more asteroid creations and take over the process, instead that looks both incorrect and odd. And you are doing this with a few global timers that can, seemingly have a valid timer but is overridden by a new allocation, just to be invalidated by another method randomly soon after even though what it was trying to invalidate technically was leaked and no longer has a pointer to it, thus invalidating the wrong instance to the wrong timer. Keep in mind your tmp-timer and timer-3 has random intervals. If tmp-timer has an interval of 3 seconds and timer-3 has a 9 second interval at some point something looks like it is bound to go wrong. It is hard to say by glancing at it just how, but it looks inevitable.
Or at least I think that is what is happening. Again the code makes very weird and potentially error prone use of timers.
Also timer-1 is going to keep creating sprites unchecked without those sprites ever going away. Something to think about for sure.
Edit (Solution):
If I understand what you are trying to do you want to create timers every variable amount of seconds that, at the end, creates a new asteroid. There are two things that stand out with this. You want something to call the creation of an asteroid, and something else to do the actual create:
- (void)countDownToCreateNextAsteroid
{
int minTime = 1;
int maxTime = 10;
int randomTime = (arc4random() % (maxTime - minTime)) + minTime;
id countdownDelay = [CCDelayTime actionWithDuration:randomTime];
id creationMethod = [CCCallFunc actionWithTarget:self selector:#selector(createAsteroid)];
id countdownToCreateSeq = [CCSequence actions:countdownDelay, creationMethod, nil];
[self stopAllActions];
[self runAction:countdownToCreateSeq];
}
- (void)createAsteroid
{
CGPoint someStartPosition = ccp(1024.0f, 512.0f);
CCSprite* asteroid = [CCSprite spriteWithFile:#"someImage.png"];
asteroid.position = someStartPosition;
// asterdoid dot yada yada = ....
[self addChild:asteroid];
float someDuration = 10.0f;
CGPoint someOffscreenPos = ccp(-1024.0f, 512.0f);
// Move off screen, then remove yourself...
id asteroidMove = [CCMoveTo actionWithDuration:someDuration
position:someOffscreenPos];
id asteroidRemove = [CCCallBlock actionWithBlock:^
{
[asteroid removeFromParentAndCleanup:YES];
}];
id asteroidSeq = [CCSequence actions:asteroidMove, asteroidRemove, nil];
[asteroid runAction:asteroidSeq];
[self countDownToCreateNextAsteroid];
}
Therefore in your onEnter you could have something to kick it all off and that's it:
- (void)onEnter
{
[super onEnter];
// yada yada
[self countDownToCreateNextAsteroid];
}
And the rest would take care of itself. This assumes this quick and simple approach is all you were after. The count down method sets up the count down, calls a method to create an asteroid at the end of that time, then exits. After the asteroid is created a new call to the count down method is invoked, which will setup another asteroid to be created at another random time. This is one quick and simple way to do what it looks you are were actually trying to accomplish with all those timers.
Related
I have a simple game with stones "raining" from top to bottom of the screen.
I am working with SpriteKit and I would like the stones to increase their speed by time like it would be level 1 level 2 level 3 and so on. Maximum speed should be easy to implement then.
I have a variable "speed" for the speed of the stones.
I think I could just use a timer and after 5s speed = 2, after 10s speed = 5, and so on, but I'm not sure how I can implement that.
EDIT:
I'm moving my stones with this:
-(void) moveStones{
SKAction *actionMove = [SKAction moveByX:0 y:-5 duration:0.25];
for(SKSpriteNode *stone in self.stones)
{
[stone runAction:actionMove];
}
}
And in my viewDidLoad I have the timer:
moveStones = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(moveStones) userInfo:nil repeats:YES];
If the timer does not respect the view, is there a way to move them without that timer?
If you are moving stones by physics, then after the stone is spawned you should apply an stronger impulse/force (based on current level) If you are moving them with actions, then change the speed of action, or lower the moveDuration. If you are moving stones by changing its position in update: method, then change the value which indicates how much points a sprite should move in every update call.
To implement time based actions like spawning stones or to decide when to change the speed of spawned nodes, you should use SKAction, or update: method and its passed parameter called currentTime. Using NSTimer for any of this in SpriteKit can lead to problems, because NSTimer don't respect view's , scene's or node's paused state, so its better to avoid using it.
Here is an example on how you can do what you want with SKAction. In this example you can see how to:
track the number of seconds passed
maintain the info about current level
spawn node with random delay
stop spawning nodes by using action key
and probably some more
GameScene.h
#import "GameScene.h"
#interface GameScene()
#property (nonatomic, assign) NSInteger counter;
#property (nonatomic, assign) NSInteger level;
#property (nonatomic, strong) SKLabelNode *debug;
#end
#implementation GameScene
// Here we set initial values of counter and level. Debug label is created here as well.
-(void)didMoveToView:(SKView *)view {
self.counter = 0;
self.level = 1;
self.backgroundColor = [SKColor grayColor];
self.debug = [SKLabelNode labelNodeWithFontNamed:#"ArialMT"];
self.debug.fontColor = [SKColor purpleColor];
self.debug.fontSize = 30.0f;
self.debug.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
self.debug.text = [NSString stringWithFormat:#"Counter : [ %ld ], Level [ %ld ]", (long)self.counter,(long)self.level];
[self addChild:self.debug];
}
//Method to start a timer. SKAction is used here to track a time passed and to maintain current level
-(void)startTimer{
__weak typeof (self) weakSelf = self; //make a weak reference to scene to avoid retain cycle
SKAction *block = [SKAction runBlock:^{
weakSelf.counter++;
//Maintaining level
if(weakSelf.counter < 5){
//level 1
weakSelf.level = 1;
}else if(weakSelf.counter >=5 && weakSelf.counter <10){
//level 2
weakSelf.level = 2;
}else{
//level 3
weakSelf.level = 3;
}
weakSelf.debug.text =
[NSString stringWithFormat:#"Counter : [ %ld ], Level [ %ld ]", (long)weakSelf.counter,(long)weakSelf.level];
}];
[self runAction:[SKAction repeatActionForever:[SKAction sequence:#[[SKAction waitForDuration:1], block]]] withKey:#"counting"];
}
//Method for stopping timer and reseting everything to default state.
-(void)stopTimer{
if([self actionForKey:#"counting"]){
[self removeActionForKey:#"counting"];
}
self.counter = 0.0f;
self.level = 1;
self.debug.text =
[NSString stringWithFormat:#"Counter : [ %ld ], Level [ %ld ]", (long)self.counter,(long)self.level];
}
//Get current speed based on time passed (based on counter variable)
-(CGFloat)getCurrentSpeed{
if(self.counter < 5){
//level 1
return 1.0f;
}else if(self.counter >=5 && self.counter <10){
//level 2
return 2.0f;
}else{
//level 3
return 3.0f;
}
}
//Method which stop generating stones, called in touchesBegan
-(void)stopGeneratingStones{
if([self actionForKey:#"spawning"]){
[self removeActionForKey:#"spawning"];
}
}
//You can use this useful method to generate random float between two numbers
- (CGFloat)randomFloatBetween:(CGFloat)smallNumber and:(CGFloat)bigNumber {
CGFloat diff = bigNumber - smallNumber;
return (((CGFloat) (arc4random() % ((unsigned)RAND_MAX + 1)) / RAND_MAX) * diff) + smallNumber;
}
//Method for generating stones, you run this method when you want to start spawning nodes (eg. didMoveToView or when some button is clicked)
-(void)generateStones{
SKAction *delay = [SKAction waitForDuration:2 withRange:0.5]; //randomizing delay time
__weak typeof (self) weakSelf = self; //make a weak reference to scene to avoid retain cycle
SKAction *block = [SKAction runBlock:^{
SKSpriteNode *stone = [weakSelf spawnStoneWithSpeed:[weakSelf getCurrentSpeed]];
stone.zPosition = 20;
[weakSelf addChild:stone];
}];
[self runAction:[SKAction repeatActionForever:[SKAction sequence:#[delay, block]]] withKey:#"spawning"];
}
//Returns stone with moving action added. Inside, you set standard things, like size, texture, physicsbody, name and position of a stone
-(SKSpriteNode*)spawnStoneWithSpeed:(CGFloat)stoneSpeed{
CGSize stoneSize = CGSizeMake(30,30); //you can randomize size here
CGPoint stonePosition = CGPointMake( [self randomFloatBetween:0.0f and:self.frame.size.width], CGRectGetMaxY(self.frame)); //you can randomize position here
SKSpriteNode *stone = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:stoneSize];
stone.name = #"stone"; //this helps if you want to enumerate all stones by name later on in your game
stone.position = stonePosition;
SKAction *move = [SKAction moveByX:0 y:-200 duration:3.25];
//one way to change speed
move.speed = stoneSpeed;
SKAction *moveAndRemove = [SKAction sequence:#[move, [SKAction removeFromParent]]];
[stone runAction:moveAndRemove withKey:#"moving"]; //access this key if you want to stop movement
return stone;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
//just a simple way to start and stop a game
if(![self actionForKey:#"counting"]){
[self startTimer];
[self generateStones];
}else{
[self stopTimer];
[self stopGeneratingStones];
}
}
#end
And here is the result:
Now its up to you how you will set the speed. You can play with that.
If you want to skip some parts, or you don't know where to look, the heart of everything is in method called generateStones That is how you can spawn stones with a time delay by using SKActions. The another important method is spawnStoneWithSpeed: where you can see how to manipulate with speed of an action.
Another way to affect on node's moving speed, as I said, is to change duration parameter of method moveByX: duration:
Hope this helps!
I need an image to change itself when it's touched.
At the moment the image that changes is the next image that spawns and not self.
#implementation MyScene2
{
Marsugo *marsugo;
SKAction *actionMoveDown;
SKAction *actionMoveEnded;
SKTexture *rescued;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
// Initializes Background
self.currentBackground = [Background generateNewBackground];
[self addChild:self.currentBackground];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
NSArray *nodes = [self nodesAtPoint:[touch locationInNode:self]];
for (SKNode *node in nodes) {
if ([node.name isEqualToString:playerObject]) {
rescued = [SKTexture textureWithImageNamed:#"rescued"];
marsugo.texture = rescued;
// this is changing the image of the next marsugo that spawns instead of self.
}
}
}
}
-(void)addMarsugos
{
// the marsugo is being initialized inside this method, that might be the issue i believe
// Create sprite
marsugo = [[Marsugo alloc]init];
marsugo.xScale = 0.3;
marsugo.yScale = 0.3;
marsugo.zPosition = 75;
// Bounds + Spawn Positions
int minX = marsugo.size.width;
int maxX = self.frame.size.width - marsugo.size.width;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
marsugo.position = CGPointMake(actualX, self.frame.size.height + 50);
[self addChild:marsugo];
// Spawn Timer
int minDuration = 1;
int maxDuration = 10;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Movement Actions
actionMoveDown = [SKAction moveTo:CGPointMake(actualX, CGRectGetMinY(self.frame)) duration:actualDuration];
actionMoveEnded = [SKAction removeFromParent];
[marsugo runAction:[SKAction sequence:#[actionMoveDown, actionMoveEnded]]];
NSLog(#"Marsugo X: %f - Speed: %i", marsugo.position.x, actualDuration);
}
#end
Like i said previously, i need the self sprite to change texture and not the "next spawning sprite".
Any help fixing this would be appreciated, thank you.
You don't want to use touchesBegan - you're better off using a tap gesture recognizer. Below I have _testView, which is an instance variable I create and add to the view in viewDidLoad. I then created a tap gesture recognizer that calls a function when the view is tapped, and that function changes the color of the view - but in your case you can call your function that changes the image:
- (void)viewDidLoad {
[super viewDidLoad];
// create the test view and add it as a subview
_testView = [[UIView alloc] initWithFrame:CGRectMake(20, 100, 100, 100)];
_testView.backgroundColor = [UIColor redColor];
[self.view addSubview:_testView];
// create the tap gesture recognizer and add it to the test view
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(changeColor)];
[_testView addGestureRecognizer:tapGestureRecognizer];
}
- (void)changeColor {
// here I'm changing the color, but you can do whatever you need once the tap is recognized
_testView.backgroundColor = [UIColor blueColor];
}
This results in this at first:
Then when I tap the view:
You can override -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event in your Marsugo class and change the texture there. That way, you won't have to check if the touch is inside your node because that method won't be called if it's not.
I am in the process of making a game and I need an object to move only when the buttons are pressed. I have a method that begins the movement, and so far I am ending the movement of an object by having destroying the body of the object. The problem that I have is that I can't move the object again since the program will now crash. I'm wondering if there is a way to recreate the body once its destroyed since my current method of checking if the body still exists isn't working.
Here is my code.
NSMutableArray *spaceObjectsArray;
#pragma mark - HelloWorldLayer
#interface HelloWorldLayer()
-(void) initPhysics;
-(void) addNewSpriteAtPosition:(CGPoint)p;
-(void) createMenu;
#end
#implementation HelloWorldLayer
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(void)gameLogic:(ccTime)delta
{
[self addSpaceObjects];
}
-(void) addSpaceObjects
{
_spaceObject = [CCSprite spriteWithFile:#"blueDot.jpg"];
//create spaceObject body
b2BodyDef spaceObjectbBodyDef;
spaceObjectbBodyDef.type=b2_dynamicBody;
spaceObjectbBodyDef.userData = _spaceObject;
//make the location of spaceObject
CGSize winSize = [CCDirector sharedDirector].winSize;
int minX= _spaceObject.contentSize.width/2;
int maxX = winSize.width - _spaceObject.contentSize.width/2;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
_spaceObject.position = ccp(actualX, winSize.height + _ship.contentSize.height);
spaceObjectbBodyDef.position.Set(actualX/PTM_RATIO, (winSize.height+_ship.contentSize.height)/PTM_RATIO);
_spaceObjectBody= _world->CreateBody(&spaceObjectbBodyDef);
//create spaceObject shape
b2PolygonShape spaceObjectShape;
spaceObjectShape.SetAsBox(_spaceObject.contentSize.width/PTM_RATIO/2, _spaceObject.contentSize.height/PTM_RATIO/2);
//create spaceObject fixture
b2FixtureDef spaceObjectShapeDef;
spaceObjectShapeDef.shape= &spaceObjectShape;
spaceObjectShapeDef.density = 2;
spaceObjectShapeDef.restitution =0;
spaceObjectShapeDef.friction=0;
_spaceObjectFixture = _spaceObjectBody->CreateFixture(&spaceObjectShapeDef);
[self addChild:_spaceObject];
_spaceObject.tag=1;
[spaceObjectsArray addObject:_spaceObject];
//aply force on the object
int randomValue = ((arc4random() % 5) *-1);
b2Vec2 force = b2Vec2(0,randomValue);
_spaceObjectBody ->ApplyLinearImpulse(force, _spaceObjectBody->GetPosition());
}
init method that contains the body creations and definitions
-(id) init
{
if( (self=[super init])) {
CGSize s = [CCDirector sharedDirector].winSize;
//create spaceShip sprite and add it to the layer
_ship = [CCSprite spriteWithFile:#"theShip.gif" ];
_ship.position = ccp(s.width/2, 1.25*_ship.contentSize.height);
[self addChild:_ship];
//create the world
b2Vec2 gravity = b2Vec2_zero;
_world = new b2World(gravity);
//create ship body
b2BodyDef shipBodyDef;
shipBodyDef.type = b2_dynamicBody;
shipBodyDef.position.Set((s.width/2)/PTM_RATIO, (1.25*_ship.contentSize.height)/PTM_RATIO);
shipBodyDef.userData = _ship;
if(_shipBody == NULL){
_shipBody =_world->CreateBody(&shipBodyDef);
}
//create ship shape
b2PolygonShape shipShape;
shipShape.SetAsBox(_ship.contentSize.width/PTM_RATIO/2, _ship.contentSize.height/PTM_RATIO/2);
//create Ship definition and add to body
b2FixtureDef ShipShapeDef;
ShipShapeDef.shape = &shipShape;
ShipShapeDef.density = 3;
ShipShapeDef.friction =0;
ShipShapeDef.restitution =0;
_shipFixture = _shipBody->CreateFixture(&ShipShapeDef);
//make the paddles
//bottom left one
_paddle1 = [CCSprite spriteWithFile:#"spritePaddle.jpeg"];
int bottomOfScreenX = 0 + _paddle1.contentSize.width/2;
int bottomOfScreenY = 0+_paddle1.contentSize.height/2;
_paddle1.position = ccp(bottomOfScreenX,bottomOfScreenY);
[self addChild:_paddle1];
//bottom right one
_paddle2 = [CCSprite spriteWithFile:#"spritePaddle.jpeg"];
int bottomRightOfScreenX = s.width - _paddle2.contentSize.width/2;
_paddle2.position = ccp(bottomRightOfScreenX, bottomOfScreenY);
[self addChild:_paddle2];
//continuously spawn spaceObjects
[self schedule:#selector(gameLogic:) interval:1];
// enable events
self.touchEnabled = YES;
// init physics
[self schedule:#selector(tick:)];
}
return self;
}
-(void)tick:(ccTime) delta
{//this method is to simulate physics and to test for the position of where objects should be if force has been applied to them
_world->Step(delta, 8, 8);
for (b2Body *b=_world->GetBodyList(); b; b=b->GetNext()){
if (b->GetUserData() != NULL){
CCSprite *shipData = (CCSprite *)b->GetUserData();
shipData.position = ccp(b->GetPosition().x *PTM_RATIO, b->GetPosition().y *PTM_RATIO);
}
}
}
The paddles to move the ship are touched logic
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//Set up a way for touches to be turned into locations onscreen
NSSet *allTouches = [event allTouches];
UITouch *touch = [allTouches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location ];
//check to see if Left Paddle is being pressed
if (CGRectContainsPoint([_paddle1 boundingBox], location)){
b2Vec2 force = b2Vec2(-5,0);
_shipBody->ApplyLinearImpulse(force, _shipBody ->GetPosition());
}
if (CGRectContainsPoint([_paddle2 boundingBox], location)){
b2Vec2 force = b2Vec2(5,0);
_shipBody->ApplyLinearImpulse(force, _shipBody->GetPosition());
}
}
The paddle box is no longer being touched logic
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
_world->DestroyBody(_shipBody);
}
-(void) dealloc
{
delete _world;
_world = NULL;
[super dealloc];
}
#end
Destroying the body to end the movement of it is not the best solution. You should only remove the body if you really don't want it to be part of the simulation any more.
There are several options for stopping the body's movement:
1 - Set its linear velocity to 0. This will bring it to an immediate stop. If something else is pushing on it (e.g. contact wit body), you will have to decide what to do.
body->SetLinearVelocity(b2Vec2(0,0)));
2 - Set its linear/angular damping to 0. This will dissipate the momentum it has so it will slowly stop. The factor you use should be greater than 0. The body will come to a halt more quickly with larger values and it will be resistant to movement from other bodies (if they bump it, it will slow down and stop again). Remember to turn the linear/angular damping back to 0 when you want the body to start moving.
body->SetLinearDamping(0.2);
body->SetAngularDamping(0.2);
3 - Give it a target position to seek to and set the position as the place you want it to be. This is basically a feedback control loop where you are applying a force to get it to move towards where you want the body to stay. It can be used to make a body follow paths, etc. The code below is part of a larger code base (you can see it here), but you should be able to get the general idea. This function applies thrust to the object so that it pushes towards a target.
void MovingEntity::ApplyThrust()
{
// Get the distance to the target.
b2Vec2 toTarget = GetTargetPos() - GetBody()->GetWorldCenter();
toTarget.Normalize();
b2Vec2 desiredVel = GetMaxSpeed()*toTarget;
b2Vec2 currentVel = GetBody()->GetLinearVelocity();
b2Vec2 thrust = GetMaxLinearAcceleration()*(desiredVel - currentVel);
GetBody()->ApplyForceToCenter(thrust);
}
My project creates a bomb, an explosion, then checks for collisions in the explosions and finally delete the bombs that didn't get hit in a collision. This is explained in more detail here. The following code does this.
-(void)placeBomb
{
NSLog(#"Bomb placed");
_circle = [[CCSprite alloc]initWithFile:#"Circle.png"];
CGPoint circle0position = ccp(_cat.position.x , _cat.position.y);
CGPoint c0TileCoordt = [self tileCoordForPosition:circle0position];
CGPoint c0TileCoord = [self positionForTileCoord:c0TileCoordt];
_circle.position = c0TileCoord;
[self addChild:_circle];
id fade = [CCScaleTo actionWithDuration:3.5 scale:0];
[_circle runAction:fade];
double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self explosionFromPoint:c0TileCoordt withSprite:_circle];
});
}
- (BOOL)isLocationBombable:(CGPoint)tileCoord;
{
if ([self isValidTileCoord:tileCoord] && ![self isWallAtTileCoord:tileCoord])
{
return YES;
}
else
{
return NO;
}
}
-(void)explosionFromPoint:(CGPoint)explosionPoint withSprite:(CCSprite*)sprite;
{
//int
explosionLenght += 1;
if (explosionLenght >= 7) //Just for testing purposes, don't have a way to increase it naturally.
{
explosionLenght = 1;
}
BOOL topB = YES;
BOOL leftB = YES;
BOOL bottomB = YES;
BOOL rightB = YES;
int bombX = (explosionPoint.x + 1);
int bombY = (explosionPoint.y + 1);
int bombNegX = (explosionPoint.x - 1);
int bombNegY = (explosionPoint.y - 1);
CGPoint top = ccp(explosionPoint.x, bombY);
CGPoint left = ccp(bombNegX, explosionPoint.y);
CGPoint bottom = ccp(explosionPoint.x, bombNegY);
CGPoint right = ccp(bombX, explosionPoint.y);
if (![self isLocationBombable:top])
{topB = NO;}
if (![self isLocationBombable:left])
{leftB = NO;}
if (![self isLocationBombable:bottom])
{bottomB = NO;}
if (![self isLocationBombable:right])
{rightB = NO;}
for (int i = 0; i <= explosionLenght; i++) {
int bombX = (explosionPoint.x + i);
int bombY = (explosionPoint.y + i);
int bombNegX = (explosionPoint.x - i);
int bombNegY = (explosionPoint.y - i);
CGPoint top = ccp(explosionPoint.x, bombY);
CGPoint left = ccp(bombNegX, explosionPoint.y);
CGPoint bottom = ccp(explosionPoint.x, bombNegY);
CGPoint right = ccp(bombX, explosionPoint.y);
CCSprite *circleTop = [[CCSprite alloc]initWithFile:#"Circle.png"];
CCSprite *circleLeft = [[CCSprite alloc]initWithFile:#"Circle.png"];
CCSprite *circleBottom = [[CCSprite alloc]initWithFile:#"Circle.png"];
CCSprite *circleRight = [[CCSprite alloc]initWithFile:#"Circle.png"];
int scaleTime = 5;
if ([self isLocationBombable:top] && topB == YES)
{
circleTop.position = [self positionForTileCoord:top];
[self addChild:circleTop];
id fadeTop = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleTop.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
[circleTop runAction:fadeTop];
}
if ([self isLocationBombable:left] && leftB == YES)
{
circleLeft.position = [self positionForTileCoord:left];
[self addChild:circleLeft];
id fadeLeft = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleLeft.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
[circleLeft runAction:fadeLeft];
}
if ([self isLocationBombable:bottom] && bottomB == YES)
{
circleBottom.position = [self positionForTileCoord:bottom];
[self addChild:circleBottom];
id fadeBottom = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleBottom.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
[circleBottom runAction:fadeBottom];
}
if ([self isLocationBombable:right] && rightB == YES)
{
circleRight.position = [self positionForTileCoord:right];
[self addChild:circleRight];
id fadeRight = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleRight.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
[circleRight runAction:fadeRight];
}
}
[currentBombs addObject:sprite];
int a = [currentBombs count];
NSLog(#"cBCount: %i",a);
NSLog(#"Explosion done, call checkdamage");
[self schedule:#selector(checkDamageForBomb)];
[self performSelector:#selector(removeSprite:) withObject:sprite afterDelay:5];
}
-(void)removeSprite:(CCSprite *)sprite{
int aa = [currentBombs count];
NSLog(#"removeSprite startcall cbc: %i",aa);
if([currentBombs containsObject:sprite])
{
NSLog(#"Found sprite in array, deleted!");
[currentBombs removeObject:sprite];
int a = [currentBombs count];
NSLog(#"containObject cbc: %i",a);
}
else {
NSLog(#"Didn't find the object in array, didn't delete!");
int a = [currentBombs count];
NSLog(#"elseCO cbc: %i",a);
}
if (currentBombs.count == 0)
{
[self stopCheckDamage];
}
}
-(void)stopCheckDamage{
NSLog(#"StopCheckDamage");
[self unschedule:#selector(checkDamageForBomb)];
}
-(void)checkDamageForBomb{
for (CCSprite* bomb in currentBombs)
{
CGPoint bombPos = [self tileCoordForPosition:bomb.position];
for (int i = 0; i <= explosionLenght; i++) {
CGPoint playerPos = [self tileCoordForPosition:_cat.position];
int bombX = (bombPos.x + i);
int bombY = (bombPos.y + i);
int bombNegX = (bombPos.x - i);
int bombNegY = (bombPos.y - i);
CGPoint centre = bombPos;
CGPoint top = ccp(centre.x, bombY);
CGPoint left = ccp(bombNegX, centre.y);
CGPoint bottom = ccp(centre.x, bombNegY);
CGPoint right = ccp(bombX, centre.y);
//pastebin.com/biuQBfnv
if (CGPointEqualToPoint(top, playerPos) || CGPointEqualToPoint(left, playerPos) || CGPointEqualToPoint(bottom, playerPos) || CGPointEqualToPoint(right, playerPos))
{
playerHits += 1;
NSLog(#"Player hit %i",playerHits);
[currentBombs removeObject:bomb];
break;
}
}
}
}
My problem is with the -(void)removeSprite:(CCSprite *)sprite{method. This is supposed to delete only the one it got called with, but instead it kills them all, as you can see in this log.
15:14:02.499 Tile[1549:c07] Bomb placed
15:14:03.816 Tile[1549:c07] Bomb placed
15:14:05.501 Tile[1549:c07] cBCount: 1
15:14:05.501 Tile[1549:c07] Explosion done, call checkdamage
15:14:06.818 Tile[1549:c07] cBCount: 2
15:14:06.819 Tile[1549:c07] Explosion done, call checkdamage
15:14:06.819 Tile[1549:c07] CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: 0.00 to 0.00
15:14:10.503 Tile[1549:c07] removeSprite startcall cbc: 2 // has 2
15:14:10.503 Tile[1549:c07] Found sprite in array, deleted! //Line above and under
15:14:10.504 Tile[1549:c07] containObject cbc: 0 //Deleted 2, supposed to kill 1
15:14:10.505 Tile[1549:c07] StopCheckDamage
15:14:11.820 Tile[1549:c07] removeSprite startcall cbc: 0
15:14:11.820 Tile[1549:c07] Didn't find the object in array, didn't delete!
15:14:11.821 Tile[1549:c07] elseCO cbc: 0
15:14:11.821 Tile[1549:c07] StopCheckDamage
If you look at the log location in the code above, you will see that it deletes two instead of the one I wanted to. How can I prevent this behaviour or customise it to only kill the proper sprite?
Edit: To clarify
I thought that as I use the spritein the -(void)explosionFromPoint:(CGPoint)explosionPoint withSprite:(CCSprite*)sprite; this would somehow just give an unique ID to the object and know which one I was talking about. I'm new to coding.
You have the same sprite in the array twice. Both entries are removed. If you're going to use removeObject you need to create multiple sprite objects. Otherwise you need to use removeObjectAtIndex.
The problem as #HotLicks mentioned is you push the same instance in the array.
You should use a local CCSprite instance so your scheduled calls will use different instances. The reason for adding the same instance twice is because placeBomb is called twice before anything happens and in the second call you override the first instance you created. Since _circle is a pointer by the time both scheduled tasks will be called _circle will point to the same instance in both.
So change :
_circle = [[CCSprite alloc]initWithFile:#"Circle.png"];
To :
CCSprite *circle = [[CCSprite alloc]initWithFile:#"Circle.png"];
and update the rest of the method with circle and not _circle.
There is something that is going wrong in your code. Try checking something like that when you remove object from array do this:
if([currentBombs count]>1 )
[currentBombs removeObjectAtIndex:1];
If your code works fine. that is only one object removed from your array. Then I suggest you to check your removeSprite method print sprite object to check what's going wrong.
I think you can use tag values if you using same sprite objects.
You probably added the same (indiviudal) sprite twice?
Instead of logging a variable that has the count, you can log the object itself. It will print the value of its description. That will print out the class and address for all NSObject subclasses per default. Actually NSMutableArray (and similar NS... classes) print quite well.
NSLog ("%#",myObj);
Doing so you probably see more clearly what really happens.
Don't use class variable while creating bombs and try....
CCSprite * _circle = [[CCSprite alloc]initWithFile:#"Circle.png"];
I am making a game, and everything works except the restart button. I would like the game to start again from the restart method, which loads everything onto the screen and sets up gameplay. However I get an error saying "Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[HelloWorldLayer resetAll:]: unrecognized selector sent to instance 0x9142c70'"
Below is the code for the main class (I'm assuming you guys don't need the other classes, as they work fine):
#import "HelloWorldLayer.h"
#import "AppDelegate.h"
#import "Block.h"
#import "Character.h"
#implementation HelloWorldLayer
-(id) init{ //Init method
if(self = [super initWithColor:ccc4(84,56,8,26)]){ //Initialise with a black background
[self restart];
}
return self;
}
-(void) setUpMenus{
moveRight = [CCMenuItemImage itemWithNormalImage:#"GoRightButton.png" selectedImage:#"GoRightButtonSelected.png" target: self selector:#selector(moveRightMethod:)];
moveLeft = [CCMenuItemImage itemWithNormalImage:#"GoLeftButton.png" selectedImage:#"GoLeftButtonSelected.png" target: self selector:#selector(moveLeftMethod:)];
moveUp = [CCMenuItemImage itemWithNormalImage:#"GoUpButton.png" selectedImage:#"GoUpButtonSelected.png" target: self selector:#selector(moveUpMethod:)];
moveDown = [CCMenuItemImage itemWithNormalImage:#"GoDownButton.png" selectedImage:#"GoDownButtonSelected.png" target: self selector:#selector(moveDownMethod:)];
moveRight.position = ccp(player.image.position.x - 10, player.image.position.y);
moveLeft.position = ccp(player.image.position.x - 80, player.image.position.y);
moveUp.position = ccp(player.image.position.x - 40, player.image.position.y + 30);
moveDown.position = ccp(player.image.position.x - 40, player.image.position.y - 30);
myScore.position = ccp(player.image.position.x - 20, player.image.position.y + 100);
mainMenu = [CCMenu menuWithItems:moveRight, moveLeft, moveUp, moveDown, nil];
[self addChild: mainMenu];
}
-(void) enemyMoveTowardsPlayer{
CGPoint playerPosition = player.image.position;
[enemy moveToLocationEnemy:&playerPosition];
}
-(void) moveRightMethod: (id) sender{
if(enemyExists == YES){
[self enemyMoveTowardsPlayer];
}
if(player.image.position.x < 5000){
[player move];
[self changeBack];
}
}
-(void) moveLeftMethod: (id) sender{
if(enemyExists == YES){
[self enemyMoveTowardsPlayer];
}
if(player.image.position.x > 20){
[player moveLeft];
[self change];
}
}
-(void) moveUpMethod: (id) sender{
if(enemyExists == YES){
[self enemyMoveTowardsPlayer];
}
if(player.image.position.y < 7000){
[player moveUp];
[self changeUp];
}
}
-(void) moveDownMethod: (id) sender{
if(player.image.position.y > -100){
[player moveDown];
[self changeDown];
}
if(enemyExists == YES){
[self enemyMoveTowardsPlayer];
}
}
-(void) callSpawnEnemy{
CCSprite *enemyImage = [CCSprite spriteWithFile:#"Lizard_Sprite.png"]; //Enemy picture
enemy = [[Character alloc] initWithImageAndNameAndProjectile:enemyImage
andAnotherParam:#"Enemy"];
//Enemy instantiation
enemy.image.position = ccp(100,400);
[self addChild:enemy.image]; //Add the enemy image object
enemyExists = YES;
}
-(void)ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{
//CODE FOR HANDLING TOUCH EVENTS. THE BELOW CODE SIMPLY CREATES AND INSTANTIATES A TOUCH OBJECT AND STORES THE LOCATION
CGPoint touchLocation;
for(UITouch *touch in touches ) {
touchLocation = [touch locationInView: [touch view]];
CGPoint prevLocation = [touch previousLocationInView: [touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
prevLocation = [[CCDirector sharedDirector] convertToGL: prevLocation];
CGPoint diff = ccpSub(touchLocation,prevLocation);
[self setPosition: ccpAdd(self.position, diff)];
}
//
NSLog(#"%f", touchLocation.x);
NSLog(#"%f",touchLocation.y);
}
-(void) change{ //Code to change the image to a leftward facing sprite
CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:#"PlayerSpriteLeftHugeV2.png"];
[player.image setTexture: tex];
}
-(void) changeBack{ //Code to change the image to a rightward facing sprite
CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:#"PlayerSpriteHugeV2.png"];
[player.image setTexture: tex];
}
-(void) changeUp{ //Code to change the image to a leftward facing sprite
CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:#"PlayerSpriteUp.png"];
[player.image setTexture: tex];
}
-(void) changeDown{ //Code to change the image to a rightward facing sprite
CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:#"PlayerSpriteDown.png"];
[player.image setTexture: tex];
}
-(void) generate2dMap{ //Method to generate a 130 by 80 block map (10400 blocks)
for(int i = 20; i < 13000; i+=100){ //X coordinate. Increment by length of each block.
for(int g = 20; g < 8000; g+=100){ //Y coordinate. Increment by length of each block.
CGPoint p = CGPointMake(i, g); //Point for the block. Changes every time the loop goes through
int randNumber = rand() % 11; //Random number to determine the block type
if(randNumber < 1){ //Only 1 in 10 blocks should be gold blocks
Block* gold = [[Block alloc] initWithTypePositionAndImage:#"Gold" andImageName:[CCSprite spriteWithFile:#"GoldBlockHuge.png"]];
//Create a gold object and pass the appropriate information to the constructor
[allBlocks addObject: gold]; //Add the gold object to the mutable array
gold.image.position = p; //Set the block image position to be the current location
[self addChild:gold.image]; //Add the block image to the screen
}
else{ //More common. Should occur 9/10 times
Block* dirt = [[Block alloc]
initWithTypePositionAndImage:#"Dirt" andImageName:[CCSprite spriteWithFile:#"DirtBlockHuge.png"]];
//Create a dirt object and pass the appropriate information to the constructor
[allBlocks addObject: dirt]; //Add the dirt object to the mutable array
dirt.image.position = p; //Set the block image position to be the current location
[self addChild:dirt.image]; //Add the block image to the screen
}
}
}
}
-(void) checkScore{ //Update the score. Set the score object to be the score primative from the player object
[myScore setString:[NSString stringWithFormat:#"%d", player->score]];
stringScore = myScore.string;
}
-(BOOL) checkForCollisions{ //Method to check for collision detection
for(Block *b in allBlocks){ //Loop through all blocks to check if any have collided with the player image
CGRect playerRect = CGRectMake(player.image.position.x, player.image.position.y, 90, 84);
//Rectangle object for the player
CGRect blockRect = CGRectMake(b.image.position.x, b.image.position.y, 50, 50);
//Rectangle object for each block
if(CGRectIntersectsRect(playerRect, blockRect)){ //If the block is in the state of intersecting the player
amountOfBlocks++;
collisionSprite = b; //Set the collision object to be the block
if([collisionSprite.type isEqualToString:#"Gold"]){ //If it's collided with a gold object...
[player incrementScoreBy:100]; //Then increment the score by 100
}
else if([collisionSprite.type isEqualToString:#"Bronze"]){
[player incrementScoreBy:20];
}
else if([collisionSprite.type isEqualToString:#"Silver"]){
[player incrementScoreBy:50];
}
else if([collisionSprite.type isEqualToString:#"Dirt"]){
[player incrementScoreBy:2];
}
return YES; //There has been a collision, and you should terminate this round of the method
}
if(enemyExists == YES){
CGRect enemyRect = CGRectMake(enemy.image.position.x, enemy.image.position.y, 80,80);
if(CGRectIntersectsRect(enemyRect, blockRect)){
collisionSprite = b;
enemyHitBlock = YES;
return YES;
}
if(CGRectIntersectsRect(enemyRect, playerRect)){
enemyHitPlayer = YES;
return YES;
}
}
}
return NO; //There has not been a collision, and you should terminate this round of the method
}
-(void) update: (ccTime) dt{ //Update method called on regular interval that checks for collisions, updates the score
if(isDone == NO){
if([self checkForCollisions]){ //Check for collisions
[self removeChild:collisionSprite.image cleanup:YES]; //Remove the sprite if there has been a collision
[allBlocks removeObject:collisionSprite]; //Remove the object if there has been a collision
if(enemyExists == YES){
if(enemyHitPlayer == YES){
[self removeChild:enemy.image cleanup:YES];
enemyHitPlayer = NO;
enemyExists = NO;
}
if(enemyHitBlock == YES){
[self removeChild:collisionSprite.image cleanup:YES]; //Remove the sprite if there has been a collision
enemyHitBlock = NO;
}
}
}
mainMenu.position = ccp(player.image.position.x - 10, player.image.position.y);
myScore.position = ccp(player.image.position.x - 20, player.image.position.y + 100);
if(enemyExists == NO){
[self callSpawnEnemy];
}
[self checkScore]; //Update the score
if([stringScore intValue] >= 200){
[self endGame];
}
}
}
-(void) endGame{
isDone = YES;
endGameString = #"You reached the max score in ";
stringBlockCount = [NSString stringWithFormat:#"%d blocks", amountOfBlocks];
totalString = [endGameString stringByAppendingString:stringBlockCount];
endGameMessage = [CCLabelTTF labelWithString:totalString fontName:#"Times New Roman" fontSize:20];
CGSize winSize = [[CCDirector sharedDirector] winSize];
CGPoint point = ccp(winSize.height/2,winSize.width/2);
endGameMessage.position = point;
[self stopAllActions];
[self runAction:[CCFollow actionWithTarget:endGameMessage]];
[self removeAllChildrenWithCleanup:YES];
[self addChild: endGameMessage];
restart = [CCMenuItemImage itemWithNormalImage:#"RestartButton.png" selectedImage:#"RestartButtonSelected.png" target: self selector:#selector(resetAll:)];
restart.position = ccp(point.x - 500, point.y - 250);
restartMenu = [CCMenu menuWithItems:restart, nil];
[self addChild:restartMenu];
}
-(void) restart{
[self removeAllChildrenWithCleanup:YES];
NSLog(#"Got here");
isDone = NO;
amountOfBlocks = 0;
allBlocks = [[NSMutableArray alloc] init]; //Instantiate array that holds all blocks
collisionSprite = [[Block alloc] init]; //Instantiate object that holds the collision object
enemyCollisionSprite = [[Character alloc] init];
enemyHitBlock = NO;
enemyHitPlayer = NO;
[self generate2dMap]; //Generate the map
self.isTouchEnabled = YES; //Enable touch technology
CCSprite *playerImage = [CCSprite spriteWithFile:#"PlayerSpriteHugeV2.png"]; //Player image object
player = [[Character alloc] initWithImageAndNameAndProjectile:playerImage andAnotherParam:#"Player"];
//Instantiate player object
player.image.position = ccp(0,210); //Set player image position
//Set player projectile image position
stringScore = [NSString stringWithFormat:#"%d", player->score]; //Score object
myScore = [CCLabelTTF labelWithString:stringScore fontName:#"Times New Roman" fontSize:20];
//CCLabel representation of score
myScore.position = ccp(20,300); //Set position of label score
myScore.color = ccc3(255,255,255); //Set color of label score
[self addChild:myScore]; //Add the score object
[self addChild:player.image]; //Add the player image object
[self callSpawnEnemy];
[self setUpMenus];
[self moveForward];
[self schedule:#selector(update:) interval:0.005]; //Schedule updating on 0.005 second intervals
}
+(void) resetAll{
[[CCDirector sharedDirector] replaceScene:[HelloWorldLayer scene]];
}
-(void) moveForward{ //Method to move the camera forward
[self runAction:[CCFollow actionWithTarget:player.image]];
}
+(CCScene*) scene{
CCScene *scene = [CCScene node];
HelloWorldLayer *layer = [HelloWorldLayer node];
[scene addChild: layer];
return scene;
}
-(void) dealloc{
[super dealloc];
}
-(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewController{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
#end
Any thoughts?
Jake
It seems that you called a class method(+) where you need to call the instance method(-). You just need to reset all the resources but not the whole object. And, as a class method, your +(void)resetAll method is meaningless for it has no return value.
I suggest, you may put all your resource-init codes into a single method, when you need to restart, just call [self removeAllChildrenWithCleanup:] and call that specified resource-init method.