Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
665 views
in Technique[技术] by (71.8m points)

ios - How to speed up a Sprite Kit game after every additional 50 points

I am making a 2D game with Sprite Kit and I need to add a method to speed up the game after every 50 points.

I worked out a method that adds the speed as wanted, but I have to copy, paste and adjust an if-statement in the update method for every additional 50 points.

Another problem with my solution is that I have to give my points an increment right after speeding it up, because if not the game speeds up completely, though I don't know why.

Here is my solution so far:

// inside the update method
// points are added when an enemy is destroyed
// pointSpeed is the float variable that is used to change positions

if (points == 4) {
    pointSpeed++;
    points++;
}
if (points == 8) {
    pointSpeed++;
    points++;
}
if (points == 12) {
    pointSpeed++;
    points++;
}

I would like to implement this feature without being forced to do all increments manually and without the game speeding up without points increments.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Swift way

You can use didSet property observer to change the game speed based on the current score:

import SpriteKit

class GameScene: SKScene {

    var score:Int = 0 {

        didSet{

            if score % 10 == 0 {
               gameSpeed += 0.5
            }

            label.text = "Score : (score), Speed : (gameSpeed)"
        }

    }

    var gameSpeed:Double = 1.0

    var label:SKLabelNode = SKLabelNode(fontNamed: "ArialMT")

    override func didMoveToView(view: SKView) {
        /* Setup your scene here */

        label.text = "Score : (score), Speed : (gameSpeed)"
        label.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidX(frame))
        label.fontSize = 18

        addChild(label)

    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

        score++
    }

}

From the docs:

didSet is called immediately after the new value is stored.

So, immediately after the new score is set, you should update the gameSpeed variable.

Objective-C way

In Objective-C there is no such a thing which can provide the exact same behaviour like Swift's didSet and willSet property observers. But there are another ways, for example you can override a score's setter, like this:

#import "GameScene.h"

@interface GameScene()

@property(nonatomic, strong) SKLabelNode *label;

@property(nonatomic, assign) NSUInteger score;

@property(nonatomic, assign) double gameSpeed;

@end

@implementation GameScene

-(instancetype)initWithCoder:(NSCoder *)aDecoder{

NSLog(@"Scene's initWithSize: called");

if(self = [super initWithCoder:aDecoder]){

        /*

         Instance variables in Obj-C should be used only while initialization, deallocation or when overriding accessor methods.
         Otherwise, you should use accessor methods (eg. through properties).

        */

        _gameSpeed  = 1;
        _score      = 0;

       NSLog(@"Variables initialized successfully");
    }

    return self;
}

-(void)didMoveToView:(SKView *)view {

    self.label      = [SKLabelNode labelNodeWithFontNamed:@"ArialMT"];
    self.label.fontSize = 18;
    self.label.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
    self.label.text = [NSString stringWithFormat:@"Score : %lu, Speed %f", self.score, self.gameSpeed];
    [self addChild:self.label];

}

//Overriding an actual setter, so when score is changed, the game speed will change accordingly
-(void)setScore:(NSUInteger)score{

    /*

     Make sure that you don't do assigment like this, self.score = score, but rather _score = score
     because self.score = somevalue, is an actual setter call - [self setScore:score]; and it will create an infinite recursive call.

     */
    _score          = score;


    if(_score % 10 == 0){
        self.gameSpeed += 0.5;
    }

    // Here, it is safe to to write something like self.score, because you call the getter.

    self.label.text = [NSString stringWithFormat:@"Score : %lu, Speed %f", self.score, self.gameSpeed];

}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    self.score++;
}

@end

Or, I guess, you can do the same using KVO, but for your example because of simplicity, you can just override a setter. Or maybe you could just make a custom method which will increase the score, handle the gameSpeed's updating logic, and update the speed if necessary:

//Whenever the player scores, you will do this
[self updateScore: score];

So, you will have an additional method definition, which must be called when player scores, in compare to this (assuming that score's setter is overridden):

self.score = someScore; 

It's really up to you.

Hope this make sense and it helps!

EDIT:

I've changed the initWithSize: to initWithCoder, because initWithSize is not called when the scene is loaded from sks, which is probably how you are doing it, due to fact that in Xcode 7 scene is loaded by default from .sks file.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...