SpriteKit - SpriteNode never shows - swift

I am attempting to learn SpriteKit. I want to add a rectangle/square to the scene in code; but the item never shows;
All I want to do is add a white square to the screen, but find that it never adds.
In a breakpoint, I notice that didMove() never seems to get called.
What am I doing wrong?
class GameScene: SKScene {
override func didMove(to view: SKView) {
let item = SKSpriteNode(color: .white, size: CGSize(width: 150, height: 200))
item.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
self.addChild(item)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
update
I did not change the view controller generated by xcode
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
There is no GameScene.sks, I deleted this file as I do not want to use sks files

If a breakpoint in didMove(to view:) is never reached, then definitely your ViewController doesn't present the scene for some reason. Can you open your ViewController's file (e.g. GameViewController.swift) and see if you have any of those lines there (or anything to that effect):
let viewSize = UIScreen.main.bounds.size
let scene = GameScene(size: viewSize)
let skView = self.view as! SKView
skView.presentScene(scene)

Turns out view controller was not presenting the scene and changing it to this, following code posted by #Stoyan worked and I saw my sprite showing
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
let viewSize = UIScreen.main.bounds.size
let scene = GameScene(size: viewSize)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.presentScene(scene)
}

Related

SpriteKit Main Menu button issue

Wondering if you could help, I created a main menu with a background and button node. When I tap on the PLAY button, the game does not navigate to my GameScene and instead calls my print statement. Here is the code below:
import Foundation
import SpriteKit
import GameplayKit
class MainMenu: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
background()
playButton()
}
func background()
{
let back = SKSpriteNode(imageNamed: "MainMenu")
back.anchorPoint = CGPoint(x: 0, y: 0)
back.zPosition = 1
back.size = CGSize(width: frame.width,height: frame.height)
back.name = "Background"
addChild(back)
}
func playButton()
{
let button = SKSpriteNode(imageNamed: "button")
button.zPosition = 2
button.name = "Play Button"
button.position = CGPoint(x: frame.width/2, y: frame.height/2)
button.setScale(0.3)
addChild(button)
}
func loadGame(){
guard let skView = self.view as SKView? else{
print("Could not get Skview")
return
}
guard let scene = GameScene(fileNamed: "GameScene") else {
print("Error Getting GameScene")
return
}
//let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
skView.showsPhysics = true
scene.scaleMode = .aspectFill
skView.presentScene(scene)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else{
return
}
let touchLoation = touch.location(in: self)
let touchNodes = nodes(at: touchLoation)
let firstTouchedNode = atPoint(touchLoation).name
print(firstTouchedNode)
if firstTouchedNode == "Play Button"{
loadGame()
}
}
}
func loadGame(){
guard let skView = self.view as SKView? else{
print("Could not get Skview")
return
}
var scene:SKScene = GameScene(size: self.size)
}
//let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
skView.showsPhysics = true
scene.scaleMode = .aspectFill
var transition:SKTransition = SKTransition.fadeWithDuration(1)
self.view?.presentScene(scene, transition: transition)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let node = self.atPoint(location)
if node.name == "Play Button"{
//when playbutton is pressed execute code here
loadGame()
}
}

How to prevent back button from dissapearing?

I am creating a doodle page with an "X" as the close/dismiss button.
I also have a "clear" button to remove the doodles that have been made.
My issue is that when I press clear, even the X button disappears, how do I prevent this from happening?
This is my current app.
This is my Doodle class which allows me to draw on the view.
import UIKit
class DoodleView: UIView {
var lineColor:UIColor!
var lineWidth:CGFloat!
var path:UIBezierPath!
var touchPoint:CGPoint!
var startingPoint:CGPoint!
override func layoutSubviews() {
self.clipsToBounds = true
self.isMultipleTouchEnabled = false
lineColor = UIColor.white
lineWidth = 10
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
startingPoint = touch?.location(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
touchPoint = touch?.location(in: self)
path = UIBezierPath()
path.move(to: startingPoint)
path.addLine(to: touchPoint)
startingPoint = touchPoint
drawShapeLayer()
}
func drawShapeLayer() {
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = lineColor.cgColor
shapeLayer.lineWidth = lineWidth
shapeLayer.fillColor = UIColor.clear.cgColor
self.layer.addSublayer(shapeLayer)
self.setNeedsDisplay()
}
func clearCanvas() {
path.removeAllPoints()
self.layer.sublayers = nil
self.setNeedsDisplay()
}
}
This is the class controlling the view controller.
import UIKit
class DoodlePageViewController: UIViewController {
#IBOutlet weak var doodleView: DoodleView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func clearDoodle(_ sender: Any) {
doodleView.clearCanvas()
}
#IBAction func CloseDoodlePage(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
Without really seeing how you declared the buttons, I can only guess that when you call the function clearCanvas(), the X button is part of the sublayer in doodle view since you are setting self.layer.sublayers = nil hence making it also dissapear.
Make sure that the X button is on top of the doodle view when you create it.
You are clearing all the subviews out of your view when you call self.layer.sublayers = nil
you either need to re-add the 'x' view back into the hierarchy. I am assuming using your function drawShapeLayer() is the 'x' ??
so it would be
func clearCanvas() {
path.removeAllPoints()
self.layer.sublayers = nil
drawShapeLayer()
self.setNeedsDisplay()
}

Could not cast value of type 'Pong.MenuVC' (0x1072ea808) to 'Pong.GameViewController'

I have built an app using Xcode and swift 5.
Every time I click the "Easy", "Medium", "Hard" or "2 Player" button I get an error:
Could not cast value of type 'Pong.MenuVC' (0x1072ea808) to
'Pong.GameViewController'
Does anyone know how to fix it?
Thank you for your help.
Code of MenuVC:
enum gameType {
case easy
case medium
case hard
case player2
}
class MenuVC : UIViewController {
#IBAction func Player2(_ sender: Any) {
moveToGame(game: .player2)
}
#IBAction func Easy(_ sender: Any) {
moveToGame(game: .easy)
}
#IBAction func Medium(_ sender: Any) {
moveToGame(game: .medium)
}
#IBAction func Hard(_ sender: Any) {
moveToGame(game: .hard)
}
```
**Code with Thread 1: signal SIGABRT:**
```swift
func moveToGame(game : gameType) {
let gameVC = self.storyboard?.instantiateViewController(withIdentifier: "gameVC") as! GameViewController
currentGameType = game
self.navigationController?.pushViewController(gameVC, animated: true)
}
}
Code of GameViewController:
import UIKit
import SpriteKit
import GameplayKit
var currentGameType = gameType.medium
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
scene.size = view.bounds.size
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
In your storyboard the id gameVC is of type MenuVC not GameViewController so change class name of the vc
let gameVC = self.storyboard?.instantiateViewController(withIdentifier: "gameVC") as! GameViewController

swift/scenekit problems getting touch events from SCNScene and overlaySKScene

Good afternoon, I'm trying to figure out how to get touch notifications from an SCNNode & a SKSpriteNode from an SCNScene overlayed with a SKScene.
import UIKit
import SceneKit
class GameViewController: UIViewController {
var scnView:SCNView!
var scnScene:SCNScene!
var sprite: spritekitHUD!
var cameraNode: SCNNode!
var shape: SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
setupScene()
}
func setupScene() {
scnView = self.view as! SCNView
scnView.delegate = self
scnView.allowsCameraControl = true
scnScene = SCNScene(named: "art.scnassets/scene.scn")
scnView.scene = scnScene
sprite=spritekitHUD(size: self.view.bounds.size, game: self)
scnView.overlaySKScene=sprite
cameraNode = scnScene.rootNode.childNode(withName: "camera",
recursively: true)!
shape=scnScene.rootNode.childNode(withName: "shape", recursively: true)
shape.name="ThreeDShape"
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
let touch = touches.first!
let location = touch.location(in: scnView)
let hitResults = scnView.hitTest(location, options: nil)
if let result = hitResults.first {
handleTouchFor(node: result.node)
}
}
func handleTouchFor(node: SCNNode) {
if node.name == "ThreeDShape" {
print("SCNNode Touched")
}
}
}
This is my Spritekit overlay scene
import Foundation
import SpriteKit
class spritekitHUD: SKScene{
var game:GameViewController!
var shapeNode: SKSpriteNode!
init(size: CGSize, game: GameViewController){
super.init(size: size)
self.backgroundColor = UIColor.white
let spriteSize = size.width/12
self.shapeNode= SKSpriteNode(imageNamed: "shapeNode")
self.shapeNode.size = CGSize(width: spriteSize, height: spriteSize)
self.shapeNode.position = CGPoint(x: spriteSize + 8, y: spriteSize + 8)
self.shapeNode.name="test"
self.game=game
self.addChild(self.pauseNode)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch=touches.first else{
return
}
let location=touch.location(in: self)
if self.atPoint(location).name=="test" {
print("Spritekit node pressed")
}
}
}
so with this I can successfully get notifications that my spritenode has been touched on my overlaySKScene but I cant figure out how to get a notification that my SCNode has been touched. If you cant have 2 touchesbegan functions does anyone have any ideas how I can handle the 3d events with 2d events at the same time?
Thanks for your help!!
If you want to use an SKScene overlay of an SCNView for user controls, (eg you want to implement a button in the SKScene overlay that "consumes" the touch), but also have touches that don't hit the controls to pass through and register in the underlying SCNView, you have to do this: set isUserInteractionEnabled to false on the SKScene overlay itself, but then to true on any individual elements within that overlay that you'd like to act as buttons.
let overlay = SKScene(fileNamed: "overlay")
overlay?.isUserInteractionEnabled = false
let pauseButton = overlay?.childNode(withName: "pauseButton") as? Button
// Button is a subclass of SKSpriteNode that overrides touchesEnded
pauseButton?.isUserInteractionEnabled = true
sceneView.overlaySKScene = overlay
If the user touches a button, the button's touch events (touchesBegan, touchesEnded etc) will fire and consume the touch (underlying gesture recognizers will still fire though). If they touch outside of a button however, the touch will pass through to the underlying SCNView.
This is "lifted" straight out of Xcode's Game template......
Add a gesture recognizer in your viewDidLoad:
// add a tap gesture recognizer
let tapGesture = UITapGestureRecognizer(target: self, action:
#selector(handleTap(_:)))
scnView.addGestureRecognizer(tapGesture)
func handleTap(_ gestureRecognize: UIGestureRecognizer) {
// retrieve the SCNView
let scnView = self.view as! SCNView
// check what nodes are tapped
let p = gestureRecognize.location(in: scnView)
let hitResults = scnView.hitTest(p, options: [:])
// check that we clicked on at least one object
if hitResults.count > 0 {
// retrieved the first clicked object
let result: AnyObject = hitResults[0]
// result.node is the node that the user tapped on
// perform any actions you want on it
}
}
You can implement this method in spritekitHUD:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
game.touchesBegan(touches, with: event)
}

Trouble with SKScenes and Segueing to ViewControllers SpriteKit

In my GameViewController i have two scenes, one being the GameScene and another being a scene (EndScene) that is switched to when the player dies. In the first block of code below is my EndScene Scene that is supposed to switch to my main menu viewcontroller, however it wont for some reason. I'm not getting any errors or likewise, it just stays on EndScene. In the second block of code is my GameViewController.swift file. Can someone help point out what im doing wrong here and what i can do to fix it?
EndScene.swift
import SpriteKit
class EndScene : SKScene {
var viewController : UIViewController?
override func didMoveToView(view: SKView) {
self.segue()
}
func segue() {
self.viewController?.performSegueWithIdentifier("gameToMain", sender: viewController)
}
}
GameViewController.swift
import UIKit
import SpriteKit
class GameViewController: 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
scene.viewController = self
skView.presentScene(scene)
}
if let scene = EndScene(fileNamed: "EndScene"){
// 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
scene.viewController = self
skView.presentScene(scene)
}
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
try
let newScene: GameScene = GameScene(size: size)
let transition = SKTransition.fadeWithColor(SKColor.blackColor(), duration: 0.5)
view?.presentScene(newScene, transition: transition)
There is other transitions you can use, but try this code and see if it will switch your scenes