WatchKit 在 SpriteKit 游戏中移动一个 SimpleSpriteNode
WatchKit Move a SimpleSpriteNode in SpriteKit Game
我想知道是否有人有办法使用 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()
}
}
欢迎来到 SO!
好吧,有很多东西要解压,所以买点爆米花吧……我认为你走在正确的轨道上,大多数情况下你需要在出错时检查 nil。
首先,你的接口控制器是错误的,也是我关心的部分。在这里,您只是实例化新的 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 有很多问题,所以我不会像上面那样一一列举。看看我做了什么然后问我是否有任何问题。基本上,您从一开始就注定要使用此函数,因为您是从接口控制器调用此方法时使用的 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
}
}
这里故事的真正寓意是检查是否为零。如果您一直使用 someOptional?.someMethod()
,那么您可能无法轻松确定是否实际调用了 someMethod()
。因此,您不知道问题是否出在调用逻辑、方法或对象不存在等
强制展开在生产代码中是不受欢迎的,但 IMO 在刚开始时非常有价值——因为它可以帮助您快速识别错误。
稍后,您可以开始使用 if let
和 guard
之类的东西来帮助检查 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")
这是两个完整的文件。请注意,我必须注释掉一些内容才能使其在我的项目中运行:
游戏场景:
// 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
}
}
}
接口控制器:
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()
}
}
我想知道是否有人有办法使用 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()
}
}
欢迎来到 SO!
好吧,有很多东西要解压,所以买点爆米花吧……我认为你走在正确的轨道上,大多数情况下你需要在出错时检查 nil。
首先,你的接口控制器是错误的,也是我关心的部分。在这里,您只是实例化新的 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 有很多问题,所以我不会像上面那样一一列举。看看我做了什么然后问我是否有任何问题。基本上,您从一开始就注定要使用此函数,因为您是从接口控制器调用此方法时使用的 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
}
}
这里故事的真正寓意是检查是否为零。如果您一直使用 someOptional?.someMethod()
,那么您可能无法轻松确定是否实际调用了 someMethod()
。因此,您不知道问题是否出在调用逻辑、方法或对象不存在等
强制展开在生产代码中是不受欢迎的,但 IMO 在刚开始时非常有价值——因为它可以帮助您快速识别错误。
稍后,您可以开始使用 if let
和 guard
之类的东西来帮助检查 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")
这是两个完整的文件。请注意,我必须注释掉一些内容才能使其在我的项目中运行:
游戏场景:
// 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
}
}
}
接口控制器:
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()
}
}