Horizontal Scrolling in Spritekit (Vs. a Viewcontroller)

Horizontal scrolling in SpriteKit (vs. a viewcontroller)

To do this purely in SpriteKit you essentially create a moveableNode and add all your menu stuff to that node and than you move that node in touchesMoved. You would have to keep track of its position and fiddle with the speed etc.

https://codedump.io/share/etvt4SwQI6iR/1/how-to-create-a-vertical-scrolling-menu-in-spritekit

I think this kind of menu is the rare occasion where it is actually better to use UIKit in SpriteKit, such as UIScrollViews or UICollectionViews.

Trying to replicate them in SpriteKit is a bit tricky, requires some extra code and also doesnt give you the nice scrolling/bounce effect.

You can create a UIColletionView in Spritekit if thats what you are looking for, you just need to subclass. I am using one for my game I am working on as the level select screen.

Create a new swift file

class CustomCollectionView: UICollectionView {

// MARK: - Init
init(frame: CGRect) {
super.init(frame: frame)

/// set up
backgroundColor = UIColor.clearColor()
#if os(iOS)
pagingEnabled = true
#endif
self.frame = frame
delegate = self
dataSource = self
indicatorStyle = .White
scrollEnabled = true
canCancelContentTouches = false

registerClass... // register your cells
}

// MARK: - Delegates
extension CustomCollectionView: UICollectionViewDataSource {

func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 5
}

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 8
}

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
.......
}

Then in your SKScenes you can add the collection View

  weak var collectionView: CustomCollectionView!

collectionView = CustomCollectionView(frame: view!.frame)
view!.addSubview(collectionView)

You will have to read some tutorials so you can get comfortable with UICollectionView, like how to create custom cells and general setUp etc. You will also have to ensure that you remove the collectionView when changing SKScenes

 collectionView.removeFromSuperview()

Is this what you are asking?

How to make a horizontal scrolling character chooser in SpriteKit

You should do well to avoid using UIKit classes in SpriteKit, just because it's just a different beast. Additionally, SpriteKit uses its own game time, so you wouldn't want to rely on some other time if for example your game is paused for whatever reason.

I typically build my own components, which would be how I would approach this challenge as well. You should make a custom SKNode subclass, let's say Picker, the instance of which you embed onto the SKScene. Within that component, you'll handle touches independently from the SKScene and program in tapping, dragging and dropping behavior, natively. SKAction animations will help in snapping the correct "page" selection into place after a drop.

So then, whenever the component detects a tap or a swiped selection (the end of a drag: drop), it sends a signal either to your data or the SKScene parent, telling that the selection should be updated.

I'm sure there might be third-party components around, but I would invest time into this sort of development, if you plan on sticking with SpriteKit. You will typically need to get into the weeds of things, but that's part of the fun :).

I'm happy to help further, if needed.

Displaying Levels with SpriteKit / Views vs. ViewController

Either of your methods will work, but you'll have to decide a way that works best for the specific scenario, so the "right approach" is up to you. I had the same question about six months ago and the following solution has worked flawlessly for me so far:

The Benefits of using SKSpriteNodes in your SKScene

I discovered that you can write a custom SKNode subclass which I named SpriteKitButton that contains custom code and animations that make an SKSpriteNode act like a UIButton for use with SpriteKit, and it allows you to add better looking animations and such to it when it is pressed.

I primarily prefer this approach because the custom sprite buttons are as easy to initialize as a UIButton and will load with the respective SKScene you add them to and disappear with them as well.

This has allowed me to only work with SKScenes instead of having to deal with loading UIViewControllers or UIViews along with loading SKScenes.

Therefore, all you have to do is simply call self.view?.presentScene... to change between your scenes, and you will have sprites that act and feel like buttons in them.

Here's a hint to get you started:

1) Make a new swift file and add the following:

import SpriteKit

class SpriteKitButton: SKNode {

var button: SKSpriteNode!
var buttonUnpressedTexture: SKTexture!
var buttonPressedTexture: SKTexture!
var action: (String) -> Void

init(buttonUnpressedImage: String, buttonPressedImage: String, buttonAction: (String) -> Void) {

//Initialize your button here
}

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

}

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

}

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

}
}

2) Make a new button in your SKScene like so:

    let artboardButton = SpriteKitButton(buttonUnpressedImage: "artboardButtonUnpressed.png", buttonAction: openArtboard)
artboardButton.position = CGPoint(x: ..., y: ...)
artboardButton.zPosition = 0.0
artboardButton.userInteractionEnabled = true
self.addChild(artboardButton)

//Add a function to handle what to do when the button is pressed
func openArtboard() {

}

Use the various touches methods inside of your SKNode subclass to handle what you want the button to do when it is touched. How you handle what the button does when handled is entirely up to you.

You can also use a for loop to load your 20 or so buttons at once.

Be sure to unload the button in willMoveFromView by setting it to nil.

SpriteKit and Scrollable List

I would avoid using UIKit components if possible. When you have a UIView that is parented to UIKit components, it makes it difficult to interact with your SpriteKit components that live in the SKView. It's pretty easy to subclass SKNode and make a "ScrollingNode" that uses gesture recognizers on the SKView to do the scrolling. Here's an example:

https://github.com/JenDobson/SpriteKitScrollingNode

Another great tutorial to take a look at is http://www.raywenderlich.com/44270/sprite-kit-tutorial-how-to-drag-and-drop-sprites. Go to the part at the end on gesture recognizers.

How would you make a horizontal picker view in sprite kit to choose different characters?

Just set up a UISwipeGestureRecognizer in your view. When a swipe is detected, get the direction of the swipe and either display the next or previous sprite with an animation. If you haven't used gesture recognizers documentation is here. Another option is to simply detect swipes generically using touchesBegan, touchesMoved, etc.

How to create a vertical scrolling menu in spritekit?

The second answer as promised, I just figured out the issue.

I recommend to always get the latest version of this code from my gitHub project incase I made changes since this answer, link is at the bottom.

Step 1: Create a new swift file and paste in this code

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

This make a subclass of UIScrollView and sets up the basic properties of it. It than has its own touches method which get passed along to the relevant scene.

Step2: In your relevant scene you want to use it you create a scroll view and moveable node property like so

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

and add them to the scene in 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)

What you do here in line 1 is you init the scroll view helper with you scene dimensions. You also pass along the scene for reference and the moveableNode you created at step 2.
Line 2 is where you set up the content size of the scrollView, in this case its twice as long as the screen height.

Step3: - Add you labels or nodes etc and position them.

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

in this example the label would be on the 2nd page in the scrollView. This is where you have to play around with you labels and positioning.

I recommend that if you have a lot pages in the scroll view and a lot of labels to do the following. Create a SKSpriteNode for each page in the scroll view and make each of them the size of the screen. Call them like page1Node, page2Node etc. You than add all the labels you want for example on the second page to page2Node. The benefit here is that you basically can position all your stuff as usual within page2Node and than just position page2Node in the scrollView.

You are also in luck because using the scrollView vertically (which u said you want) you dont need to do any flipping and reverse positioning.

I made some class func so if you need to disable your scrollView incase you overlay another menu ontop of the scrollView.

CustomScrollView.enable()
CustomScrollView.disable()

And finally do not forget to remove the scroll view from your scene before transitioning to a new one. One of the pains when dealing with UIKit in spritekit.

scrollView?.removeFromSuperView()

For horizontal scrolling simply change the scroll direction on the init method to .horizontal (step 2).

And now the biggest pain is that everything is in reverse when positioning stuff. So the scroll view goes from right to left. So you need to use the scrollView "contentOffset" method to reposition it and basically place all your labels in reverse order from right to left. Using SkNodes again makes this much easier once you understand whats happening.

Hope this helps and sorry for the massive post but as I said it is a bit of a pain in spritekit. Let me know how it goes and if I missed anything.

Project is on gitHub

https://github.com/crashoverride777/SwiftySKScrollView



Related Topics



Leave a reply



Submit