我正在为我正在构建的精灵工具包应用程序创建主菜单。在整个项目中,我使用SKScenes来保持我的水平和实际的游戏玩法。但是,现在我需要一个主菜单,其中包含“Play”,“Levels”,“Shop”等按钮......但是,我现在添加按钮的方式感觉不太舒服,就像这样:
let currentButton = SKSpriteNode(imageNamed: button) // Create the SKSpriteNode that holds the button
self.addChild(currentButton) // Add that SKSpriteNode to the SKScene
我检查按钮的触摸如下:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
for node in self.nodes(at: touchLocation) {
guard let nodeName = node.name else {
continue
}
if nodeName == ButtonLabel.Play.rawValue {
DispatchQueue.main.asyncAfter(deadline: .now()) {
let transition = SKTransition.reveal(with: .left, duration: 1)
self.view?.presentScene(self.initialLevel, transition: transition)
self.initialLevel.loadStartingLevel()
}
return
}
if nodeName == ButtonLabel.Levels.rawValue {
slideOut()
}
}
}
但是,我不知道这是否有效。 我正在考虑使用UIButtons,但为此我必须使用UIView?
或者我可以将UIButtons添加到SKView (我真的没有区分SKView,SKScene和UIView)推荐什么菜单?
答案 0 :(得分:5)
我完全赞同@Whirlwind,为你的按钮创建一个单独的类来处理你的工作。我不认为@ElTomato的建议是正确的建议。如果您创建一个包含按钮的图像,则您对这些按钮的放置,大小,外观和按钮状态没有灵活性。
这是一个非常简单的按钮类,它是SKSpriteNode的子类。它使用委托将信息发送回父级(例如已按下哪个按钮),并为您提供简单的状态更改(单击时变小,释放后恢复正常大小)
import Foundation
import SpriteKit
protocol ButtonDelegate: class {
func buttonClicked(sender: Button)
}
class Button: SKSpriteNode {
//weak so that you don't create a strong circular reference with the parent
weak var delegate: ButtonDelegate!
override init(texture: SKTexture?, color: SKColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
setScale(0.9)
self.delegate.buttonClicked(sender: self)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
setScale(1.0)
}
}
此按钮可以通过2种方式实例化。您可以在场景编辑器中创建它的实例,或在代码中创建实例。
class MenuScene: SKScene, ButtonDelegate {
private var button = Button()
override func didMove(to view: SKView) {
if let button = self.childNode(withName: "button") as? Button {
self.button = button
button.delegate = self
}
let button2 = Button(texture: nil, color: .magenta, size: CGSize(width: 200, height: 100))
button2.name = "button2"
button2.position = CGPoint(x: 0, y: 300)
button2.delegate = self
addChild(button2)
}
}
func buttonClicked(sender: Button) {
print("you clicked the button named \(sender.name!)")
}
你必须记住使场景符合代表
class MenuScene: SKScene, ButtonDelegate
func buttonClicked(sender: Button) {
print("you clicked the button named \(sender.name!)")
}
答案 1 :(得分:1)
对于简单场景,您正在做的事情很好,实际上是首选,因为您可以使用.SKS文件。
但是,如果你有一个复杂的场景我想做的是子类化Sprite,然后覆盖该节点的touchesBegan。
这是我在所有项目中使用的节点......这是一个简单的&#34;开关&#34;按钮。我使用&#34;指针&#34;通过我自己制作的自定义引用类到布尔值,这样这个节点就不需要关心你的其他场景,节点等 - 它只是将Bool的值改为其他代码位的做他们想做的事情:
public final class Reference<T> { var value: T; init(_ value: T) { self.value = value } }
// MARK: - Toggler:
public final class Toggler: SKLabelNode {
private var refBool: Reference<Bool>
var value: Bool { return refBool.value }
var labelName: String
/*
var offText = ""
var onText = ""
*/
func toggleOn() {
refBool.value = true
text = labelName + ": on"
}
func toggleOff() {
refBool.value = false
text = labelName + ": off"
}
/*init(offText: String, onText: String, refBool: Reference<Bool>) {
ref = refBool
super.init(fontNamed: "Chalkduster")
if refBool.value { toggleOn() } else { toggleOff() }
isUserInteractionEnabled = true
}
*/
init(labelName: String, refBool: Reference<Bool>) {
self.refBool = refBool
self.labelName = labelName
super.init(fontNamed: "Chalkduster")
isUserInteractionEnabled = true
self.refBool = refBool
self.labelName = labelName
if refBool.value { toggleOn() } else { toggleOff() }
}
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if refBool.value { toggleOff() } else { toggleOn() }
}
public required init?(coder aDecoder: NSCoder) { fatalError("") }
override init() {
self.refBool = Reference<Bool>(false)
self.labelName = "ERROR"
super.init()
}
};
这是一个更精细的按钮,而不是说点击它时只运行一些代码的东西。
重要的是,如果你走这条路,那么你需要确保将节点.isUserInteractionEnabled
设置为true
,否则它将不会接收到触摸输入。
根据你正在做的事情的另一个建议是将逻辑与行动分开:
// Outside of touches func:
func touchPlay() {
// Play code
}
func touchExit() {
// Exit code
}
// In touches began:
guard let name = node.name else { return }
switch name {
case "play": touchPlay()
case "exit": touchExit()
default:()
}
PS:
以下是如何使用Toggler的一个非常基本的示例:
class Scene: SKScene {
let spinnyNode = SKSpriteNode(color: .blue, size: CGSize(width: 50, height: 50))
// This is the reference type instance that will be stored inside of our Toggler instance:
var shouldSpin = Reference<Bool>(true)
override func didMove(to view: SKView) {
addChild(spinnyNode)
spinnyNode.run(.repeatForever(.rotate(byAngle: 3, duration: 1)))
let toggleSpin = Toggler(labelName: "Toggle Spin", refBool: shouldSpin)
addChild(toggleSpin)
toggleSpin.position.y += 100
}
override func update(_ currentTime: TimeInterval) {
if shouldSpin.value == true {
spinnyNode.isPaused = false
} else if shouldSpin.value == false {
spinnyNode.isPaused = true
}
}
}