2017-07-13 7 views
1

Ich würde gerne wissen, ob jemand eine Möglichkeit hat, einen SKSpriteNode in SpriteKit Watch Spiel mit WKCrownDelegate zu bewegen. entweder in Y-Richtung oder X-RichtungWatchKit Move einen SimpleSpriteNode in SpriteKit Spiel

Ich hoffe, dies hilft anderen, die mit WatchKit beginnen.

Das ist mein 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 
    } 
} 

Das ist mein 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 
    } 
    } 
} 

Dies ist mein 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() 
    } 
} 
+0

Wenn Sie Ihre Stellvertretung Code schreiben, kann ich Ihnen zeigen, wie es zu einem SKNode bespannen auf. – Fluidity

+0

Ich habe es am besten aktualisiert Ich könnte hoffen, dass dies hilft und danke für die Hilfe. @Flüssigkeit –

Antwort

0

Willkommen bei SO!

Ok, es gibt eine Menge zu entpacken also etwas Popcorn ... Ich denke, dass Sie hier auf dem richtigen Weg sind, meistens müssen Sie auf Null prüfen, wenn Sie Fehler bekommen.

Erstens, das ist falsch in Ihrem Schnittstellencontroller, und der Teil, der mich betroffen. Hier werden nur neue GameScene-Instanzen instanziiert, die vollständig von der gameScene-Instanz getrennt sind, die von Ihrem Schnittstellen-Controller einige Zeilen entfernt erstellt wurde. Dann, waren Sie die Krone delegieren Funktionen auf diese völlig leer gameScenes Senden .:

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! 

Ich reparierte es, indem Sie die tatsächlichen gameScene Zuweisung an den Eigenschaften verwenden möchten (also können sie von der Krone Delegat verwendet werden) .

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 

Dies wurde auch den neuen Code in Ihre Krone Delegierten zu vertreten geändert:

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

In addPlayer Sie dies taten:

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

... und ich repariere es durch Vertauschen der Linien.

Persönlich mag ich folgendes tun, um sicherzustellen, dass keine kleinen Fehler gemacht:

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

moveSprite mit ihm ein paar Probleme hatte, so werde ich aufzählen sie nicht als Ich habe oben getan. Überprüfe, was ich getan habe, dann frag mich, ob du irgendwelche Fragen hast. Im Grunde genommen warst du von Anfang an mit dieser Funktion verdammt, weil du diese Methode vom Interface-Controller aus mit den ausgefuchsten player Werten aufgerufen hast, die alle null waren.

Auch die .applyImpulse gab mir ziemlich schlechte Kontrollen, so änderte ich es auf eine einfache Einstellung von .position. Es gibt immer noch ein kleines Problem mit dem Segeln, bevor der Spieler stoppt, aber das kann in einer anderen Frage behandelt werden :) (beachte, dass ich das nur am Simulator getestet habe ... ist möglicherweise kein Problem auf dem Gerät).

Auch ich hasse Fehler, die durch Rechtschreibfehler in Strings verursacht werden, also konvertierte ich dies zu einem enum für Sie.

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 
    } 
} 

Die wirkliche Moral der Geschichte ist hier zu -Check für null. Wenn Sie einfach someOptional?.someMethod() die ganze Zeit verwenden, dann werden Sie wahrscheinlich nicht in der Lage sein, leicht zu bestimmen, ob someMethod() tatsächlich aufgerufen wird oder nicht .. so wissen Sie nicht, ob das Problem mit der aufrufenden Logik ist, die Methode, oder mit dem Objekt nicht vorhanden, und etc.

Force Unwrapping ist in Produktionscode verpönt, aber IMO ist es äußerst wertvoll beim ersten Start - weil es Ihnen hilft, Fehler schnell zu identifizieren.

Später, können Sie beginnen, Dinge wie if let und guard zu helfen, überprüfen für null verwenden, ohne Ihre Programme abstürzt, sondern dass mehr Unordnung und Komplexität zu Ihrem Code hinzufügt, wenn Sie versuchen, nur die Grundlagen einer neuen API zu lernen und Sprache.

Und als letzter Tipp, versuchen Sie nicht hartcodierte Zeichenfolgen zu verwenden, wann immer möglich: sie in eine ENUM setzen oder eine Konstante, wie ich in Ihrem Code haben:

// 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") 


Hier sind die beiden Dateien in ihrer Gesamtheit .. beachten sie, dass ich ein paar Dinge kommentieren sie musste, um es in meinem Projekt zu arbeiten:


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() 
    } 
} 
Verwandte Themen