使用 spritekit swift 在 swift 中为游戏创建主菜单

Posted

技术标签:

【中文标题】使用 spritekit swift 在 swift 中为游戏创建主菜单【英文标题】:Create a main menu for game in swift using spritekit swift 【发布时间】:2017-03-13 01:57:59 【问题描述】:

我是 swift 新手,并且有一个 SKScene,我在其中创建了一个游戏。但是,我似乎无法弄清楚如何构建菜单。如果看到创建另一个视图控制器或另一个 SKScene 的两种解决方案,但它们都非常混乱和复杂。我愿意使用这些方法中的任何一种或任何其他方法,有没有人有任何技巧来解决这个问题。一些代码会有所帮助。感谢您的帮助。

【问题讨论】:

能否请您绘制您希望实现的 UI 图表。它可能只是一个带有方框的框图。 您可以使用UIStackview 来实现此目的。当我到达电脑时,将尝试发布代码。在此期间,您可以阅读有关 UIStackview 的信息。 @user1046037 如何在 SKScene 中放置堆栈视图 很抱歉我之前没有使用场景套件。 没关系,我创建了一个复杂的解决方法 【参考方案1】:

Sprite-Kit 中有多种获取菜单的方法。

通常人们画一些SKLabelNodeSKSpriteNode来构建菜单声音或制作特定的SKNode来构建这种结构。

但我想听听关于StackView 的关于cmets 的建议。 我们知道StackView 是一个UIKit 元素:

提供用于布置视图集合的简化界面 在一列或一行中。

所以,我们可以构建一个包含所有菜单声音的垂直StackView(P.S. 下面的代码显示了一个简单的标签集合,您可以根据需要自定义您的StackView 视图):

import SpriteKit
import UIKit
protocol StackViewDelegate: class 
    func didTapOnView(at index: Int)

class GameMenuView: UIStackView 
    weak var delegate: StackViewDelegate?
    override init(frame: CGRect) 
        super.init(frame: frame)
        self.axis = .vertical
        self.distribution = .fillEqually
        self.alignment = .fill
        self.spacing = 5
        self.isUserInteractionEnabled = true
        //set up a label
        for i in 1...5 
            let label = UILabel()
            label.text = "Menu voice \(i)"
            label.textColor = UIColor.white
            label.backgroundColor = UIColor.blue
            label.textAlignment = .center
            label.tag = i
            self.addArrangedSubview(label)
        
        configureTapGestures()
    
    required init(coder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    
    private func configureTapGestures() 
        arrangedSubviews.forEach  view in
            view.isUserInteractionEnabled = true
            let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapOnView))
            view.addGestureRecognizer(tapGesture)
        
    
    func didTapOnView(_ gestureRecognizer: UIGestureRecognizer) 
        if let index = arrangedSubviews.index(of: gestureRecognizer.view!) 
            delegate?.didTapOnView(at: index)
        
    

class GameScene: SKScene, StackViewDelegate 
    var gameMenuView = GameMenuView()
    private var label : SKLabelNode?
    override func didMove(to view: SKView) 
        self.label = self.childNode(withName: "//helloLabel") as? SKLabelNode
        if let label = self.label 
            label.alpha = 0.0
            label.run(SKAction.fadeIn(withDuration: 2.0))
        
        // Menu setup with stackView
        gameMenuView.frame=CGRect(x:20,y:50,width:280,height:200)
        view.addSubview(gameMenuView)
        gameMenuView.delegate = self
    
    func didTapOnView(at index: Int) 
        switch index 
        case 0: print("tapped voice 1")
        case 1: print("tapped voice 2")
        case 2: print("tapped voice 3")
        case 3: print("tapped voice 4")
        case 4: print("tapped voice 5")
        default:break
        
    

输出

【讨论】:

在我提出问题并得到答案的这段时间里,我对我的问题做了一个简单的工作。我创建了一个与场景大小相同的 SKSprite 节点,并将其移出“屏幕显示框”。然后每当我想调用它时,我就把它移到“屏幕显示框”中。这个解决方案有什么我应该知道的问题吗? 隐藏菜单不是一种优雅的方式,您可以简单地更改 alpha 或 isHidden 属性,zPosition 属性,但这不是错误的方法..跨度> 我只是让它滑入动画,但我担心的是它会减慢我的游戏速度。 如果您的菜单中的简单精灵很少,请不要担心【参考方案2】:

方法:

下面提到的代码使用UIStackview 创建2 个部分。 您可以使用类似的方法。

输出:

代码:

class ViewController: UIViewController 

    private let baseSection = UIStackView()
    private let section1    = UIStackView()

    private let titleLabel  = UILabel()
    private let button1     = UIButton(type: .custom)
    private let button2     = UIButton(type: .custom)
    private let button3     = UIButton(type: .custom)
    

    //MARK: Load view
    override func viewDidLoad() 
        super.viewDidLoad()

        setupViews()
    

    
    //MARK: Setup views
    private func setupViews() 
        
        setupBaseSection()
        setupTitleLabel()
        setupButton1()
        setupSection1()
        setupButton2()
        setupButton3()
    
    
    private func setupTitleLabel() 
        
        titleLabel.text      = "Swirl"
        titleLabel.font      = UIFont.preferredFont(forTextStyle: .headline)
        titleLabel.textColor = #colorLiteral(red: 0.8156862745, green: 0.9137254902, blue: 0.1647058824, alpha: 1)
        
        baseSection.addArrangedSubview(titleLabel)
    
    
    private func setupButton1() 
        
        button1.backgroundColor = #colorLiteral(red: 0.9098039216, green: 0.168627451, blue: 0.3921568627, alpha: 1)
        
        baseSection.addArrangedSubview(button1)
        
        button1.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.2).isActive = true
        button1.heightAnchor.constraint(equalToConstant: 50).isActive = true
    
    
    private func setupButton2() 
        
        button2.backgroundColor = #colorLiteral(red: 0.8156862745, green: 0.9137254902, blue: 0.1647058824, alpha: 1)
        
        section1.addArrangedSubview(button2)
        
        button2.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.1).isActive = true
        button2.heightAnchor.constraint(equalToConstant: 50).isActive = true
    
    
    private func setupButton3() 
        
        button3.backgroundColor = #colorLiteral(red: 0.8156862745, green: 0.9137254902, blue: 0.1647058824, alpha: 1)
        
        section1.addArrangedSubview(button3)
        
        button3.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.2).isActive = true
        button3.heightAnchor.constraint(equalToConstant: 50).isActive = true
    
    
    //MARKL Set up stack views
    
    private func setupBaseSection() 

        baseSection.axis          = .vertical
        baseSection.distribution  = .fill
        baseSection.alignment     = .center
        baseSection.spacing       = 10

        
        baseSection.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(baseSection)
        
        baseSection.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        baseSection.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        baseSection.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    
    
    private func setupSection1() 
        
        section1.axis         = .horizontal
        section1.distribution = .equalSpacing
        section1.alignment    = .fill
        section1.spacing      = 20
        
        baseSection.addArrangedSubview(section1)
    

【讨论】:

为什么必须是 SSKcene ?难道它不能作为一个单独的视图控制器覆盖在你的场景上吗?视图控制器不需要覆盖整个屏幕,甚至可以是屏幕的一部分。 这是真的,但我需要知道如何从视图控制器更改为 SKScene 在现有内容之上展示一个视图控制器。你甚至可以有一个透明的背景【参考方案3】:

我体验过将 UIButtons 和 UILabels 添加到 SpriteKit 场景中,这对于定位它们的问题会变得非常技术性。由于 UI 对象位于视图上,而不是直接位于 SpriteKit 场景上。您可以将 SKSpriteNode 用作按钮,将 SKLabelNode 用作标题,用于菜单场景。

Sprite 套件场景放置在 UIView 上,并根据您定义的缩放模式进行缩放。 Apple 的默认缩放模式 .aspectFill 不需要调整 Sprite 套件对象在不同手机设备屏幕尺寸上的定位。

这是一个 SKSpriteNode 的自定义类,具有与按钮相同的功能。

 import Foundation
 import SpriteKit

 class ButtonLabelNode : SKSpriteNode 

let buttonPressed: () -> ()

init(texture: SKTexture?, color: UIColor, size: CGSize, text: String, buttonPressed: @escaping () -> ()) 

    self.buttonPressed = buttonPressed

    super.init(texture: texture, color: color, size: size)

    let label = SKLabelNode(fontNamed: "Futura")
    label.fontSize = 50
    label.fontColor = SKColor.red
    label.position = CGPoint.init(x: 0.0, y: 0.0)
    label.zPosition = 1
    label.verticalAlignmentMode = .center
    label.text = text
    self.addChild(label)

    self.isUserInteractionEnabled = true



override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 

    self.alpha = 0.8



override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 

    self.alpha = 1.0

  buttonPressed()


required init?(coder aDecoder: NSCoder) 
    fatalError("init(coder:) has not been implemented")


 

当在 SpriteNode 上开始触摸时,alpha 减小到 0.8,并在触摸结束时回到 1.0,给它与 UIButton 相同的视觉效果。在覆盖的函数“touchesEnded”中,有一个函数在每次按下按钮时都会被调用,该函数被添加到初始化程序中,并且可以在你的游戏场景中进行初始化。

override func didMove(to view: SKView) 

    let labelNode = LabelNode(texture: nil, color: .white, size: CGSize.init(width: 200, height: 100), text: "Play", buttonPressed: playButton)
    labelNode.position = CGPoint(x: self.frame.midX, y: self.frame.midY)

    self.addChild(labelNode)

    

    func playButton() 
    
    print("play")
     

您可以创建任意数量的此类实例,并赋予它们自己的功能,因为添加的初始化函数对其自己的实例是唯一的。与为类提供所需的协议方法不同,因为这将影响类的所有实例。

【讨论】:

以上是关于使用 spritekit swift 在 swift 中为游戏创建主菜单的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式使用 Swift 在 Spritekit 中滚动视图(无情节提要)

如何在 Swift 和 SpriteKit 中使用 UISwipeGestureRecognizer 而不是 touchesMoved?

PhysicsBody 使用 SpriteKit 和 Swift 为零

SpriteKit, Swift 2.0 - 在游戏地图上生成对象

在使用 Swift 的 SpriteKit 中的 didBeginContact(日志)中未检测到 contactTestBitMask

使用 spritekit swift 在 swift 中为游戏创建主菜单