having a node follow at a constant speed

北慕城南 提交于 2019-12-05 18:32:43

Here you go:

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()
  }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!