让节点以恒定速度跟随

时间:2017-06-03 21:08:54

标签: ios swift sprite-kit

我试图制作一个小型迷你游戏,你可以在屏幕上拖动一个球,每隔10秒就会有一个球加入到你身后。到目前为止,你可以在屏幕上拖一个球,一个球跟着你,但当另一个球加入球组时。我认为这是因为球跟着我,取决于我的速度。所以有一种方法可以让我的球以一定的速度不断地跟着我,比如每秒10个像素,这样可以防止球分组在一起。

我目前正在努力获得分数,所以它应该很快就能活下来。如果你碰到其中一个球,你就死了。

下面是代码和我当前代码的简短gif

!(https://gyazo.com/1d6a56527bfd0884e8a26cff730f4e03

  import SpriteKit
  import GameplayKit

   struct physicsCatagory{
     static let me : UInt32 = 0x1 << 1
     static let enemy : UInt32 = 0x1 << 2

    }

  class GameScene: SKScene, SKPhysicsContactDelegate {


private func makeEnemyName() -> String {
    enemyCounter += 1
    return "enemy\(enemyCounter)"
}

private func addEnemyToDict(enemy: SKSpriteNode, target: SKSpriteNode) {
    if let name = enemy.name { spriteDictionary[name] = (enemy, target) }
    else { print("enemy not found") }
}

private func removeEnemyFromDict(enemy: SKSpriteNode) {
    if let name = enemy.name { spriteDictionary[name] = nil }
    else { print("enemy not removed from dictionary!") }
}

private func moveFollowerToTarget(_ sprites: FollowerAndTarget) {
    let action = SKAction.move(to: sprites.target.position, duration: 1)
    sprites.follower.run(action)
}



private func allEnemiesMoveToTarget() {
    for sprites in spriteDictionary.values {
        moveFollowerToTarget(sprites)
    }
}

let enemySpeed: CGFloat = 300
var me = SKSpriteNode()
// Tuple to keep track of enemy objects:
typealias FollowerAndTarget = (follower: SKSpriteNode, target: SKSpriteNode)
// [followerName: (followerSprite, targetSprite):
var spriteDictionary: [String: FollowerAndTarget] = [:]
// Give each enemy a unique name for the dictionary:
var enemyCounter = 0
var died = Bool()




override func didMove(to view: SKView) {

    createScene()
}



func createEnemy () {
    if died == true{

    }
    else {
    let enemy = SKSpriteNode(imageNamed: "enemy1")
    enemy.name = makeEnemyName()
    addEnemyToDict(enemy: enemy, target: me)
    moveFollowerToTarget((follower: enemy, target: me))
    enemy.size = CGSize(width: 60, height: 60)
    enemy.position = CGPoint(x:667, y: 200)
    enemy.physicsBody?.restitution = 0.5
    enemy.physicsBody = SKPhysicsBody(circleOfRadius: 60)
    enemy.physicsBody?.affectedByGravity = false
    enemy.zPosition = 2
    enemy.physicsBody?.linearDamping = 0
    enemy.physicsBody?.isDynamic = true
    enemy.physicsBody?.categoryBitMask = physicsCatagory.enemy
    enemy.physicsBody?.collisionBitMask = physicsCatagory.me
    enemy.physicsBody?.contactTestBitMask = physicsCatagory.me
    addChild(enemy)
    }
}


func didBegin(_ contact: SKPhysicsContact) {

    let firstBody = contact.bodyA
    let secondBody = contact.bodyB

    if firstBody.categoryBitMask == physicsCatagory.me && secondBody.categoryBitMask == physicsCatagory.enemy || firstBody.categoryBitMask == physicsCatagory.enemy && secondBody.categoryBitMask == physicsCatagory.me {
        died = true
        restartScene()
    }

}
var lose: SKLabelNode!


func restartScene(){
    self.removeAllChildren()
    self.removeAllActions()
    died = false

    if let nextScene = GameScene(fileNamed: "menuScene"){
        nextScene.scaleMode = self.scaleMode
        let transition = SKTransition.fade(withDuration: 1)
        view?.presentScene(nextScene, transition: transition)
    }
}

func createScene(){
    me = self.childNode(withName: "me") as! SKSpriteNode
    me.physicsBody = SKPhysicsBody(circleOfRadius: 20)
    me.physicsBody?.affectedByGravity = false
    me.physicsBody?.categoryBitMask = physicsCatagory.me
    me.physicsBody?.collisionBitMask = physicsCatagory.enemy
    me.zPosition = 2

    self.physicsWorld.contactDelegate = self

    let border = SKPhysicsBody (edgeLoopFrom: self.frame)
    border.friction = 0
    self.physicsBody = border

    run(SKAction.repeatForever(SKAction.sequence([SKAction.run(createEnemy), SKAction.wait(forDuration: 4.0)])))
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    for touch in touches{

        let location = touch.location(in: self)
        me.run(SKAction.moveTo(x: location.x, duration: 0))
        me.run(SKAction.moveTo(y: location.y, duration: 0))
        allEnemiesMoveToTarget()
    }




}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

    for touch in touches{

        let location = touch.location(in: self)
        me.run(SKAction.moveTo(x: location.x, duration: 0))
        me.run(SKAction.moveTo(y: location.y, duration: 0))
        allEnemiesMoveToTarget()
    }
}

override func update(_ currentTime: TimeInterval) {

    // Will iterate through dictonary and then call moveFollowerToTarget()
    // thus giving each enemy a new movement action to follow.
    allEnemiesMoveToTarget()
}

}

1 个答案:

答案 0 :(得分:2)

你走了:

import SpriteKit
import GameplayKit

struct physicsCatagory{
  static let me    : UInt32 = 0x1 << 1
  static let enemy : UInt32 = 0x1 << 2
  static let coin  : UInt32 = 0x1 << 3
}

class GameScene: SKScene, SKPhysicsContactDelegate {

  var lose: SKLabelNode!
  var me = SKSpriteNode()
  // Tuple to keep track of enemy objects:
  typealias FollowerAndTarget = (follower: SKSpriteNode, target: SKSpriteNode)
  // [followerName: (followerSprite, targetSprite):
  var spriteDictionary: [String: FollowerAndTarget] = [:]
  // Give each enemy a unique name for the dictionary:
  var enemyCounter = 0
  let enemySpeed: CGFloat = 3
  var died = Bool()
  var timer = SKLabelNode()
  var timerValue: Int = 0 {
    didSet {
      timer.text = "\(timerValue)"
    }
  }

  private func makeEnemyName() -> String {
    enemyCounter += 1
    return "enemy\(enemyCounter)"
  }

  private func addEnemyToDict(enemy: SKSpriteNode, target: SKSpriteNode) {
    if let name = enemy.name { spriteDictionary[name] = (enemy, target) }
    else { print("enemy not found") }
  }

  private func removeEnemyFromDict(enemy: SKSpriteNode) {
    if let name = enemy.name { spriteDictionary[name] = nil }
    else { print("enemy not removed from dictionary!") }
  }

  // dont change anything outside of this, this is what makes the enemy follow you, so i have to have the enemy follow me at a constant speed
  private func moveFollowerToTarget(_ sprites: FollowerAndTarget) {
    let location = me.position

    // Aim
    let dx = location.x - sprites.follower.position.x
    let dy = location.y - sprites.follower.position.y
    let angle = atan2(dy, dx)

    sprites.follower.zRotation = angle

    // Seek
    let vx = cos(angle) * enemySpeed
    let vy = sin(angle) * enemySpeed

    sprites.follower.position.x += vx
    sprites.follower.position.y += vy
  }


  private func allEnemiesMoveToTarget() {
    for sprites in spriteDictionary.values {

      moveFollowerToTarget(sprites)
    }
  }

  private func keepEnemiesSeparated() {

    for sprites in spriteDictionary.values {

      let iterator = sprites.follower
      iterator.constraints = []

      // get every other follower:
      var otherFollowers: [SKSpriteNode] = []
      for sprites in spriteDictionary.values {
        if sprites.follower == iterator { continue }
        else { otherFollowers.append(sprites.follower) }
      }

      // Assign constrain
      for follower in otherFollowers {
        let distanceBetween = CGFloat(60)
        let constraint = SKConstraint.distance(SKRange(lowerLimit: distanceBetween), to: follower)
        iterator.constraints!.append(constraint)
      }
    }
  }

  func createEnemy () {
    if died { return }

    let enemy = SKSpriteNode(color: .green, size: CGSize(width: 60, height: 60))
    enemy.size = CGSize(width: 60, height: 60)
    enemy.zPosition = 2
    enemy.position.y -= size.height / 2
    enemy.physicsBody = {
      let pb = SKPhysicsBody(circleOfRadius: 30)
      pb.restitution = 0.5
      pb.affectedByGravity = false
      pb.linearDamping = 0
      pb.isDynamic = true
      pb.categoryBitMask    = physicsCatagory.enemy
      pb.collisionBitMask   = physicsCatagory.me
      pb.contactTestBitMask = physicsCatagory.me
      return pb
    }()

    enemy.name = makeEnemyName()
    addEnemyToDict(enemy: enemy, target: me)
    moveFollowerToTarget((follower: enemy, target: me))
    keepEnemiesSeparated()

    addChild(enemy)
  }

  func createCoin () {
    let coin = SKSpriteNode(color: .yellow, size: CGSize(width: 20, height: 20))
    let height = self.view!.frame.height
    let width = self.view!.frame.width

    let randomPosition = CGPoint( x:CGFloat( arc4random_uniform( UInt32( floor( width  ) ) ) ),
                                  y:CGFloat( arc4random_uniform( UInt32( floor( height ) ) ) )
    )

    coin.position = randomPosition
    addChild(coin)
  }

  func restartScene(){
    self.removeAllChildren()
    self.removeAllActions()
    died = false

    let nextScene = GameScene(size: self.size)
    nextScene.scaleMode = self.scaleMode
    let transition = SKTransition.fade(withDuration: 1)
    view?.presentScene(nextScene, transition: transition)
  }

  func createScene(){
    me = SKSpriteNode(color: .blue, size: CGSize(width: 60, height: 60))
    me.physicsBody = SKPhysicsBody(circleOfRadius: 30)
    me.physicsBody?.affectedByGravity = false
    me.physicsBody?.categoryBitMask = physicsCatagory.me
    me.physicsBody?.collisionBitMask = physicsCatagory.enemy
    me.zPosition = 2

    timer = SKLabelNode(fontNamed: "Chalkduster")
    timer.text = "\(timerValue)"
    addChild(me)
    addChild(timer)

    let wait = SKAction.wait(forDuration: 1)

    let block = SKAction.run({
      [unowned self] in

      if self.timerValue >= 0{
        self.timerValue += 1
      }else{
        self.removeAction(forKey: "countdown")
      }
    })

    let sequence = SKAction.sequence([wait,block])

    run(SKAction.repeatForever(sequence), withKey: "countdown")

    self.physicsWorld.contactDelegate = self

    let border = SKPhysicsBody (edgeLoopFrom: self.frame)
    border.friction = 0
    self.physicsBody = border

    run(SKAction.repeatForever(SKAction.sequence([SKAction.run(createEnemy), SKAction.wait(forDuration: 2.0)])))

    run(SKAction.repeatForever(SKAction.sequence([SKAction.run(createCoin), SKAction.wait(forDuration: TimeInterval(arc4random_uniform(11) + 5))])))

  }


  override func didMove(to view: SKView) {
    scene?.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    createScene()
  }

  func didBegin(_ contact: SKPhysicsContact) {

    let firstBody = contact.bodyA
    let secondBody = contact.bodyB

    if firstBody.categoryBitMask == physicsCatagory.me    && secondBody.categoryBitMask == physicsCatagory.enemy
    || firstBody.categoryBitMask == physicsCatagory.enemy && secondBody.categoryBitMask == physicsCatagory.me {
      died = true
      restartScene()
    }
  }

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    for touch in touches{

      let location = touch.location(in: self)
      me.run(SKAction.moveTo(x: location.x, duration: 0))
      me.run(SKAction.moveTo(y: location.y, duration: 0))
      allEnemiesMoveToTarget()
    }
  }

  override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

    for touch in touches{

      let location = touch.location(in: self)
      me.run(SKAction.moveTo(x: location.x, duration: 0))
      me.run(SKAction.moveTo(y: location.y, duration: 0))
      allEnemiesMoveToTarget()
    }
  }

  override func update(_ currentTime: TimeInterval) {

    // Will iterate through dictonary and then call moveFollowerToTarget()
    // thus giving each enemy a new movement action to follow.
    allEnemiesMoveToTarget()
  }
}