2017-05-28 60 views
1

我正在嘗試創建一個遊戲,其中的對象需要追逐食物。現在,當食物在給定半徑範圍內時,物體會加速。但我需要速度始終如一。需要對象以恆定速度移動

任何建議如何解決這個問題?我試圖在追逐功能下添加一個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

@Whirlwind請在這裏看看:) – Grumme

+0

結帳答案,我剛剛測試過,並取得了良好的效果。 – Whirlwind

回答

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的屬性。

,並更改Fishchase()方法:

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

增量時間只是前一個和當前幀之間的時間差。

爲什麼這對於保持節點的恆定速度很重要?那麼,我們使用我們的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

這是增量時間到來的地方。因爲它可能發生幀不是以1/60秒(0.016667)呈現,而是渲染可能需要更長時間(例如0.02,0.03秒),所以我們計算差異,並用它來調整運動。在沒有使用德爾塔時間的情況下,作弊的國際海事組織比較正常的行爲,因爲如果遊戲滯後很久(例如,它的英雄傳送),玩家會在瞬間失去控制,但是那部分是關鍵的話題:)你需要測試什麼作品/看起來更適合你。

所以我們做的(爲了計算距離):

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

,並且會給你一個恆定的速度。

我可以更詳細地瞭解一下,但這裏已經很晚了,你可能已經知道事情是如何運作的,所以我會在這裏停下來。快樂的編碼!

+0

如果您不想使用增量時間,只需將60分鐘(或您的首選幀速率)劃分爲'swimmingSpeed',而不是將其乘以增量。 – Whirlwind

相關問題