需要物体以恒定的速度移动
Posted
技术标签:
【中文标题】需要物体以恒定的速度移动【英文标题】:Need objects to move with a constant speed 【发布时间】:2017-05-28 13:56:29 【问题描述】:我正在尝试创建一个游戏,其中对象需要追逐食物。现在,当食物在给定半径内时,物体会加速。但我需要速度始终保持不变。
有什么建议可以解决这个问题吗?我曾尝试在chasing函数下添加一个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 *** answer of how chasing works : https://***.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)
可以看到更新功能的游戏场景。
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)
【问题讨论】:
@Whirlwind 请看这里:) 查看答案,我刚刚测试过,效果不错。 【参考方案1】:大概是这样的(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的一个属性。
并更改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 *** answer of how chasing works : https://***.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 参数。您可能想知道什么是增量时间?我将引用那篇文章中的 LearnCocos2d:
Delta 时间只是上一个时间和上一个时间之间的时间差 当前帧。
为什么保持节点的恒定速度很重要?好吧,我们使用我们的Fish.swimmingSpeed
变量来确定鱼的速度(忘记kFishSpeed
,它现在没有目的)。
现在SKAction
的情况下,一个duration参数直接决定了鱼的速度,因为duration适用于时间,而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,因为如果游戏滞后很多(例如,它的英雄传送),玩家会失去对时刻的控制,但这部分是题外话:) 由你来测试什么工作/看起来更适合你。
所以我们这样做(为了计算距离):
let vx = cos(angle) * self.swimmingSpeed * delta
let vy = sin(angle) * self.swimmingSpeed * delta
这会给你一个恒定的速度。
我可以更详细地介绍,但这里已经很晚了,你可能已经知道事情是如何运作的,所以我会在这里停下来。编码愉快!
【讨论】:
如果您不想使用增量时间,只需将swimmingSpeed
除以 60(或您的首选帧速率),而不是乘以增量。以上是关于需要物体以恒定的速度移动的主要内容,如果未能解决你的问题,请参考以下文章