WatchKit present a SKScene from another unexpectedly found nil while unwrapping an optional value - sprite-kit

I want to present a SpriteKit scene from another SKScene in my WatchKit app.
Here is my code:
class GameScene: SKScene, SKPhysicsContactDelegate {
var scene2: Scene2!
#IBOutlet var skInterface: WKInterfaceSKScene!
override func sceneDidLoad() {
physicsWorld.contactDelegate = self
....
changeScene()
}
func changeScene() {
if let scene = Scene2(fileNamed: "Scene2") {
scene2 = scene
scene.scaleMode = .aspectFill
self.skInterface.presentScene(scene)
self.skInterface.preferredFramesPerSecond = 30
}
}
But when i try to present other scene i get this error:
"fatal error: unexpectedly found nil while unwrapping an Optional value"
How can i solve this problem?

Related

How do I make the back of an object invisible with a mask object?

I would like to hide the back of the object when AR projecting. You see, the object is sort of deepened in the wall. Can I make some kind of mask object so that the object is not visible?
import UIKit
import RealityKit
class ViewController: UIViewController {
#IBOutlet var arView: ARView!
override func viewDidLoad() {
super.viewDidLoad()
// Load the "Box" scene from the "Experience" Reality File
let ARscene1 = try! Experience.load_1()
let ARscene2 = try! Experience.load_2()
let ARscene3 = try! Experience.load_3() //that scene, object named "mask"
// Add the box anchor to the scene
arView.scene.anchors.append(ARscene1)
arView.scene.anchors.append(ARscene2)
arView.scene.anchors.append(ARscene3)
}
}
Here you can see how my model looks like.
Here's a little macOS project showing you how to setup Occlusion Material in RealityKit:
import Cocoa
import RealityKit
class GameViewController: NSViewController {
#IBOutlet var arView: ARView!
let anchor = try! TwoCubes.loadTwoObjects()
override func awakeFromNib() {
arView.environment.background = .color(.black)
let boxEntity1: Entity = anchor.invisible!.children[0]
var boxComponent1: ModelComponent = boxEntity1.components[ModelComponent]!.self
let boxEntity2: Entity = anchor.visible!.children[0]
var boxComponent2: ModelComponent = boxEntity2.components[ModelComponent]!.self
let material1 = OcclusionMaterial() // Material hiding other objects behind it
var material2 = SimpleMaterial()
material2.baseColor = .color(.orange)
boxComponent1.materials = [material1]
boxComponent2.materials = [material2]
anchor.invisible!.components.set(boxComponent1)
anchor.visible!.components.set(boxComponent2)
arView.scene.anchors.append(anchor)
}
}

Particles Speed Up When Recording A Video Using ARVideoKit

We have an ARKit & SceneKit app that records videos using ARVideoKit pod (link). Inside our scene we have a fire particle. Fire is playing at a slow speed. However, when you start recording a video the fire particle speeds up. I can't figure out why the fire particle is speeding up. Please see the video (here) to see the issue.
Here's a sample project which you can use to test the issue: Project
I would appreciate if anyone can explain why this is happening.
Edit 1: Below is my ViewController Code
import UIKit
import ARKit
import SceneKit
import ARVideoKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet var sceneView: ARSCNView!
#IBOutlet weak var photoButton: UIButton!
#IBOutlet weak var videoButton: UIButton!
#IBOutlet weak var takeVideoButtonPressedLabel: UILabel!
// ARVideoKit Variables
var recorder : RecordAR?
var takenImage: UIImage?
var takenVideoAtURL : URL?
let recordingQueue = DispatchQueue(label: "recordingThread", attributes: .concurrent)
var arTrackingConfig = ARWorldTrackingConfiguration()
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.scene = SCNScene(named: "art.scnassets/fire.scn")!
self.resetTracking()
}
func resetTracking() {
arTrackingConfig = ARWorldTrackingConfiguration()
arTrackingConfig.isLightEstimationEnabled = true
sceneView.session.run(arTrackingConfig, options: [.resetTracking,
.removeExistingAnchors])
}
}
// MARK: - ARVideoKit Implementation
extension ViewController {
#IBAction func takePhoto(_ sender: UIButton) {
// Do Nothing
}
#IBAction func takeVideo(_ sender: UIButton) {
setupCamera()
startRecording()
}
func setupCamera() {
recorder = RecordAR(ARSceneKit: sceneView)
recorder?.prepare(arTrackingConfig)
}
func startRecording() {
takeVideoButtonPressedLabel.isHidden = false
recordingQueue.async {
self.recorder?.record(forDuration: 5) { path in
self.takenVideoAtURL = path
DispatchQueue.main.async {
self.takeVideoButtonPressedLabel.isHidden = true
}
}
}
}
}
Edit 2: Added sample project for testing.
While getting the image from SCNRenderer using snapshot method pass 0 as time , this way force rendering will not happen and animations will be smoother. In your case particles will not speed up

How to present interstitials to an SKScene in SpriteKit?

I´m continuously trying to implement an interstitial to a view in my project but I always get the same error: "Cannot convert value of type 'Start' to expected argument type 'UIViewController'" when I make the call defined below.
Does someone know how to implement them in SprtieKit because I only found resources for Swift?
Here is my code:
import SpriteKit
import GameKit
import GoogleMobileAds
class Start: SKScene, GADInterstitialDelegate{
var interstitial: GADInterstitial!
override func didMove(to view: SKView) {
interstitial = createAndLoadInterstitial()
interstitial.delegate = self
if self.interstitial.isReady {
NotificationCenter.default.addObserver(self, selector: Selector(("quitToLevel:")), name: NSNotification.Name(rawValue: "quitToLevelID"), object: nil)
interstitial.present(fromRootViewController: self) //->Error: Cannot convert value of type 'Start' to expected argument type 'UIViewController'
}else{
print("add didn´t load")
}
}
func createAndLoadInterstitial() -> GADInterstitial {
let interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/4411468910")
interstitial.delegate = self
interstitial.load(GADRequest())
return interstitial
}
func interstitialDidDismissScreen(_ ad: GADInterstitial) {
interstitial = createAndLoadInterstitial()
}
}

Custom Class Sprite kit

I'm trying to give my main character a Custom Class so that I can define all his behaviors inside the defined class.
I already did something like this, by using protocols:
protocol CustomNodeEvents{
func didMoveToScene()
}
This protocol is in the GameScene file, before the class starts.
Then I call the function like this in the GameScene class:
override func didMove(to view: SKView) {
if let customnode = player as? CustomNodeEvents{
customnode.didMoveToScene()
}
}
Where the player is defined as:
player = self.childNode(withName: "//player") as! SKSpriteNode
also inside the didMove(to view: SKView) function.
Now I created another file (my custom class file) and I write:
import SpriteKit
class Custom: SKSpriteNode, CustomNodeEvents{
func didMoveToScene() {
print("It Worked")
}
}
I don't have any errors but it doesn't actually run the block of code inside my Custom class (in the GameScene.sks file I already attached the class to the player).
My question is how can I make it work?
And the second question is, is this the best way to define a Custom Class and "connect" it with other classes?
EDIT:
class GameScene: SKScene {
var player: SKSpriteNode!
override func didMove(to view: SKView) {
player = self.childNode(withName: "//player") as! SKSpriteNode
if let customnode = player as? CustomNodeEvents{
customnode.didMoveToScene()
}
Not sure where the problem is, but this code works for me:
import SpriteKit
protocol CustomNodeEvents
{
func didMoveToScene()
}
class Custom : SKSpriteNode,CustomNodeEvents
{
func didMoveToScene()
{
print("It Worked")
}
}
class GameScene:SKScene{
var player : SKSpriteNode!
override func didMove(to view: SKView) {
player = self.childNode(withName: "//player") as! SKSpriteNode
if let customnode = player as? CustomNodeEvents
{
customnode.didMoveToScene()
}
else
{
print("Error creating node")
}
}
}
I can only conclude that there is an issue in the sks file, and the player we are grabbing is not the player you are looking for, or the player is not of class Custom. (Note I placed the player sprite at the top most level of the scene)
Seems like you are casting player as a SpriteNode and then later trying to upcast it to a CustomNodeEvents protocol. Assuming your sprite custom class is set in the .sks file as you say, SpriteKit should be creating an instance of your custom class so you can just do:
if let customNode = self.childNode(withName: "//player") as! Custom {
customNode.didMoveToScene()
}

error in viewcontroller (lldb) when presenting another view

when I try to present an uiactivityviewcontroller or just another viewcontroller, I get an lldb error without any further explanation
EDITED:
Game starts with GameMenuScene, when play is clicked it will move to GameScene:
class GameMenuScene: SKScene {
weak var weakGameVC: GameViewController?
if nodeAtPoint.name == "play"{
NSUserDefaults.standardUserDefaults().setInteger(score+1, forKey: "currentLevel")
NSUserDefaults.standardUserDefaults().synchronize()
self.removeAllActions()
self.removeAllChildren()
var scene1:SKScene = GameScene(size: self.size)
scene1.weakGameVC = self ##Updated : error: SKSCene does not have a member named "weakGameVC"
self.view?.presentScene(scene1)
}
}
GameScene:
Here is the GameViewController(as you can see, first scene is GameMenuScene:
import UIKit
import SpriteKit
import AVFoundation
import Social
class GameViewController: UIViewController, UITextFieldDelegate{
weak var weakGameVC: GameViewController? ##Updated
var player:SKSpriteNode = SKSpriteNode()
var scene:GameMenuScene!
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as SKView
skView.multipleTouchEnabled = false
scene = GameMenuScene(size: skView.bounds.size)
scene.weakGameVC = self ##Updated
//scene.scaleMode = SKSceneScaleMode.ResizeFill
skView.showsFPS = true
skView.showsNodeCount = true
skView.presentScene(scene)
}
func shareButton() {
var myShare = "aa"
let activityVC:UIActivityViewController = UIActivityViewController(activityItems: ["aa"], applicationActivities: nil)
presentViewController(activityVC, animated: true, completion: nil)
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
And here the GameScene, where the gameplay is:
import SpriteKit
import Social
class GameScene: SKScene, SKPhysicsContactDelegate {
weak var weakGameVC: GameViewController?
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location: CGPoint! = touch.locationInNode(self)
let nodeAtPoint = self.nodeAtPoint(location)
if (nodeAtPoint.name != nil) {
if nodeAtPoint.name == "share"{
println("1")
if let gvc = weakGameVC {
println("2")
gvc.shareButton()
}
}
}
}
}
}
It doesn't work because in touchBegan method you create new object GameViewController:
var gameViewController = GameViewController()
The gameViewController is not added to the view hierarchy (it's not presented) and after that you call shareButton() method which wants to present another view (share view) on not presented view (game view).
The solution should be that you handle the touch (touchesBegan) in the same class where you have shareButton() by calling shareButton() on the self not gameViewController.shareButton().
Or create delegate method from scene to view controller, if touch happened in scene call delegate method and handle it in view controller to present the share view
// EXTENDED - Solution with weak reference to game view controller.
In GameScene add weak reference to gameVC:
weak var weakGameVC: GameViewController?
and later in the same file:
if nodeAtPoint.name == "share" {
if let gvc = weakGameVC {
gvc.shareButton()
}
}
In GameViewController file after you create game scene you have to add something like that:
gameScene.weakGameVC = self