需要物体匀速运动 [英] Need objects to move with a constant speed

查看:38
本文介绍了需要物体匀速运动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个游戏,其中对象需要追逐食物.现在,当食物在给定半径内时,物体会加速.但我需要速度始终保持不变.

I'm trying to create a game, where the objects need to chase food. Right now the objects speeds up, when the food is within the given radius. But I need the speed to always be the same.

有什么建议可以解决这个问题吗?我试图在chase函数下添加一个SKAction,在那里我设置了position.x和position.y,但我无法让它正常工作.

Any suggestions how to fix this? I have tried to add an SKAction under the chase function, where I set the position.x and position.y, but I can't make it work correct.

鱼类:

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.

Gamescene where you can see the update function.

 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)
        }
    }
}

推荐答案

大概是这样的 (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 的一个属性.

The variable prev is a property of GameScene.

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

 //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
            }
        }
    }

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

I have added the delta time parameter. You may wonder what is delta time? I will quote LearnCocos2d from that article:

Delta 时间只是上次和上次之间的时间差当前帧.

Delta time is simply the time difference between the previous and the current frame.

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

Why is this important to maintain the constant speed of a node? Well, we use our Fish.swimmingSpeed variable to determine the speed of fish(forget kFishSpeed, it doesn't have a purpose now).

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

Now in the case of SKAction, a duration parameter directly determines the speed of fish, because duration applies to time, and time = distance / speed, so we calculate the time like this currently:

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

现在假设持续时间等于 1.这意味着鱼将每秒移动 100 点.现在,update() 方法和动作之间的区别在于它每秒执行 60 次.因为我们的方法 chase() 理想情况下每秒调用 60 次,我们的速度现在必须是 Fish.swimmingSpeed/60.

Now lets say that duration equals to 1. That means, the fish is going to move 100 pts per second. Now, the difference between update() method and actions, is that it is executed 60 times per second. And because our method chase() is ideally called 60 time per second, our speed now have to be Fish.swimmingSpeed / 60.

这就是增量时间的用武之地.因为可能发生帧不是在 1/60 秒 (0.016667) 内渲染,而是渲染可能需要更长的时间(例如 0.02,0.03 秒),我们计算该差异,并用它来调整运动.与不使用增量时间的正常行为相比,IMO 有点作弊,因为如果游戏滞后很多(例如其英雄传送),玩家将失去对时刻的控制,但那部分是题外话:) 由您来测试什么工作/看起来更适合你.

And this is where delta time comes in. Because it may happen that frame is not rendered in 1/60 second (0.016667), but rather rendering may take longer (eg. 0.02,0.03 sec), we calculate that difference, and use it to adjust the movement. Kind of cheating IMO in compare to normal behaviour without using delta time, because player loses the control on moments if game lags a lot (eg. its hero teleports), but that part is off topic :) It is up to you to test what works / looks better for you.

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

So we do (in order to calculate the distance):

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

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

and that will give you a constant speed.

我可以更详细地介绍,但现在已经晚了,你可能已经知道事情是如何运作的,所以我就到此为止了.快乐编码!

I could go more into detail but it is late here, and you probably got an idea how things are functioning, so I will stop here. Happy coding!

这篇关于需要物体匀速运动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆