我正在尝试制作一个游戏,其中物品需要追逐食物。当食物在给定半径范围内时,物体加速。但我需要速度始终保持不变。
有任何建议如何解决这个问题?我试图在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)
}
}
}
答案 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
这会给你一个恒定的速度。
我可以更详细,但现在已经很晚了,你可能知道事情是如何运作的,所以我会停在这里。快乐的编码!