【问题标题】:Gradually increase speed of SpriteKit objects逐渐提高 SpriteKit 对象的速度
【发布时间】:2015-09-16 09:43:23
【问题描述】:

我有一个简单的游戏,石头从屏幕的顶部到底部“下雨”。

我正在使用 SpriteKit,我希望石头能够随着时间的推移增加它们的速度,就像它会是 1 级 2 级 3 级等等。那么最大速度应该很容易实现。

我对石头的速度有一个可变的“速度”。

我想我可以只使用一个计时器,在 5 秒后速度 = 2,在 10 秒后速度 = 5,等等,但我不确定如何实现。

编辑:

我正在用这个移动我的石头:

-(void) moveStones{
SKAction *actionMove = [SKAction moveByX:0 y:-5 duration:0.25];
for(SKSpriteNode *stone in self.stones)
{
    [stone runAction:actionMove];
}
}

在我的 viewDidLoad 中,我有计时器:

 moveStones = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(moveStones) userInfo:nil repeats:YES];

如果计时器不尊重视图,有没有办法在没有计时器的情况下移动它们?

【问题讨论】:

    标签: ios objective-c sprite-kit


    【解决方案1】:

    如果你是通过物理移动石头,那么在石头生成后你应该施加更强的冲力/力(基于当前等级)如果你用动作移动它们,那么改变动作的速度,或者降低 moveDuration .如果您通过在 update: 方法中更改其位置来移动石头,则更改指示精灵在每次更新调用中应移动多少点的值。

    要实现生成石头等基于时间的操作或决定何时更改生成节点的速度,您应该使用 SKAction 或 update: 方法及其传递的名为 currentTime 的参数。在 SpriteKit 中使用 NSTimer 可能会导致问题,因为 NSTimer 不尊重视图、场景或节点的暂停状态,因此最好避免使用它。

    这是一个示例,说明如何使用SKAction 做你想做的事。在此示例中,您可以了解如何:

    • 跟踪经过的秒数
    • 维护当前等级信息
    • 随机延迟生成节点
    • 使用操作键停止生成节点
    • 可能还有更多

    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
    

    结果如下:

    现在由您决定如何设置速度。你可以玩这个。

    如果你想跳过某些部分,或者你不知道去哪里看,一切的核心都在名为generateStones的方法中,这就是你可以使用SKActions产生延迟石头的方法。另一个重要的方法是spawnStoneWithSpeed:,您可以在其中看到如何以动作的速度进行操作。

    另一种影响节点移动速度的方法,正如我所说,是改变方法moveByX: duration:的持续时间参数

    希望这会有所帮助!

    【讨论】:

    • @Robin 我可以尝试一下,只需一分钟 :) 您应该始终显示一个有问题的代码示例,该示例显示您迄今为止尝试过的内容。这样你就更有可能得到更好的答案,因为人们会知道问题的真正原因。
    • @Robin 乍一看,代码可能看起来很复杂,但其实不然。这些只是普通的事情,有很多方法可以做到。轻松浏览代码,查看 cmets,如果您有任何问题,请随时提出。
    • 我想首先,我会没事的。我没有检查所有代码,但它看起来不错,非常感谢。我明天有更多时间检查它是否适合我:)。
    • 也许还有一个问题,你能看一下这个吗:stackoverflow.com/questions/32610258/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-29
    • 2018-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-06
    相关资源
    最近更新 更多