Trouble subclassing SCNScene - swift

I've been trying to subclass SCNScene as that seems like the best place to keep my scene related logic. Now I'm not sure wether that's reccomended so my first question is - Should I be subclassing SCNScene, and if not why not? The documentation seems to suggest it's common but I've read comments online that suggest I shouldn't subclass it. Scrap that, I was looking at the documentation for SKScene. The SCNScene class reference makes no mention of subclassing.
Assuming it's ok to structure my game that way, here's my progress
// GameScene.swift
import Foundation
import SceneKit
class GameScene: SCNScene {
lazy var enityManager: BREntityManager = {
return BREntityManager(scene: self)
}()
override init() {
print("GameScene init")
super.init()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func someMethod() {
// Here's where I plan to setup the GameplayKit stuff
// for the scene
print("someMethod called")
}
}
Note: I'm using lazy var as per the answer to this question
In my view controller, i'm trying to use GameScene like this
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// create a new scene
let scene = GameScene(named: "art.scnassets/game.scn")!
// retrieve the SCNView
let scnView = self.view as! SCNView
// set the scene to the view
scnView.scene = scene
// Should print "someMethod called"
scene.someMethod()
}
}
However, the call to GameScene.someMethod() triggers an EXEC_BAD_ACCESS error.
Also, if I omit the call to GameScene.someMethod, the scene loads correctly, but the overridden initializer in GameScene doesn't appear to be called.
I'm not sure what's going on here. It's clear there's something about subclassing in Swift that I've not understood. Or perhaps there' some aspect of order in which things are meant to run that I've missed.

Should I be subclassing SCNScene, and if not why not?
No, you don't need to subclass, and you're probably better off not subclassing. SCNScene is more like basic data/model classes (NSString, UIImage, etc) than like behavior/controller/view classes (UIViewController, UIControl, etc). That is, it's a general description of or container for something (in this case, a 3D scene) and doesn't really provide behavior. Since there's not much in the way of behavior, there's not much opportunity to override behavior with a subclass. (Nor is the class designed around subclass entry points meant to be overridden, like viewDidLoad and whatnot.)
While it's possible to subclass SCNScene, you don't gain much from doing so, and some APIs might not work as you expect...
However, the call to GameScene.someMethod() triggers an EXEC_BAD_ACCESS error.
Also, if I omit the call to GameScene.someMethod, the scene loads correctly, but the overridden initializer in GameScene doesn't appear to be called.
A .scn file is actually an NSKeyedArchiver archive, which means that the objects inside it have the same classes that were used to create it. when you call init(named:) on your SCNScene subclass, the superclass' implementation eventually calls through to NSKeyedUnarchiver unarchiveObjectWithData: to load the archive. Absent some unarchiver mangling, that method will instantiate a SCNScene, not an instance of your subclass.
Because you don't get an instance of your subclass, your subclass initializers aren't called, and attempting to call your subclass' methods results in a runtime error.
Aside: Why is SpriteKit different from SceneKit here? SKScene is a bit of an odd duck in that it's neither a pure model class nor a pure controller class. This is why you see a lot of projects, including the Xcode template, using an SKScene subclass. There are drawbacks to this approach, however — if you don't plan carefully, it gets too easy to wed your game logic tightly to your game data, which makes expanding your game (say, to multiple levels) require tedious coding instead of data editing. There's a WWDC 2014 session on this topic that's well worth watching.
So, what to do?
You have a few choices here...
Don't subclass. Keep your game logic in a separate class. This doesn't necessarily have to be a view controller class — you could have one or more Game objects that are owned by your view controller or app delegate. (This makes especially good sense if your game logic transitions between or manipulates the content of multiple scenes.)
Subclass, but arrange to put the stuff from an unarchived SCNScene into an instance of your subclass — instantiate your subclass, load an SCNScene, then move all children of its root node into your subclass' root node.
Subclass, but force NSKeyedUnarchiver to load your subclass instead of SCNScene. (You can do this with class name mappings.)
Of these, #1 is probably the best.

tl;dr: Extend SCNNode to generate your scene programmatically.
This started as a comment on Rickster's excellent answer but I could see it getting too long. I taught a course this quarter that spent a substantial amount of time on SceneKit. We subclassed SCNScene all the time and didn't run into any trouble.
However, after spending an hour just now reviewing the code I presented, the code the students wrote, and a ton of other sample code (from Apple and from other developers), I would not subclass SCNScene again.
None of the Apple sample code subclasses SCNScene. That should be a very strong clue. Handling the serialization (initWithCoder and the read from file) is a recurring question on StackOverflow. That's a second strong clue that you don't want to go there.
In all of the code I reviewed that subclassed SCNScene, the reason to do it was to generate the scene programmatically. That's Rickster's choice #2. But none of that code really needs to be on SCNScene. If you're building a bunch of nodes in particular configurations, adding lighting and look-at constraints, and setting up materials, it feels like you're building an SCNScene. But really you're building a tree of SCNNode instances.
So I think in hindsight, choice #2 does not provide a good enough reason to subclass. All of that construction can be done by writing a generator function, in an extension to SCNNode.
It's tempting to subclass SCNNode for this custom tree. Don't do it. You'll be able to save it and read it just fine (assuming you write the appropriate NSSecureCoding functions). But you will be unable to open your scene with the Xcode Scene Editor, because the Scene Editor doesn't know how to unarchive and instantiate your custom SCNNode subclasses.

Related

Creating custom class and adding to my viewcontroller from storyboad?

I'm just copying codes from an old project. I found something strange and I don't know how to do that. I just want to create that "Chart View Model" like below images. I also need an outlet too.
There is also a corresponding class.
class ChartModel: NSObject {
// .....
}
All I want to know is the purpose of using model in storyboads and how to do that.
Have you ever put a gesture recognizer into a storyboard scene? This is exactly parallel. The loading of the nib instantiates the gesture recognizer and attaches it to a view, so that you don't have to do that in code. That's what's happening here.
Any NSObject subclass can be instantiated as a nib object. Find the Object in the library:
Drag it from the library right into the scene; select it and change its class in the Identity inspector to the desired class (ChartModel).
Now you face the problem of what will happen to this instance when it is created at nib-loading time. On iOS, it will vanish in a puff of smoke unless someone else retains it. The usual solution is that you've got some other nib object with an outlet to this Object. Now, when the nib loads, the Object is instantiated and assigned to the corresponding property in the other nib object. That is what your outlet does:
#IBOutlet var chartViewModel : ChartModel!
Okay, but so far, that is exactly equivalent to saying
var chartViewModel = ChartModel()
It's just that, instead of instantiating the ChartModel in code, we instantiate it by the loading of the nib.
So why do that in the first place? Why instantiate this ChartModel from the nib instead of in code? It makes sense only if a ChartModel itself has outlets that can be configured in the nib. You didn't show us that (you only showed the first line of the declaration of class ChartModel) so it's impossible to say more about what the actual purpose was in this case.

Implement TouchBar functionality in Xcode Playgrounds

How can I implement a NSTouchBar in using only Xcode Playgrounds? I realize that NSTouchBar, and its accompanying methods including makeTouchBar() are contained within the NSResponder class, which is the superclass of NSView, NSViewController, NSWindow, and SKView (a subclass of NSView.)
It is for this reason that I'm a little unsure how to approach access the TouchBar. There are so many subclasses of NSResponder I'm unsure as to which one is the correct one. And to my knowledge I cannot even access all of them, including the NSWindow for the Playground, which I suspect may be the one I need to access.
My current setup is:
let containerView = SKView()
PlaygroundPage.current.liveView = containerView
let containterScene = SKScene()
containerView.presentScene(containterScene)
And in checking the responder chain, for the SKView I get:
<SKScene> name:'(null)' frame:{{-250, -250}, {500, 500}} anchor:{0.5, 0.5}
And for the SKScene:
<SKView: 0x7fba36037a00>
<NSView: 0x7fba32d6fe80>
<PlaygroundViewBridgeService: 0x7fba32d569e0>
<NSNextStepFrame: 0x7fba32d76e20>
<NSWindow: 0x7fba32e177e0>
For some reason, the SKScene and SKView are not within the same chain, and I can't seem to access any responder higher than the SKView. Is there a way to extend the functionality of the already existing NSWindow?
Another problem is that many tutorials on using the TouchBar require access to the AppDelegate, which I don't think I have in Xcode Storyboards.
Any help implementing the TouchBar in Storyboards would be greatly appreciated.
The solution is actually quite simple. Create a custom subclass of NSViewController and implement the TouchBar override functions within it.
Then implement the above code:
let containerView = CustomSKView()
let containterScene = CustomSKScene()
containerView.presentScene(containerScene)
With the following additions.
let containerViewController = CustomNSViewController()
containerViewController.view = containerView
PlaygroundPage.current.liveView = containerViewController.view
This sets your custom NSViewController within the responder chain, allowing it to take care of the Touch Bar. There is also no need to worry about the App Delegate.

Examples of Delegates in Swift [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have been trying to learn how delegation with protocols work. I understood everything, but I can't think of when to use delegation other than when using table views and possibly scroll views.
In general, when is delegation used?
What is Delegation?
First of all, you should know that Delegation Pattern is not exclusive for iOS world:
In software engineering, the delegation pattern is a design pattern in
object-oriented programming that allows object composition to achieve
the same code reuse as inheritance.
But working with delegation in the iOS world is so common, I assume that you can see many of classes that provide a delegation/datasource for giving the ability to provide properties or behaviors for the used instance. It is one of main mechanisms of how objects talk to each other in CocoaTouch.
Alternatives:
However, delegation is not the only way to let objects talk to each other in iOS, you might want to know that there are:
NotificationCenter.
KVO (Key-Value Observing).
Completion handlers/Callbacks (using closures).
Target-Action.
Remark: in case if you are interested in comparing between them, you might want to check the following articles:
Communication Patterns.
When to Use Delegation, Notification, or Observation in iOS.
Delegates vs Observers.
When to use Delegation?
So, the question is: "So why should I use delegation instead of those options?"
I will try to make it simple; I would suggest the use of delegation when you have one to one relationship between two objects. Just to make it clearer, the goal of talking a little bit about the NotificationCenter is to try to make sense when delegations are used:
NotificationCenter represents one to many relationship; Simply, it works as: posting (notifying) a notification on a specific event and observing (get notified about) this notification -- it could be observed anywhere else; Logically, that's what one to many relationship means. It is a representation of the Observer Pattern.
How to Apply Delegation?
For the purpose of simplifying, I would mention it as steps:
Knowing the requirements: Each delegate has its own rules, listed in the delegate protocol which is a set of method signatures that you should implement for conforming this delegation.
Conforming for the delegation: it is simply letting your class to be a delegate, by marking it. For instance: class ViewController: UIViewController, UITableViewDelegate {}.
Connecting the delegate object: Marking your class to be a delegate is not enough, you need to make sure that the object you want to be confirmed by your class to give the required job to your class.
Implementing the requirements: Finally, your class have to implement all required methods listed in the delegate protocol.
For Example
Does it sounds a little confusing? What about a real-world example?
Consider the following scenario:
Imagine that you are building an application related to playing audios. Some of the viewControllers should have a view of an audio player. In the simplest case, we assume that it should have a play/pause button and another button for, let's say, showing a playlist somehow, regardless of how it may look like.
So far so good, the audio player view has its separated UIView class and .xib file; it should be added as a subview in any desired viewController.
Now, how can you add functionality to both of the buttons for each viewController? You might think: "Simply, I will add an IBAction in the view class and that's it", at first look, it might sound ok, but after re-thinking a little bit, you will realize that it will not be applicable if you are trying to handle the event of tapping the button at the controller layer; To make it clear, what if each viewController implemented different functionality when tapping the buttons in the audio player view? For example: tapping the playlist in "A" viewController will display a tableView, but tapping it in the "B" viewController will display a picker.
Well, let's apply Delegation to this issue:
The "#" comments represents the steps of "How to Apply Delegation?" section.
Audio Player View:
// # 1: here is the protocol for creating the delegation
protocol AudioPlayerDelegate: class {
func playPauseDidTap()
func playlistDidTap()
}
class AudioPlayerView: UIView {
//MARK:- IBOutlets
#IBOutlet weak private var btnPlayPause: UIButton!
#IBOutlet weak private var btnPlaylist: UIButton!
// MARK:- Delegate
weak var delegate: AudioPlayerDelegate?
// IBActions
#IBAction private func playPauseTapped(_ sender: AnyObject) {
delegate?.playPauseDidTap()
}
#IBAction private func playlistTapped(_ sender: AnyObject) {
delegate?.playlistDidTap()
}
}
View Controller:
class ViewController: UIViewController {
var audioPlayer: AudioPlayerView?
// MARK:- Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
audioPlayer = AudioPlayerView()
// # 3: the "AudioPlayerView" instance delegate will implemented by my class "ViewController"
audioPlayer?.delegate = self
}
}
// # 2: "ViewController" will implement "AudioPlayerDelegate":
extension ViewController: AudioPlayerDelegate {
// # 4: "ViewController" implements "AudioPlayerDelegate" requirments:
func playPauseDidTap() {
print("play/pause tapped!!")
}
func playlistDidTap() {
// note that is should do a different behavior in each viewController...
print("list tapped!!")
}
}
Quick Tip:
As one of the most popular examples of using delegation is Passing Data Back between View Controllers.
Delegation is used when you want to pass some information or state of object A to another object B. Usually object B is the object that created object A.
I will list some situations where you would use delegation.
Yes you're right. table views and scroll views use delegates because they want to tell whoever is interested (usuall your view controller) that "someone selected a row!" or "someone scrolled the scroll view!". Not only do scroll views and table views use delegates, UITextField and UIDatePicker and a lot of other views use delegates too!
View Controllers also have delegates. For example, UIImagePickerController. The reason why is roughly the same as above - because the UIImagePickerController wants to tell you messages like "an image has been selected!". Another example would be UIPopoverControllerDelegate. This delegate tells you things like "the popover has been dismissed!"
Other classes that use delegates include CLLocationManager. This delegate tells you things like "the user's location has been detected" or "failed to detect the user's location".
You can use delegation in your code when a certain view controller of yours wants to send messages to other view controllers. If it is a settings view controller, it might send messages like "the font size setting has been changed!" and the view controller that cares about the font size setting changing will know and change the font size of a label or something.
Delegate Method to Selectionimages
Create baseClass And Insert the following code
Create Another class then insert code
Delegation in IOS world and mostly in MVC (Model View Controller)
is a way for the View to talk to the Controller and it's called "blind communication"
and delegation means to give the " leading stick " to another object ( doesn't really care who is taking over but usually the Controller) to control over components that the view can not control on it's own (remember it's only a view) or doesn't own
to make it more simple ....
the controller can talk to a view but the view can not talk to the controller without Delegation

Difference between GameViewController and SKScenes

I've been developing a game using SpriteKit and Swift but I seem to be having trouble determining what the real differences are between the GameViewController and any one of my SKScenes. I'm trying to understand the differences because I want to implement a GameCenter or local leaderboard into my game but in all the tutorials I find (like this one:Game Center Leaderboards! (Swift 2 in Xcode) ) they have all the logic in GameViewController as they are working with single view apps. I'm having trouble understanding the relation when I read the docs, so any help would be great. Ultimately, I want to be able to display and push data to and from GameCenter in one of my scenes such as GameOverScene. Thanks for any help!
Here is some good info to start with:
Diagram of what happens each frame in SK:
So you see, the SKScene is the class with all of the fun stuff like Nodes and Actions, and is where everything (important to you) happens. You can generate these scenes through the Editor, but then you probably need to make a new .swift file to go with it (as each scene can have its own logic).
The editor is just a 'shortcut' to initializing a bunch of stuff, and honestly, you can make complete games with little code (but you very quickly find out that you want more)
So in this code, where you declare GameScene or PauseScreen (which are basically just class declarations, that inherit from SKScene), you quickly find this line talking about something that ISNT a scene:
override func didMoveToView(view: SKView)
.. it's calling a SKView... what is that, and where did it come from?
(Read about SKView here, and look at its inheritance):
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKView/index.html#//apple_ref/occ/cl/SKView
We find this SKView declaration in the GameViewController file, (which is just a class), notice that it's the same as the regular iOS apps mostly, as it inherits UIViewController:
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
Again, that method is declared in GameViewController.swift, which is basically just this:
class GameViewController: UIViewController
So how does all of this relate to iOS apps and SpriteKit? Well, they are all mashed on top of each other:
IOS app anatomy:
Basically, from right to left, you have the Window, which is (correct me if wrong) the AppDelegate, then the ViewController, then your View, which has all of the cool stuff in it (Storyboards sit inside of the View, just as SKScenes sit inside of the View.... Labels, Nodes, or Buttons, all sit inside of their respective classes ((the view)))
It's all a big sandwich of inheritance.
Check out the Apple websites for more info.
https://developer.apple.com/library/safari/documentation/UserExperience/Conceptual/MobileHIG/ContentViews.html#//apple_ref/doc/uid/TP40006556-CH13-SW1
https://developer.apple.com/spritekit/
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SpriteKitFramework_Ref/
https://developer.apple.com/library/safari/documentation/UserExperience/Conceptual/MobileHIG/Anatomy.html
Basically, everything is an Class inherited from a class inherited from a class and so on, so on... It can get messy. You can also see these inheritances in Xcode by CMD+clicking on them, which will jump you to the source file.
Goodluck with your studies and adventures in SpriteKit :)
You should only have a single GameViewController in your game. SKScenes are the scenes that the game transitions in between an to.
For example, the home menu screen? That's an SKScene. The main gameplay? That's an SKScene. The gameover screen? That's an SKScene.
The GameViewController initializes the entire view that the game will be maintained in, so the view. The SKScenes are just scenes that are placed on top of the view. You should be looking at a tutorial that uses SKScenes.
Here's how to make game center work as of the latest Swift 2.2.
Add this function anywhere inside the GameViewController class, and then just call it right after super.viewDidLoad().
func authenticateLocalPlayer() {
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController, error) -> Void in
if (viewController != nil) {
self.presentViewController(viewController!, animated: true, completion: nil)
}
else {
print((GKLocalPlayer.localPlayer().authenticated))
}
}
}
Add the following functions in your SKScene class file. Don't forget to import GameKit. Just call showLeader() whenever you want the leaderboard to be displayed.
func showLeader() {
let viewControllerVar = self.view?.window?.rootViewController
let gKGCViewController = GKGameCenterViewController()
gKGCViewController.gameCenterDelegate = self
viewControllerVar?.presentViewController(gKGCViewController, animated: true, completion: nil)
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
And this is a sample I have of how the score is saved to game center.
func saveHighscore(gameScore: Int) {
print("Player has been authenticated.")
if GKLocalPlayer.localPlayer().authenticated {
let scoreReporter = GKScore(leaderboardIdentifier: "YOUR_LEADERBOARD_ID")
scoreReporter.value = Int64(gameScore)
let scoreArray: [GKScore] = [scoreReporter]
GKScore.reportScores(scoreArray, withCompletionHandler: {error -> Void in
if error != nil {
print("An error has occured: \(error)")
}
})
}
}
It all depends about how you are designing your app, and what technologies you want to use.
If you are looking to build an app in 100% Sprite Kit, then you treat your UIViewController as a shell that holds your Sprite Kit app. The only time you should be touching this is when you need to do things that the SpriteKit scene shouldn't be doing, like creating gesture controls and what not.
However, there are uses to having multiple view controllers with sprite kit elements. Perhaps you are making a business application, and decide to include a little game to go with it.
IMO the best way to think about it in terms of web design is think of your View controller as your HTML page, and think of your Scene as your flash/silverlight/unity/etc game player that you embed in the website. Sometimes you want that player to be full screen, some times you do not, it comes down to the design of the application. When in full screen, we do not need any other components, so the player can do all the work. But what if we attach a how to guide link on the page. We wouldn't want this in game, we want this outside of it. This link will then open up a new page, not associated with the old page, and has no use for the game player components.
Now for your situation with Game Center, it gets more complicated. Game Center was built before Sprite Kit came to existence, so all of its functionality is built on UIKit. But Game Center also allows for customization, so you do not have to use the UIKit features of it. Of course, you will have to do all of the work then in displaying the information inside of your scene with Sprite Kit objects.
To make life easiest for you, you would include all of the built in code needed into your View Controller, then what you do is create a delegate that the scene knows about, and assign your view controller to this delegate. Now Game Scene can access any element of that view controller that you allow, like presenting leader boards or passing up leader boards. Check out this tutorial in its entirety, it will help you learn all you will need to achieve what you want.
https://www.raywenderlich.com/115300/swift-2-tutorial-part-3-tuples-protocols-delegates-and-table-views
In MVC the controller acts as a coordinator, a bit like the conductor in an orchestra. My preference is that scenes just do the one thing they were designed for i.e. implement game play. When a scene is completed, the final task is to notify the controller (using delegate pattern) that the scene is complete. It is then up to the controller to decide what happens next i.e. transition to next scene or game over.

How can I create a .swift file for my new .sks file?

When using Xcode, I've faced the annoying problem, which is Xcode always crashes when I click .SKS files. I have raised questions here, and even in Apple developer forum, as well as searched for the solutions on the Internet... but it is hopeless.
Because I am making a game with many scenes, so if I can't use SpriteKit editor to interact with the scenes in an easy way, I want to know how can I interact with my scenes by coding their .swift files.
So the question is, for example, when I create a file "EndScene.sks", how can I create a "EndScene.swift", which links with my .sks file?
Thank you very much!
Create a new swift file and name it the same as your .sks file. Let's say you have a .sks file called MainMenu.sks. You'd create a swift file called MainMenu.swift. Inside that file, you'll want to create a Main Menu class that inherits from SKScene.
import SpriteKit
class MainMenu: SKScene {
}
Inside there is where you'll put all your code. The key is, as you said, linking this to the .sks file.
When you go to present your scene, you'll instantiate the class and associate it with the .sks file.
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = MainMenu(fileNamed:"MainMenu") {
let skView = self.view as! SKView
// Some settings applied to the scene and view
// ... code ...
skView.presentScene(scene)
}
}
//... other code
}
Note the line let scene = MainMenu(fileNamed:"MainMenu"). That's where you are instantiating the class you created in MainMenu.swift. So, technically, MainMenu() is the .swift file and fileNamed: "MainMenu" is the .sks file. You can technically put any .sks file into the init call and it'll render that scene.
Imagine having a game with all the logic for a maze runner. You could build all the game logic in a class called MazeScene and just make a bunch of .sks files, each with a different maze. You could then so something like, MazeScene(fileNamed: "MazeOne") or MazeScene(fileNamed: "MazeThree").
For the documentation on this, SKScene inherits from SKNode so you'll find the documentation for init(fileNamed:) here
That should get you going.
So instead of commenting I have to wander because StackOverflow won't let me comment, how much of this is the same in swift 3? And when you were doing this in the UIViewController you said
if let scene = MainMenu(fileNamed:"MainMenu") {
let skView = self.view as! SKView
// Some settings applied to the scene and view
// ... code ...
skView.presentScene(scene)
}
What do you mean by
// ... code ...
Do we put all of the code we would put in the SKScene class in there instead? I thought the whole point was to have a separate SKScene class. And when I try it like that the SKScene class I have doesn't load. Thanks for any clarification and sorry I have to answer instead of comment. It's pretty stupid that you need 50 reputation to leave a comment I don't see any point in that. Anyway, Thanks!