一尘不染

如何在Spritekit中创建垂直滚动菜单?

swift

我希望在游戏中(SpriteKit)中创建带有按钮和图像的商店,但我需要使这些物品能够滚动,以便玩家可以上下滚动商店(就像UITableView一样,但每个商店中都有多个SKSpriteNodes和SKLabelNodes细胞)。知道如何在SpriteKit中做到这一点吗?


阅读 268

收藏
2020-07-07

共1个答案

一尘不染

正如所答应的第二个答案,我只是想出了问题。

我建议始终从gitHub项目中获取此代码的最新版本,以防由于此答案以来我进行了更改,链接位于底部。

第1步:创建一个新的swift文件并粘贴此代码

import SpriteKit

/// Scroll direction
enum ScrollDirection {
    case vertical // cases start with small letters as I am following Swift 3 guildlines.
    case horizontal
}

class CustomScrollView: UIScrollView {

// MARK: - Static Properties

/// Touches allowed
static var disabledTouches = false

/// Scroll view
private static var scrollView: UIScrollView!

// MARK: - Properties

/// Current scene
private let currentScene: SKScene

/// Moveable node
private let moveableNode: SKNode

/// Scroll direction
private let scrollDirection: ScrollDirection

/// Touched nodes
private var nodesTouched = [AnyObject]()

// MARK: - Init
init(frame: CGRect, scene: SKScene, moveableNode: SKNode) {
    self.currentScene = scene
    self.moveableNode = moveableNode
    self.scrollDirection = scrollDirection
    super.init(frame: frame)

    CustomScrollView.scrollView = self
    self.frame = frame
    delegate = self
    indicatorStyle = .White
    scrollEnabled = true
    userInteractionEnabled = true
    //canCancelContentTouches = false
    //self.minimumZoomScale = 1
    //self.maximumZoomScale = 3

    if scrollDirection == .horizontal {
        let flip = CGAffineTransformMakeScale(-1,-1)
        transform = flip
    }
}

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

// MARK: - Touches
extension CustomScrollView {

/// Began
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

    for touch in touches {
        let location = touch.locationInNode(currentScene)

        guard !CustomScrollView.disabledTouches else { return }

        /// Call touches began in current scene
        currentScene.touchesBegan(touches, withEvent: event)

        /// Call touches began in all touched nodes in the current scene
        nodesTouched = currentScene.nodesAtPoint(location)
        for node in nodesTouched {
            node.touchesBegan(touches, withEvent: event)
        }
    }
}

/// Moved
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {

    for touch in touches {
        let location = touch.locationInNode(currentScene)

        guard !CustomScrollView.disabledTouches else { return }

        /// Call touches moved in current scene
        currentScene.touchesMoved(touches, withEvent: event)

        /// Call touches moved in all touched nodes in the current scene
        nodesTouched = currentScene.nodesAtPoint(location)
        for node in nodesTouched {
            node.touchesMoved(touches, withEvent: event)
        }
    }
}

/// Ended
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {

    for touch in touches {
        let location = touch.locationInNode(currentScene)

        guard !CustomScrollView.disabledTouches else { return }

        /// Call touches ended in current scene
        currentScene.touchesEnded(touches, withEvent: event)

        /// Call touches ended in all touched nodes in the current scene
        nodesTouched = currentScene.nodesAtPoint(location)
        for node in nodesTouched {
            node.touchesEnded(touches, withEvent: event)
        }
    }
}

/// Cancelled
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {

    for touch in touches! {
        let location = touch.locationInNode(currentScene)

        guard !CustomScrollView.disabledTouches else { return }

        /// Call touches cancelled in current scene
        currentScene.touchesCancelled(touches, withEvent: event)

        /// Call touches cancelled in all touched nodes in the current scene
        nodesTouched = currentScene.nodesAtPoint(location)
        for node in nodesTouched {
            node.touchesCancelled(touches, withEvent: event)
        }
     }
   }
}

// MARK: - Touch Controls
extension CustomScrollView {

     /// Disable
    class func disable() {
        CustomScrollView.scrollView?.userInteractionEnabled = false
        CustomScrollView.disabledTouches = true
    }

    /// Enable
    class func enable() {
        CustomScrollView.scrollView?.userInteractionEnabled = true
        CustomScrollView.disabledTouches = false
    }
}

// MARK: - Delegates
extension CustomScrollView: UIScrollViewDelegate {

    func scrollViewDidScroll(scrollView: UIScrollView) {

        if scrollDirection == .horizontal {
            moveableNode.position.x = scrollView.contentOffset.x
        } else {
            moveableNode.position.y = scrollView.contentOffset.y
        }
    }
}

这是UIScrollView的子类,并设置了它的基本属性。然后它具有自己的触摸方法,该方法会传递到相关场景。

步骤2:在您要使用的相关场景中,创建一个滚动视图和可移动节点属性,如下所示

weak var scrollView: CustomScrollView!
let moveableNode = SKNode()

并将它们添加到didMoveToView中的场景中

scrollView = CustomScrollView(frame: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height), scene: self, moveableNode: moveableNode, scrollDirection: .vertical)
scrollView.contentSize = CGSizeMake(self.frame.size.width, self.frame.size.height * 2)
view?.addSubview(scrollView)


addChild(moveableNode)

您在第1行中所做的就是使用场景尺寸初始化滚动视图帮助器。您还将传递场景作为参考,并传递在步骤2中创建的moveableNode。第2行是您设置scrollView内容大小的地方,在这种情况下,它是屏幕高度的两倍。

第3步:-添加标签或节点等并放置它们。

label1.position.y = CGRectGetMidY(self.frame) - self.frame.size.height
moveableNode.addChild(label1)

在此示例中,标签将位于scrollView的第二页上。这是您必须摆弄标签和位置的地方。

我建议如果滚动视图中有很多页面,并且有很多标签,请执行以下操作。为滚动视图中的每个页面创建一个SKSpriteNode,并使每个页面的大小与屏幕大小相同。称它们为page1Node,page2Node等。然后将您想要的所有标签(例如,第二页上的所有标签)添加到page2Node。这样做的好处是,您基本上可以像往常一样将所有内容放置在page2Node中,而不仅仅是将page2Node放置在scrollView中。

您也很幸运,因为垂直使用scrollView(您说您想要的)不需要进行任何翻转和反向定位。

我做了一些类功能,所以如果您需要禁用scrollView的话,以防您在scrollView顶部覆盖了另一个菜单。

CustomScrollView.enable()
CustomScrollView.disable()

最后,不要忘记在过渡到新场景之前从场景中删除滚动视图。在spritekit中处理UIKit时的痛苦之一。

scrollView?.removeFromSuperView()

对于水平滚动,只需将init方法上的滚动方向更改为.horizo​​ntal(步骤2)。

而现在最大的痛苦是,在放置东西时一切都是相反的。因此,滚动视图从右到左。因此,您需要使用scrollView“
contentOffset”方法重新定位它,并且基本上将所有标签从右到左以相反的顺序放置。一旦了解发生了什么,再次使用SkNodes将使此操作变得更加容易。

希望这会有所帮助,并为大量的帖子感到抱歉,但是正如我所说,spritekit有点痛苦。让我知道进展如何,如果我错过了任何事情。

项目在gitHub上

https://github.com/crashoverride777/SwiftySKScrollView

2020-07-07