WatchKit在SpriteKit游戏中移动SimpleSpriteNode

时间:2017-07-13 23:59:34

标签: ios sprite-kit watchkit skspritenode

我想知道是否有人可以使用WKCrownDelegate在SpriteKit Watch Game中移动SKSpriteNode。无论是Y方向还是X方向

希望这能帮助那些以WatchKit开头的人。

这是我的 GameElement.swift

extension GameScene {

  func addPlayer() {
    player = SKSpriteNode(imageNamed: "Spaceship")
    player.setScale(0.15)
    player.position = CGPoint(x: 5, y: -60)
    player.name = “ONE”
    player.physicsBody?.isDynamic = false
    player.physicsBody = SKPhysicsBody(rectangleOf: player.size)


    player2 = SKSpriteNode(imageNamed: "Spaceship")
    player2.setScale(0.15)
    player2.position = CGPoint(x: 5, y: -60)
    player2.name = “ONE”
    player2.physicsBody?.isDynamic = false
    player2.physicsBody = SKPhysicsBody(rectangleOf: player2.size)

    addChild(player)
    addChild(player2)

    playerPosition = player.position
  }
}

这是我的 GameScene.swift

class GameScene:  SKScene, SKPhysicsContactDelegate, WKCrownDelegate {

  var watchParticles:SKEmitterNode!

  var player:SKSpriteNode!
  var player2:SKSpriteNode!

  var playerPosition:CGPoint!

  override func sceneDidLoad() {

    self.scaleMode = SKSceneScaleMode.aspectFill

    watchParticles = SKEmitterNode(fileNamed: "watchParticles")
    addChild(watchParticles)


    self.physicsWorld.gravity = CGVector(dx: 0 , dy: 0)
    physicsWorld.contactDelegate = self
    addPlayer()
  }

  func moveSprite(player : SKSpriteNode,moveDirection: String){

    switch moveDirection {
    case "UP":
      print("UP")
      player.childNode(withName: "ONE")?.physicsBody?.applyImpulse(CGVector(dx: 60, dy: 0))
    case "DOWN":
      print("DOWN")
      player.childNode(withName: "ONE")?.physicsBody?.applyImpulse(CGVector(dx: -60, dy: 0))
    case "STOP":
      print("STOPPED")
      player.childNode(withName: "ONE")?.physicsBody?.velocity = CGVector(dx: 0, dy: 0)

    default:
      break
    }
  }
}

这是我的 InterfaceController.swift

class InterfaceController: WKInterfaceController, WKCrownDelegate {

  @IBOutlet var skInterface: WKInterfaceSKScene!
  private var moveDirection = ""
  private var game = GameScene()
  private var player = GameScene()

  override func awake(withContext context: Any?) {
    super.awake(withContext: context)
    crownSequencer.delegate = self
    crownSequencer.focus()

    // Configure interface objects here.

    // Load the SKScene from 'GameScene.sks'
    if let scene = GameScene(fileNamed: "GameScene") {

      // Set the scale mode to scale to fit the window
      scene.scaleMode = .aspectFill

      // Present the scene
      self.skInterface.presentScene(scene)
      crownSequencer.delegate = self
      crownSequencer.focus()


      // Use a value that will maintain a consistent frame rate
      self.skInterface.preferredFramesPerSecond = 30
    }
  }

  func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) {
    if rotationalDelta > 0{
      moveDirection = "UP"
      game.moveSprite(player:  player.player, moveDirection: moveDirection)

    }else if rotationalDelta < 0{
      moveDirection = "DOWN"
      game.moveSprite(player:  player.player, moveDirection: moveDirection)
    }
  }

  func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) {
    moveDirection = "STOP"
    game.moveSprite(player:  player.player, moveDirection: moveDirection)

  }

  override func willActivate() {
    // This method is called when watch view controller is about to be visible to user
    super.willActivate()
  }

  override func didDeactivate() {
    // This method is called when watch view controller is no longer visible
    super.didDeactivate()
  }
}

1 个答案:

答案 0 :(得分:0)

欢迎来到SO!

好的,有很多东西需要打开包装才能拿到一些爆米花...我认为你在这里走的正确,大多数时候你需要在出现错误时检查是否为零。

首先,这在您的界面控制器和与我有关的部分是错误的。在这里,您只是实例化新的GameScene实例,这些实例与您的接口控制器创建的gameScene实例完全分开几行。然后,你将冠代理功能发送到这些完全空的gameScenes。:

private var game = GameScene()   // You are referencing nothing here, just creating a new gamescene.
private var player = GameScene() // I don't think that player is supposed to be a gamescene!

我通过将您想要使用的实际gameScene分配给属性来修复它(因此它们可以由冠代表使用)。

private var game: GameScene!
lazy private var player: SKSpriteNode = self.game.player

override func awake(withContext context: Any?) {
  // ... Stuff...
  if let scene = GameScene(fileNamed: "GameScene") {
    game = scene

这也改为代表你的皇冠代表中的新代码:

game.moveSprite(player: player, moveDirection: moveDirection)

在addPlayer中你这样做:

player.physicsBody?.isDynamic = true // This needs to go AFTER you init your pb.
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)

...我通过交换线来修复它。

就个人而言,我喜欢做以下事情,以帮助确保不会出现小错误:

let pb = SKPhysicsBody(...)
pb.isDynamic = true
player.physicsBody = pb

moveSprite有很多问题,所以我不会像上面那样枚举它们。看看我做了什么然后问你我是否有任何问题。基本上,你从一开始就注定要使用这个func,因为你是从接口控制器调用这个方法的,并且输出的player值都是零。

另外,.applyImpulse给了我很糟糕的控件,所以我将其改为.position的简单调整。在播放器停止之前滑行仍有一个小问题,但这可以在另一个问题中处理:)(注意,我只在模拟器上测试过这个问题..可能不是设备上的问题)。

另外,我讨厌由字符串中的拼写错误引起的错误,因此我将此转换为枚举。

func moveSprite(player : SKSpriteNode, moveDirection: Direction) {

  // This will give us an equal amount of pixels to move across the watch devices:
  // Adjust this number for shorter / longer movements:
  let percentageOfScreenToMovePerRotation = CGFloat(1) // One percent
  let modifier = percentageOfScreenToMovePerRotation / 100
  let amountToMove = self.frame.maxX * modifier

  switch moveDirection {

  case .UP:
    player.position.x += amountToMove
  case .DOWN:
    player.position.x -= amountToMove
  case .STOP:
    break
  }
}

这里故事的真正道德是检查nil 。如果您一直只使用someOptional?.someMethod(),那么您可能无法轻易确定someMethod()是否实际被调用..因此,您不知道问题是否是与调用逻辑,方法或对象不存在等等。

强制解包在生产代码中不受欢迎,但IMO首次启动时非常有价值 - 因为它可以帮助您快速识别错误。

稍后,您可以开始使用if letguard之类的内容来帮助检查nil而不会导致程序崩溃,但是当您尝试学习时,这会增加代码的混乱性和复杂性新API和语言的基础知识。

作为最后一个提示,尽量不要使用硬编码字符串:将它们放入枚举或常量中,就像我在代码中一样:

// Because I hate string spelling erros, and you probably do too!
enum Direction {
  case UP, DOWN, STOP
}

// Because I hate errors related to spelling in strings:
let names = (ONE: "ONE", TWO: "TWO")

以下是完整的两个文件..注意,我必须注释掉一些内容才能让它在我的项目中运行:

GameScene:

// Because I hate string spelling erros, and you probably do too!
enum Direction {
  case UP, DOWN, STOP
}

class GameScene:  SKScene, SKPhysicsContactDelegate, WKCrownDelegate {

  var watchParticles:SKEmitterNode!

  var player: SKSpriteNode!
  var player2: SKSpriteNode!

  var playerPosition:CGPoint!

  // Because I hate errors related to spelling in strings:
  let names = (ONE: "ONE", TWO: "TWO")

  func addPlayer() {
    player = SKSpriteNode(color: .blue, size: CGSize(width: 50, height: 50))
    // player = SKSpriteNode(imageNamed: "Spaceship")
    //  player.setScale(0.15)
    player.position = CGPoint(x: 5, y: -60)
    player.name = names.ONE
    player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
    player.physicsBody!.isDynamic = true // This was placed *before* pb initialzier (thus never got called)

    player2 = SKSpriteNode(color: .yellow, size: CGSize(width: 50, height: 50))
    // player2 = SKSpriteNode(imageNamed: "Spaceship")
    //  player2.setScale(0.15)
    player2.position = CGPoint(x: 5, y: -60)
    player2.name = names.TWO
    player2.physicsBody = SKPhysicsBody(rectangleOf: player2.size)
    player2.physicsBody!.isDynamic = false // This was placed *before* pb initialzier (thus never got called)

    addChild(player)
    addChild(player2)

    playerPosition = player.position
  }

  override func sceneDidLoad() {

    self.scaleMode = SKSceneScaleMode.aspectFill

    //watchParticles = SKEmitterNode(fileNamed: "watchParticles")
    //addChild(watchParticles)

    self.physicsWorld.gravity = CGVector.zero
    physicsWorld.contactDelegate = self
    addPlayer()
  }

    func moveSprite(player : SKSpriteNode, moveDirection: Direction) {

      // This will give us an equal amount of pixels to move across the watch devices:
      // Adjust this number for shorter / longer movements:
      let percentageOfScreenToMovePerRotation = CGFloat(1) // One percent
      let modifier = percentageOfScreenToMovePerRotation / 100
      let amountToMove = self.frame.maxX * modifier

      switch moveDirection {

      case .UP:
        player.position.x += amountToMove
      case .DOWN:
        player.position.x -= amountToMove
      case .STOP:
        break
      }
    }
}

InterfaceController:

class InterfaceController: WKInterfaceController, WKCrownDelegate {

  @IBOutlet var skInterface: WKInterfaceSKScene!
  private var moveDirection = Direction.STOP

  private var game: GameScene!
  lazy private var player: SKSpriteNode = self.game.player

  override func awake(withContext context: Any?) {
    super.awake(withContext: context)
    crownSequencer.delegate = self
    crownSequencer.focus()

    if let scene = GameScene(fileNamed: "GameScene") {

      game = scene // VERY IMPORTANT!

      // Set the scale mode to scale to fit the window
      scene.scaleMode = .aspectFill

      // Present the scene
      self.skInterface.presentScene(scene)
      crownSequencer.delegate = self
      crownSequencer.focus()

      // Use a value that will maintain a consistent frame rate
      self.skInterface.preferredFramesPerSecond = 30
    }
    else {
      fatalError("scene not found")
    }
  }

  func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) {
    if rotationalDelta > 0{
      moveDirection = .UP
      game.moveSprite(player: player, moveDirection: moveDirection)

    } else if rotationalDelta < 0{
      moveDirection = .DOWN
      game.moveSprite(player:  player, moveDirection: moveDirection)
    }
  }

  func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) {
    moveDirection = .STOP
    game.moveSprite(player: player, moveDirection: moveDirection)
  }

  override func willActivate() {
    // This method is called when watch view controller is about to be visible to user
    super.willActivate()
  }

  override func didDeactivate() {
    // This method is called when watch view controller is no longer visible
    super.didDeactivate()
  }
}