In a game I have created I created a game mode where the player has 30 seconds to hit as many targets as they can. I added a 30 second countdown timer in the -(void)update:(CFTimeInterval)currentTime method. When the countdown is initiated, the frame rate drops significantly, and 99% of the cpu is used whenever the object collides with the target. This doesn't happen when the timer is not running. Is there another way to add a countdown timer? If not, how can I lessen the cpu usage and keep the frame rate constant? I am using Xcode 6.1 and the code is below. Also this is an iOS 8 problem. On iOS 7.1 it runs fine.
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
//reset counter if starting
int highscoret = [[NSUserDefaults standardUserDefaults] integerForKey: #"highScoret"];
if (startGamePlay){
startTime = currentTime;
startGamePlay = NO;
}
countDown.text = [NSString stringWithFormat:#"Start!"];
int countDownInt = 30.0 -(int)(currentTime-startTime);
if(countDownInt>0){ //if counting down to 0 show counter
countDown.text = [NSString stringWithFormat:#"%i", countDownInt];
}else if(countDownInt==0) { //if not show message, dismiss, whatever you need to do.
countDown.text=#"Time's Up!";
self.highScoret.text = [NSString stringWithFormat:#"%d Baskets",highscoret];
SKScene *endGameScene = [[GameOverT alloc] initWithSize:self.size];
SKTransition *reveal = [SKTransition fadeWithDuration:0.5];
[self.view presentScene:endGameScene transition:reveal];
[self saveScore2];
if (self.points > highscoret) {
self.highScoret.text = [NSString stringWithFormat:#"%d Points",highscoret];
[self saveScore];
self.points = 0;
self.scoreLabel.text = [NSString stringWithFormat:#"%d Points",_points];
}
else {
self.points = 0;
self.scoreLabel.text = [NSString stringWithFormat:#"%d Points",_points];
self.highScoret.text = [NSString stringWithFormat:#"%d Points",highscoret];
}
}
}
And here's other relevant code
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
if (CGRectContainsPoint(countDown.frame, location)){
startGamePlay = YES;
self.baskett = 0;
[self reset];
}
}
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKSpriteNode * object = [SKSpriteNode spriteNodeWithImageNamed:#"object.png"];
object.position = self.object2.position;
object.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width/2];
object.physicsBody.dynamic = YES;
object.physicsBody.categoryBitMask = objectCategory;
object.physicsBody.contactTestBitMask = targetCategory;
object.physicsBody.collisionBitMask = 0;
object.physicsBody.usesPreciseCollisionDetection = YES;
CGPoint offset = rwSub(location, object.position);
if (offset.y <= 0) return;
[self addChild:object];
CGPoint direction = rwNormalize(offset);
CGPoint shootAmount = rwMult(direction, 1000);
CGPoint realDest = rwAdd(shootAmount, object.position);
float velocity = 200/1.0;
float realMoveDuration = self.size.width / velocity;
SKAction * actionMove = [SKAction moveTo:realDest duration:realMoveDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[object runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
-(void)object:(SKSpriteNode *) object didCollideWithTarget:(SKSpriteNode *) target {
[object removeFromParent];
[[self scene] runAction:[SKAction playSoundFileNamed:#"effect2.wav" waitForCompletion:NO]];
self.points++;
self.scoreLabel.text = [NSString stringWithFormat:#"%d Points",_points];
if (self.points == 3) {
[self obstacle];
}
}
timer inside a update function is a bad idea used skaction with waitForDuration like this may be slove your problem
SKAction *updateTime= [SKAction sequence:#[
///fire function after one second
[SKAction waitForDuration:1.0f],
[SKAction performSelector:#selector(callFunction)
onTarget:self]
]];
//where 30 is number of time you want’s to call your function
[self runAction:[SKAction repeatAction:updateTime count:30] ];
-(void) callFunction
{
//what ever you want to do
}
Related
I am having trouble having my game begin paused so that the player has to press a button to start the game. I also have collision detection working, so when the two objects collide they just fall off of the screen. Here is my current code:
- (instancetype)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.physicsWorld.contactDelegate = self;
[self buildBackground];
[self startScrolling];
_firstPosition = CGPointMake(self.frame.size.width * 0.817f, self.frame.size.height * .40f);
_squirrelSprite = [SKSpriteNode spriteNodeWithImageNamed:#"squirrel"];
_squirrelSprite.position = _firstPosition;
_atFirstPosition = YES;
_squirrelSprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
_squirrelSprite.physicsBody.categoryBitMask = squirrelHitCategory;
_squirrelSprite.physicsBody.contactTestBitMask = nutHitCategory;
_squirrelSprite.physicsBody.collisionBitMask = nutHitCategory;
_squirrelSprite.physicsBody.affectedByGravity = NO;
self.physicsWorld.contactDelegate = self;
[self addChild:_squirrelSprite];
// Declare SKAction that waits 2 seconds
SKAction *wait = [SKAction waitForDuration:3.0];
// Declare SKAction block to generate the sprites
SKAction *createSpriteBlock = [SKAction runBlock:^{
const BOOL isHeads = arc4random_uniform(100) < 50;
NSString* spriteName = isHeads ? #"lightnut.png" : #"darknut.png";
SKSpriteNode *nut = [SKSpriteNode spriteNodeWithImageNamed:spriteName];
BOOL heads = arc4random_uniform(100) < 50;
nut.position = (heads)? CGPointMake(257,600) : CGPointMake(50,600);
nut.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(200,160)];
nut.physicsBody.categoryBitMask = nutHitCategory;
nut.physicsBody.contactTestBitMask = squirrelHitCategory;
nut.physicsBody.collisionBitMask = squirrelHitCategory;
nut.physicsBody.affectedByGravity = NO;
self.physicsWorld.contactDelegate = self;
[self addChild: nut];
SKAction *moveNodeUp = [SKAction moveByX:0.0 y:-700.0 duration:1.3];
[nut runAction: moveNodeUp];
}];
// Combine the actions
SKAction *waitThenRunBlock = [SKAction sequence:#[wait,createSpriteBlock]];
// Lather, rinse, repeat
[self runAction:[SKAction repeatActionForever:waitThenRunBlock]];
}
return self;
}
My touchesBegan:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (_atFirstPosition)
{
SKAction *moveNodeLeft = [SKAction moveByX:-207.8 y:0.0 duration:0.85];
[_squirrelSprite runAction: moveNodeLeft withKey:#"moveleft"];
} else {
SKAction *moveNodeRight = [SKAction moveByX:207.8 y:0.0 duration:0.85];
[_squirrelSprite runAction: moveNodeRight withKey:#"moveright"];
}
_atFirstPosition = !_atFirstPosition;
_squirrelSprite.xScale *= -1.0;
}
Finally, my didBeginContact:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
if(firstBody.categoryBitMask == squirrelHitCategory || secondBody.categoryBitMask == nutHitCategory)
{
}
}
I've been trying to figure this out for the last 2 weeks and I have read many tutorials and code to start the game with a simple start button and end screen, but nothing has worked. Any help is greatly appreciated!
I would use a navigation controller and have your start button on the root view and have that button push a viewcontroller that presents your game.
I'm not sure if I worded the question right but basically I'm working on a game that has a start button, that triggers a 10 second timer when the user clicks it, then the user has 10 seconds to collect a bunch of cats. Everything works good, my problem is that the code is set up like this in the update: method:
//reset counter if starting:
if (startGamePlay) {
startTime = currentTime;
startGamePlay = NO;
}
countDownInt = 10.0 - (int)(currentTime - startTime);
if (countDownInt>0) {
countDown.text = [NSString stringWithFormat:#"TIME REMAINING: %i", countDownInt];
}else if (countDownInt <= 0) {
[self runAction:[SKAction sequence:#[
[SKAction runBlock:^{
countDown.text = #"";
}],
[SKAction waitForDuration:0.5],
[SKAction runBlock:^{
//transition
}],
]]];
}
Where I want to insert a transition to the game over scene where it says //transition. The problem is that this is the same code that is at the beginning of the game because time timer is only initialised when the user clicks a button, at which point the startGamePlay BOOL is set to YES, and the timer is activated.
How can I insert this:
gameOverScene *gameOver = [[gameOverScene alloc] initWithSize:self.size];
[self.view presentScene:gameOver transition:[SKTransition moveInWithDirection:SKTransitionDirectionUp duration:0.5]];
What changes would I have to make to make it work? Another bool possibly? Where would I put the bool?
This is the tap method if needed:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"tapCircle"]) {
score = score + 1;
}
if ([node.name isEqualToString:#"startButton"] && _startButton.zPosition == 10.0) {
startGamePlay = YES;
_startButton.zPosition = -2.0;
}
}
}
This question of yours is very hard to understand so I am not sure I got it quite right...
The 1st snippet you posted seems to be some method invoked by some timer and is being called constantly no matter the game state? If so, it is not the best design but still: You do not need another boolean value, you should rather use an integer to represent the game state or rather yet create an enumeration:
typedef enum:NSInteger {
gameStateBegin,
gameStatePlaying,
gameStateOver,
gameStateWiting
}gameState;
- (void)onTimer {
switch (self.state) {
case gameStateBegin: {
startTime = currentTime;
self.state = gameStatePlaying;
break;
}
case gameStatePlaying: {
countDownInt = 10.0 - (int)(currentTime - startTime);
if (countDownInt>0) {
countDown.text = [NSString stringWithFormat:#"TIME REMAINING: %i", countDownInt];
}
else if (countDownInt <= 0) {
self.state = gameStateOver;
}
break;
}
case gameStateOver: {
self.state = gameStateWiting;
[self runAction:[SKAction sequence:#[
[SKAction runBlock:^{
countDown.text = #"";
}],
[SKAction waitForDuration:0.5],
[SKAction runBlock:^{
//transition
}],
]]];
break;
}
default:
break;
}
}
I'm New to SpriteKit and build a simple CLickerGame in SpriteKit. Now i have the game finish but the thing which should spawn to click on it, spawns from time to time outside the screen. Where can i select the arc4random area where the thing should spawn?
#implementation MyScene
-(void)CreateTestObject
{
SKSpriteNode *TestObject = [SKSpriteNode spriteNodeWithImageNamed:#"TestObject"];
int maxX = CGRectGetMaxX(self.frame);
float ranX = (arc4random()%maxX) + 1;
int maxY = CGRectGetMaxY(self.frame);
float ranY = (arc4random()%maxY) + 1;
TestObject.position = CGPointMake(ranX, ranY);
TestObject = CGSizeMake(75, 75);
TestObject = #"Object";
[self addChild:TestObject];
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
bg = [SKSpriteNode spriteNodeWithImageNamed:#"bg.png"];
bg.position = CGPointMake(160, 284);
bg.size = CGSizeMake(self.frame.size.width, self.frame.size.height);
[self addChild:bg];
[self CreateTestObject];
}
return self;
}
-(void)selectNodeForTouch:(CGPoint)touchLocation
{
SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:touchLocation];
if([[touchedNode name] isEqualToString:#"TestObject"])
{
SKSpriteNode *TestObject = (SKSpriteNode *)[self childNodeWithName:#"TestObject"];
TestObject.name = #"DisabledTestObject";
SKAction *grow = [SKAction scaleTo:1.2 duration:0.1];
SKAction*shrink = [SKAction scaleTo:0 duration:0.07];
SKAction *removeNode = [SKAction removeFromParent];
SKAction *seq = [SKAction sequence:#[grow, shrink, removeNode]];
[TestObject runAction:seq];
[self CreateTestObject];
[self runAction:[SKAction playSoundFileNamed:#"Sound.aif" waitForCompletion:NO]];
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
//CGPoint location = [touch locationInNode:self];
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
[self selectNodeForTouch:positionInScene];
}
}
#end
Modify the max values for your (x,y) coordinates of any object being added to your view.
For example, on an iPhone 5 display in landscape mode and the spawned object being of size width=100, height=100, the max x value should be no more than 518. Width of screen being 568 less half the width of your object (50).
The same logic applies for the y value. The max y value should be 270 given the same object dimensions.
The above assumes that you have not changed your object's anchor point from (0.5,0.5) to another value.
I'm working on a game for iOS and I was wondering why this countdown won't work. I want to have a set amount of moves per level in this game, and I want it to subtract 1 move every time the user taps on one of the puzzle pieces.
This is the bit of code I've set up:
I set up two integers in the header file: int movesRemaining; and int moved;
The SKLabelNode that displays the amount of moves:
moves = [SKLabelNode labelNodeWithFontNamed:#"DIN Condensed"];
moves.position = CGPointMake(110, -15);
moves.text = [NSString stringWithFormat:#"MOVES %d", movesRemaining];
moves.fontSize = 25;
moves.zPosition = 2.0;
moves.fontColor = [SKColor whiteColor];
[menuBar addChild:moves];
The method that calculates the score:
-(void)moves {
movesRemaining = 4;
movesRemaining = (movesRemaining - moved);
}
The code that tells movesRemaining to subtract 1 move each time the puzzle is touched:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"sq1"]) {
[_BS1 removeFromParent];
moved = 1;
}
if ([node.name isEqualToString:#"sq2"]) {
[_BS2 removeFromParent];
moved = 1;
}
if ([node.name isEqualToString:#"sq3"]) {
[_BS3 removeFromParent];
moved = 1;
}
if ([node.name isEqualToString:#"sq4"]) {
[_BS4 removeFromParent];
moved = 1;
}
if ([node.name isEqualToString:#"sq5"]) {
[_BS5 removeFromParent];
moved = 1;
}
My question is where did I go wrong that it doesn't subtract 1 each time the puzzle is tapped? How can I properly make movesRemaining subtract 1 each time the user touches the puzzle piece?
If more information is needed please let me know.
Thanks.
Problem is that it isn't being updated. How to fix:
Touches:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"sq1"]) {
movesRemaining = movesRemaining - 1;
[_BS1 removeFromParent];
}
Take out the stuff in the initWithSize method and add this to your -(void)update:(CFTimeInterval)currentTime method:
if (!moves) {
moves = [SKLabelNode labelNodeWithFontNamed:#"DIN Condensed"];
moves.position = CGPointMake(110, -15);
moves.fontSize = 25;
moves.zPosition = 2.0;
moves.fontColor = [SKColor whiteColor];
[menuBar addChild:moves];
}
//scoring
moves.text = [NSString stringWithFormat:#"MOVES %d", movesRemaining];
This will fix your code. It will then update every time it is tapped like you want it to.
Move movesRemaining = 4; to the init method.
Then add [self moves]; to the end of -(void)touchesBegan.
In the moved method change it so that it declares moved = moved - 1; then that will add the rolling effect
moves = [SKLabelNode labelNodeWithFontNamed:#"DIN Condensed"];
moves.position = CGPointMake(110, -15);
moves.text = [NSString stringWithFormat:#"MOVES %d", movesRemaining];
moves.fontSize = 25;
moves.zPosition = 2.0;
moves.fontColor = [SKColor whiteColor];
movesRemaining = 4; //Move initial movesRemaining value here.
[menuBar addChild:moves];
-(void)moves { //Subtract number of moves everytime the user moves.
movesRemaining = (movesRemaining - moved);
}
I'm trying to change some SKActions of an existing Sprite Kit tutorial project, but I'm running into issues when it comes to movement. The Tutorial and GitHub project is here:
https://www.codefellows.org/blogs/simple-sprite-kit-game-tutorial-part1
https://github.com/megharastogi/GameTutorial
As you can see in the code below, each tap only moves the node once. How do I change it so that a long tap will move continuous move the node? I tried a few things like repeatActionForever, but that didn't work very well.
-(void)addShip
{
//initalizing spaceship node
ship = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
[ship setScale:0.5];
ship.zRotation = - M_PI / 2;
//Adding SpriteKit physicsBody for collision detection
ship.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ship.size];
ship.physicsBody.categoryBitMask = shipCategory;
ship.physicsBody.dynamic = YES;
ship.physicsBody.contactTestBitMask = obstacleCategory;
ship.physicsBody.collisionBitMask = 0;
ship.physicsBody.usesPreciseCollisionDetection = YES;
ship.name = #"ship";
ship.position = CGPointMake(120,160);
actionMoveUp = [SKAction moveByX:0 y:30 duration:.2];
actionMoveDown = [SKAction moveByX:0 y:-30 duration:.2];
[self addChild:ship];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
if(touchLocation.y >ship.position.y){
if(ship.position.y < 270){
[ship runAction:actionMoveUp];
}
}else{
if(ship.position.y > 50){
[ship runAction:actionMoveDown];
}
}
}
- (void)didMoveToView:(SKView *)view
{
UILongPressGestureRecognizer *tapper = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(tappedScreen:)];
tapper.minimumPressDuration = 0.1;
[view addGestureRecognizer:tapper];
}
- (void)tappedScreen:(UITapGestureRecognizer *)recognizer
{
float touchY = [self convertPointFromView:[recognizer locationInView:self.view]].y;
SKSpriteNode *ship = [self childNodeWithName:#"ship"];
if (recognizer.state == UIGestureRecognizerStateBegan) {
if(touchY >ship.position.y){
[ship runAction:[SKAction repeatActionForever:actionMoveUp] withKey:#"longTap"];
}else{
[ship runAction:[SKAction repeatActionForever:actionMoveDown] withKey:#"longTap"];
}
}
if (recognizer.state == UIGestureRecognizerStateEnded) {
[ship removeActionForKey:#"longTap"];
}
}
Add these two methods in your code.