需要物体以恒定速度移动

时间:2017-05-28 13:56:30

标签: ios swift swift3 sprite-kit

我正在尝试制作一个游戏,其中物品需要追逐食物。当食物在给定半径范围内时,物体加速。但我需要速度始终保持不变。

有任何建议如何解决这个问题?我试图在chase函数下添加一个SKAction,我在其中设置position.x和position.y,但我无法使其正常工作。

鱼类:

class Fish:SKSpriteNode{
private let kMovingAroundKey = "movingAround"
private let kFishSpeed:CGFloat = 4.5
private var swimmingSpeed:CGFloat = 100.0
private let sensorRadius:CGFloat = 100.0
private weak var food:SKSpriteNode! = nil //the food node that this fish currently chase

override init(texture: SKTexture?, color: UIColor, size: CGSize) {
    super.init(texture: texture, color: color, size: size)

    physicsBody = SKPhysicsBody(rectangleOf: size)
    physicsBody?.affectedByGravity = false
    physicsBody?.categoryBitMask = Collider.fish
    physicsBody?.contactTestBitMask = Collider.food
    physicsBody?.collisionBitMask = 0x0 //No collisions with fish, only contact detection
    name = "fish"

    let sensor = SKShapeNode(circleOfRadius: 100)
    sensor.fillColor = .red
    sensor.zPosition = -1
    sensor.alpha = 0.1
    addChild(sensor)
}

func getDistanceFromFood()->CGFloat? {

    if let food = self.food {

        return self.position.distance(point: food.position)
    }
    return nil

}

func lock(food:SKSpriteNode){

    //We are chasing a food node at the moment
    if let currentDistanceFromFood = self.getDistanceFromFood() {

        if (currentDistanceFromFood > self.position.distance(point: food.position)){
            //chase the closer food node
             self.food = food
            self.stopMovingAround()
        }//else, continue chasing the last locked food node

    //We are not chasing the food node at the moment
    }else{
         //go and chase then
         if food.position.distance(point: self.position) <= self.sensorRadius {

            self.food = food
            self.stopMovingAround()
        }
    }
}

//Helper method. Not used currently. You can use this method to prevent chasing another (say closer) food while already chasing one
func isChasing(food:SKSpriteNode)->Bool{

    if self.food != nil {

        if self.food == food {
            return true
        }
    }

    return false
}

func stopMovingAround(){

    if self.action(forKey: kMovingAroundKey) != nil{
       removeAction(forKey: kMovingAroundKey)
    }
}


//MARK: Chasing the food
//This method is called many times in a second
func chase(within rect:CGRect){

    guard let food = self.food else {

        if action(forKey: kMovingAroundKey) == nil {
            self.moveAround(within: rect)
        }
        return
    }

    //Check if food is in the water
    if rect.contains(food.frame.origin) {

        //Take a detailed look in my Stackoverflow answer of how chasing works : https://stackoverflow.com/a/36235426

        let dx = food.position.x - self.position.x
        let dy = food.position.y - self.position.y

        let angle = atan2(dy, dx)

        let vx = cos(angle) * kFishSpeed
        let vy = sin(angle) * kFishSpeed

        position.x += vx
        position.y += vy

    }
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

func moveAround(within rect:CGRect){

    if scene != nil {

        //Go randomly around the screen within view bounds
        let point = rect.randomPoint()

        //Formula: time = distance / speed
        let duration = TimeInterval(point.distance(point: position) / self.swimmingSpeed)
        let move = SKAction.move(to: point, duration: duration)
        let block = SKAction.run {
            [unowned self] in

            self.moveAround(within: rect)
        }
        let loop = SKAction.sequence([move,block])

        run(loop, withKey: kMovingAroundKey)
    }
}
}

Gamescene,您可以在其中看到更新功能。

 override func update(_ currentTime: TimeInterval) {

    self.enumerateChildNodes(withName: "fish") {
        [unowned self] node, stop in

        if let fish = node as? Fish {

            self.enumerateChildNodes(withName: "food") {
                node, stop in

                fish.lock(food: node as! SKSpriteNode)
            }

            fish.chase(within: self.water.frame)
        }
    }
}

1 个答案:

答案 0 :(得分:2)

可能是这样的(GameScene):

 var prev : TimeInterval!

    //MARK: Chasing the food
    override func update(_ currentTime: TimeInterval) {

        defer { prev = currentTime }
        guard prev != nil else { return }

        let dt = currentTime - prev

        print("delta time \(dt)")

        self.enumerateChildNodes(withName: "fish") {
            [unowned self] node, stop in

            if let fish = node as? Fish {

                self.enumerateChildNodes(withName: "food") {
                    node, stop in

                    fish.lock(food: node as! SKSpriteNode)
                }

                fish.chase(within: self.water.frame, delta:CGFloat(dt))
            }
        }
    }

变量prev是GameScene的属性。

并更改chase()类中的Fish方法:

 //MARK: Chasing the food
    func chase(within rect:CGRect, delta:CGFloat){

        guard let food = self.food else {

            if action(forKey: kMovingAroundKey) == nil {
                self.moveAround(within: rect)
            }
            return
        }

        //Check if food is in the water
        if rect.contains(food.frame.origin) {

            //Take a detailed look in my Stackoverflow answer of how chasing works : https://stackoverflow.com/a/36235426

            //check for collision

            if self.frame.contains(food.frame.origin) {
               food.removeFromParent()
            }else {
                let dx = food.position.x - self.position.x
                let dy = food.position.y - self.position.y

                let angle = atan2(dy, dx)



                let vx = cos(angle) * self.swimmingSpeed * delta
                let vy = sin(angle) * self.swimmingSpeed * delta

                print("vx \(vx), vy (\(vy)")



                position.x += vx
                position.y += vy

                //time = distance / speed
            }
        }
    }

我添加了delta time参数。您可能想知道什么是delta时间?我将从那篇文章中引用LearnCocos2d:

  

Delta时间只是之前和之间的时差   当前框架。

为什么保持节点的恒定速度很重要?好吧,我们使用我们的Fish.swimmingSpeed变量来确定鱼的速度(忘记kFishSpeed,它现在没有目的)。

现在,在SKAction的情况下,持续时间参数直接决定了鱼的速度,因为持续时间适用于时间,time = distance / speed,所以我们计算当前的时间:

let duration = TimeInterval(point.distance(point: position) / self.swimmingSpeed)

现在让我们说持续时间等于1.这意味着,鱼将每秒移动100点。现在,update()方法和操作之间的区别在于它每秒执行60次。因为我们的方法chase()理想地称为每秒60次,所以我们的速度现在必须是Fish.swimmingSpeed / 60

这就是delta时间到来的地方。因为可能会发生帧不是在1/60秒(0.016667)中渲染,而是渲染可能需要更长时间(例如0.02,0.03秒),我们计算出这个差异,并用它来调整运动。有点作弊IMO与正常行为相比没有使用delta时间,因为如果游戏滞后很多(例如它的英雄传送),玩家失去了对时刻的控制,但那部分是关于主题的:)这取决于你测试什么工作/看起来更适合你。

所以我们这样做(为了计算距离):

let vx = cos(angle) * self.swimmingSpeed * delta
let vy = sin(angle) * self.swimmingSpeed * delta

这会给你一个恒定的速度。

我可以更详细,但现在已经很晚了,你可能知道事情是如何运作的,所以我会停在这里。快乐的编码!