Custom Class Sprite kit - swift

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

Related

How to inherit the SKspritenode and create by self.childNode

I have a problem dealing with inheritance for the skspritenode
I have created a skspritenode in the scence and want to inherit the sksprite to a car class
Car Class
class Car : SKSpriteNode {
var CurrentLocation = 1
}
GameScene
class GameScene: SKScene {
var leftCar = Car()
func setUp() {
leftCar = self.childNode(withName: "leftCar") as! SKSpriteNode
}
}
I dont know what the meaning of Cannot assign value of type 'SKSpriteNode' to Car and how to reslove this error, thankyou for your help
leftCar is of type Car so compiler is complaining to cast childNode as Car instead of SKSpriteNode shown below.
func setUp() {
if let car = self.childNode(withName: "leftCar") as? Car {
leftCar = car
}
}
BTW, you already initialized leftCar in the same GameScene so you can simply use that instead of assigning it again.
Set custom class Car in GameScene.sks for leftCar node as shown in below image,

Casting an SKSpriteNode in an SKReferenceNode

I've build a Scene1 in Xcode's Scene editor. And I've referenced another scene which has animation to this Scene1.
Now, I'm trying to cast-out an SKSpriteNode which is inside an SKReferenceNode.
The name of the SKSpriteNode that I'm trying to cast, on a scene which was references is: "sc01eyelid".
Any suggestions what I might do wrong here?
thank you in advance.
import SpriteKit
import GameplayKit
class Scene1: SKScene {
var misha: SKReferenceNode = SKReferenceNode()
var eyelidForScene1:SKSpriteNode = SKSpriteNode()
override func didMove(to view: SKView) {
castMishaForScene1()
castOutEyelid()
}
//Casting out misha
func castMishaForScene1() {
if let someSpriteNode:SKReferenceNode = self.childNode(withName: "mishaRefNode") as? SKReferenceNode {
misha = someSpriteNode
print("CASTED\(misha)")
}
else {
print("could not cast\(misha)")
}
}
//Casting out eyelid
func castOutEyelid() {
if let someSpriteNode:SKSpriteNode = misha.childNode(withName: "sc01eyelid") as? SKSpriteNode {
eyelidForScene1 = someSpriteNode
print("CASTED\(eyelidForScene1)")
}
else {
print("could not cast\(eyelidForScene1)")
}
}
}
In order to access any Node of the SKRefference one needs to put additional "//" in the withName statement:
if let someSpriteNode:SKSpriteNode = misha.childNode(withName: "//sc01eyelid") as? SKSpriteNode {}
So withName: "//sc01eyelid" would work to access the sc01eyelid Node.
More info here:
https://developer.apple.com/documentation/spritekit/sknode

How can an existing protocol implemented by a delegate be extended with a var?

I've tried many combinations and the problem still remains. I can't figure out why Swift won't compile the following code. I've used multiple variations (using 'where' to constraint protocol, moving setter & getter inside the protocol, etc...) Still no luck. Can you see where the problem is?
// GameScene.swift
import SpriteKit
extension SKSceneDelegate { // adding 'where Self: Game'
// causes err to moves somewhere else
var playerDirection: PlayerDirection { get set } // doesn't like this!
}
class GameScene: SKScene {
override func keyDown(theEvent: NSEvent) {
switch (theEvent.keyCode) {
case 123:
delegate!.playerDirection = .Left;
case 124:
delegate!.playerDirection = .Right;
default:
break
}
}
}
// SomeGame.swift
import Foundation
import SpriteKit
class Game: NSObject, SKSceneDelegate {
var _playerDirection: PlayerDirection = .None
// moving that code to the protocol, compiler can't find _playerDirection
var playerDirection: PlayerDirection {
set {
_playerDirection = newValue
}
get {
return _playerDirection
}
}
lazy var scene: GameScene = {
let scene = GameScene(size: CGSizeMake(CGFloat(100), CGFloat(100)))
scene.delegate = self
return scene
}()
func update(currentTime: NSTimeInterval, forScene scene: SKScene) {
}
}
// PlayerControlComponent.swift
import Foundation
enum PlayerDirection {
case None, Left, Right, Down, Up
}
I think you're approaching your problem from the wrong angle. It looks like what you want is to be able to access playerDirection from the keyDown() function in GameScene. Instead of attempting to make playerDirection a property of the SKSceneDelegate protocol, you should probably be checking to see if the delegate property in GameScene is a Game and, if it is, casting delegate to Game so that the playerDirection property becomes available to you.
You can do that very easily with an if let and the as? operator like this:
override func keyDown(theEvent: NSEvent) {
if let game = delegate as? Game {
switch (theEvent.keyCode) {
case 123:
game.playerDirection = .Left;
case 124:
game.playerDirection = .Right;
default:
break
}
}
}
This is doubly nice because now you're also checking to make sure that delegate actually exists before using it. Forcibly unwrapping it, like you were doing before, could cause a runtime exception if delegate isn't set when before that function is called.

How do edit a sprite based on its name?

So in my game i have a function that spawns coins,they are given the name "coin", Now I have no way to reference the coins,example to kill them or move them.So what I'm trying to do is make a reference to be able to use in my code to just change its zPosition.
Everytime I run my app and have a function run that uses the coinRef [ex. to change the zPosition], the app crashes with the error:
'Thread 1 EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0)'
Heres my code:
let coinRef: SKSpriteNode = self.childNodeWithName("coin")! as! SKSpriteNode
func hideCoins() {
coinRef.zPosition = -1
}
func showCoins() {
coinRef.zPosition = 101
}
func killCoins() {
coinRef.removeFromParent()
}
Looking at what you write
So in my game i have a function that spawns coins,they are given the name "coin"
it looks like there are multiple coins in your scene. As you can imagine a single name coin is not enough to univocally identify more then 1 coin :)
We'll need a way do identity multiple coins.
1. The Coin class
class Coin: SKSpriteNode {
private static var lastID: UInt = 0
let id:UInt
init() {
self.id = Coin.lastID++
let texture = SKTexture(imageNamed: "coin")
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
self.name = "coin"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
As you can see Coin has an internal mechanism to assign a new id to each new instance. You can use this id to reference the coins in your scene.
let coin0 = Coin()
coin0.id // 0
let coin1 = Coin()
coin1.id // 1
let coin2 = Coin()
coin2.id // 2
2. Managing your coins
class GameScene: SKScene {
func retrieveCoin(id:UInt) -> Coin? {
return children.filter { ($0 as? Coin)?.id == id }.first as? Coin
}
func hideCoin(id:UInt) {
retrieveCoin(id)?.hidden = true
}
func showCoin(id:UInt) {
retrieveCoin(id)?.hidden = true
}
func deleteCoin(id:UInt) {
retrieveCoin(id)?.removeFromParent()
}
}
The retrieveCoin method returns (if does exist) a coin with the specified id. Otherwise nil is returned.
The hideCoin and showCoin do change the hidden property to change its visibility.
Finally deleteCoin remove from the scene the Coin with the specified id.
Try this. Initialise coinRef before the didMoveToView function, and then give coinRef its value in the didMoveToView function.
class scene : SKScene {
let coinRef: SKSpriteNode = SKSpriteNode()
override func didMoveToView(view: SKView) {
coinRef: SKSpriteNode = self.childNodeWithName("coin")! as! SKSpriteNode
}
func hideCoins() {
coinRef.zPosition = -1
}
func showCoins() {
coinRef.zPosition = 101
}
func killCoins() {
coinRef.removeFromParent()
}
}

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