需要物体以恒定的速度移动
我试图创造一个游戏,物品需要追逐食物。 目前,当食物在给定半径内时,物体加速。 但我需要的速度永远是相同的。
任何build议如何解决这个问题? 我试图添加一个SKEction下的追逐function,我设置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,你可以看到更新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)) } } }
variablesprev
是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 } } }
我已经添加了增量时间参数。 你可能想知道delta时间是什么? 我将从该文章中引用LearnCocos2d:
Delta时间就是前一帧和当前帧之间的时间差。
为什么这对保持节点的恒定速度很重要? 那么,我们使用我们的Fish.swimmingSpeed
variables来确定鱼的速度(忘记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秒),所以我们计算这个差异,并使用它调整运动。 比起正常的行为,不使用德尔塔时间,因为玩家在游戏滞后很久(如英雄传送)的时候就会失去控制,但是那部分是关键的话题:)这是由你来决定的工作/看起来对你更好。
所以我们(为了计算距离):
let vx = cos(angle) * self.swimmingSpeed * delta let vy = sin(angle) * self.swimmingSpeed * delta
这会给你一个恒定的速度。
我可以深入细节,但在这里已经晚了,你可能已经知道事情是如何运作的,所以我会停下来。 快乐的编码!