This is about drawing a TileMap in SpriteKit. I get the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent: name:'(null)' texture:['nil'] position:{64, 0} size:{0, 0} rotation:0.00'
This is my GameViewController.swift:
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene(size: CGSize(width: 1024, height: 768))
// 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)
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
This is my GameScene.swift:
import SpriteKit
class GameScene: SKScene {
let dungeon = SKNode()
let readableMap: [[Int]] = [
[01, 00, 01, 01],
[01, 00, 01, 01],
[01, 00, 01, 01],
[00, 01, 00, 01]]
override init(size: CGSize) {
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
drawMap()
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func drawMap() {
let map = readableMap.reverse()
let tiles = [[Tile]](count: map.count, repeatedValue: [Tile](count: map[0].count, repeatedValue: Tile()))
for i in 0..<map.count {
for j in 0..<map[0].count {
let tile = tiles[j][i]
tile.position = CGPoint(x: j * Int(tile.tileSize.width), y: i * Int(tile.tileSize.height))
dungeon.addChild(tile)
}
}
}
}
this is the class in Tile.swift:
import SpriteKit
class Tile: SKSpriteNode {
let tileSize: CGSize = CGSize(width: 64, height: 64)
let tileTexture: SKTexture = SKTexture(imageNamed: "o")
}
what could be the reason? Thanks to anyone, I'm still learning.
Related
I'm pretty new to Swift and I'm having some trouble implementing a leaderboard into my game.
So far, I'm able to authenticate local player and set up leaderboard in iTunes Connect.
However, I'm unable to display the leaderboard itself, if I run the below code, it will abort when I click on GK Leaderboard button in SKScene. So, the question is how can I display the GK leaderboard from SKScene? Thanks!
GameViewController.swift
import SpriteKit
import GameKit
class GameViewController: UIViewController, GKGameCenterControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let scene = SceneMenu(size: view.frame.size)
scene.scaleMode = .aspectFill
scene.backgroundColor = .white
let view = view as! SKView
view.presentScene(scene)
view.showsFPS = true
view.showsNodeCount = true
authenticateLocalPlayer()
}
func authenticateLocalPlayer() {
GKLocalPlayer.local.authenticateHandler = { viewController, error in
}
}
func showLeaderboard() {
let gcVC = GKGameCenterViewController(leaderboardID: "com.generic.leaderboard", playerScope: .global, timeScope: .allTime)
gcVC.gameCenterDelegate = self
present(gcVC, animated: true)
}
func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismiss(animated: true)
}
}
SceneMenu.swift
import SpriteKit
import GameKit
class SceneMenu: SKScene {
override init(size: CGSize) {
super.init(size: size)
let btnGK = SKLabelNode(text: "GameKit")
btnGK.name = "btn_gk"
btnGK.fontSize = 20
btnGK.fontColor = SKColor.blue
btnGK.fontName = "Avenir"
btnGK.position = CGPoint(x: size.width / 2, y: size.height / 2)
addChild(btnGK)
let btnLeaderboard = SKLabelNode(text: "GK Leaderboard")
btnLeaderboard.name = "btn_leaderboard"
btnLeaderboard.fontSize = 20
btnLeaderboard.fontColor = SKColor.blue
btnLeaderboard.fontName = "Avenir"
btnLeaderboard.position = CGPoint(x: size.width / 2, y: size.height / 2 - 50)
addChild(btnLeaderboard)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let action = atPoint(location)
switch action.name {
case "btn_gk":
print("btn_gk")
GKLeaderboard.submitScore(10, context: 0, player: GKLocalPlayer.local, leaderboardIDs: ["com.generic.leaderboard"]) { _ in }
case "btn_leaderboard":
print("btn_leaderboard")
GameViewController().showLeaderboard()
default:
print("nothing")
}
}
}
}
I can’t provide any code right now but what you really should be doing is using something like UIKit to create a UIView for the leader board and then embed that into a SKScene.
There is an interface something like SKView that is backed by a UIView which makes this fairly easy to do.
Keep Sprite Kit for the game stuff. Use UIKit for the UI stuff. 😃
I tried copying the template code from a SpriteKit project into a playground, but all I get is a grey screen when I present the Scene. Do I need to create an SKScene and if so how do I assign it to the scene class that I am using.
The following is my code to create and present the scene:
#objc func goToGameScene(){
print("GameView Loaded")
sceneView = SKView(frame: CGRect(x: 0, y: 0, width: 666, height: 500))
sceneView.backgroundColor = UIColor.black
PlaygroundPage.current.liveView = sceneView
if let view = self.sceneView 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
}
And this is my SKScene class, which has a filename of GameScene.swift.
import Foundation
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var bg = SKSpriteNode()
func didBegin(_ contact: SKPhysicsContact) {
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
let bgTexture = SKTexture(image: UIImage(named: "MainScreenBackground.png")!)
let moveBGAnimation = SKAction.move(by: CGVector(dx:-bgTexture.size().width, dy:0), duration: 11)
let shiftBackground = SKAction.move(by: CGVector(dx: bgTexture.size().width, dy:0), duration: 0)
let repeatAnimationBg = SKAction.repeatForever(SKAction.sequence([moveBGAnimation, shiftBackground]))
var q = 0
while(q < 3){
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: bgTexture.size().width * CGFloat(q), y: self.frame.midY)
bg.size.height = self.frame.height
bg.run(repeatAnimationBg)
self.addChild(bg)
q+=1
bg.zPosition = -1
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
}
}
Assuming you have dragged and dropped your MainScreenBackground.png image into you Assets.xcassets folder, you can use the code below to add the image to your scene as a SKSpriteNode.
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
let bgTexture = SKSpriteNode(imageNamed: "MainScreenBackground")
self.addChild(bgTexture)
...
All the tutorials I have followed, make sprites with all their functions and everything in the same class as everything else - GameScene class. Now I am trying to divide the code from the GameScene to different classes but am having troubles with initializing the first class. Because I have almost no code, I will copy and paste all of it.
Error message:
/Users/Larisa/Desktop/Xcode/Igranje/Poskus2/Poskus2/PlayerUros.swift:17:15: Cannot invoke 'SKSpriteNode.init' with an argument list of type '(texture: SKTexture, position: CGPoint, size: () -> CGSize)'
GameScene class:
import SpriteKit
class GameScene: SKScene {
var player: PlayerUros!
override func didMoveToView(view: SKView) {
let pos = CGPointMake(size.width*0.1, size.height/2)
player = PlayerUros(pos: pos)
addChild(player)
player.rotate()
setupBackground()
}
func setupBackground() {
let BImage = SKSpriteNode(imageNamed: "bg-sky")
BImage.anchorPoint = CGPointMake(0, 0)
BImage.position = CGPointMake(0, 0)
BImage.size = CGSizeMake(self.size.width, self.size.height)
addChild(BImage)
}
}
Player class:
import Darwin
import SpriteKit
class PlayerUros: SKSpriteNode {
init(pos: CGPoint) {
let texture = SKTexture(imageNamed: "hero")
super.init(texture: texture, position: pos, size: SKTexture.size(texture))
}
func rotate() {
let rotate = SKAction.rotateByAngle(CGFloat(M_PI), duration: NSTimeInterval(1.5))
let repeatAction = SKAction.repeatActionForever(rotate)
self.runAction(repeatAction)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
and GameViewController class (not sure if it matters):
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .ResizeFill
skView.presentScene(scene)
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
Try using the line
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
instead of
super.init(texture: texture, position: pos, size: SKTexture.size(texture))
in your Player class. It works for me. Does it work for you?
I'm new to swift programming and have been stuck on this issue for the last week. I am trying to centre on a hero while applying impulse to him. My code works if I create a hero in the scene class and centre him but it doesn't work when I put a hero in another class and try to centre him from scene class. Now centre on works if I just move him by changing his x or y position but not if I apply impulse. It seems that when a hero is in a separate class, when impulse is applied x and y while he is in motion is showing up as a nil, however when he is in the same class than x and y while in the motion are showing up ok. Any help is much appreciated.
Code where Center ON works with ApplyImpulse when everything is in the same class*****
class GameScene: SKScene {
var hero:SKSpriteNode?
var mazeWorld:SKNode?
var player=Player()
var heroLocation:CGPoint = CGPointZero
override func didMoveToView(view: SKView) {
mazeWorld = childNodeWithName("mazeWorld")
heroLocation = mazeWorld!.childNodeWithName("sPoint")!.position
self.anchorPoint = CGPoint(x: 0.5,y: 0.3)
hero?.position = heroLocation
hero = SKSpriteNode(imageNamed:"hero_walk_down_0")
hero!.physicsBody?.dynamic = false
hero!.physicsBody?.affectedByGravity = true
hero!.physicsBody?.pinned = false
hero!.physicsBody = SKPhysicsBody(rectangleOfSize: hero!.size)
hero!.physicsBody?.mass = 1.0;
hero!.setScale(0.8)
mazeWorld!.addChild(hero!)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent)
{
/* Called when a touch begins */
for touch: AnyObject in touches {
hero!.physicsBody?.applyImpulse(CGVector(dx: 240, dy: 550))
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func centerOnNode(node:SKNode){
let cameraPositionInScene:CGPoint = self.convertPoint(node.position, fromNode: mazeWorld!)
mazeWorld!.position = CGPoint(x:mazeWorld!.position.x - cameraPositionInScene.x, y: mazeWorld!.position.y - cameraPositionInScene.y)
}
override func didSimulatePhysics() {
self.centerOnNode(hero!)
}
}
**** Here is the code with Center ON that doesn't work with apply impulse for some reason
import SpriteKit
class GameScene: SKScene {
var hero:SKSpriteNode?
var mazeWorld:SKNode?
var player=Player()
var heroLocation:CGPoint = CGPointZero
override func didMoveToView(view: SKView) {
mazeWorld = childNodeWithName("mazeWorld")
heroLocation = mazeWorld!.childNodeWithName("sPoint")!.position
self.anchorPoint = CGPoint(x: 0.5,y: 0.3)
player.position = heroLocation
mazeWorld!.addChild(player)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent)
{
/* Called when a touch begins */
for touch: AnyObject in touches {
player.jumpRight()
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func centerOnNode(node:SKNode){
let cameraPositionInScene:CGPoint = self.convertPoint(node.position, fromNode: mazeWorld!)
mazeWorld!.position = CGPoint(x:mazeWorld!.position.x - cameraPositionInScene.x, y: mazeWorld!.position.y - cameraPositionInScene.y)
}
override func didSimulatePhysics() {
self.centerOnNode(player)
}
}
import Foundation
import SpriteKit
class Player: SKNode{
var objectSprite:SKSpriteNode?
override init()
{
super.init()
objectSprite = SKSpriteNode(imageNamed:"hero_walk_down_0")
objectSprite!.physicsBody?.dynamic = false
objectSprite!.physicsBody?.affectedByGravity = true
objectSprite!.physicsBody?.pinned = false
objectSprite!.physicsBody = SKPhysicsBody(rectangleOfSize: objectSprite!.size)
objectSprite!.physicsBody?.mass = 1.0
objectSprite!.setScale(0.8)
addChild(objectSprite!)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func jumpRight()
{
objectSprite!.physicsBody?.applyImpulse(CGVector(dx: 240, dy: 550))
}
}
This is my first project with SpriteKit and I am following THIS tutorial
But when I try to give the position to the Image as he did into that tutorial at 20:10 with this code :
playScene.swift
import SpriteKit
class playScene : SKScene {
let runningBar = SKSpriteNode(imageNamed: "bar")
override func didMoveToView(view: SKView) {
println("We are at the new scene!")
self.backgroundColor = UIColor(hex: 0x80D9FF, alpha: 1)
self.runningBar.anchorPoint = CGPointMake(0.5, 0.5)
self.runningBar.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - (self.runningBar.size.height / 2))
self.addChild(self.runningBar)
}
override func update(currentTime: NSTimeInterval) {
}
}
I want to give position at bottom of the screen but I got this Output:
But the output should be:
GameScene.swift (If needed)
import SpriteKit
class GameScene: SKScene {
let playButton = SKSpriteNode(imageNamed: "play")
override func didMoveToView(view: SKView) {
self.playButton.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(self.playButton)
self.backgroundColor = UIColor(hex: 0x80D9FF, alpha: 1)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches{
let location = touch.locationInNode(self)
if self.nodeAtPoint(location) == self.playButton{
var scene = playScene(size: size.self)
let skView = self.view as SKView!
skView.ignoresSiblingOrder = true
scene.scaleMode = .ResizeFill
scene.size = skView.bounds.size
skView.presentScene(scene)
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Can anybody tell me how can I achieve this?
Thanks In advance.
You need to look at the tutorial properly. You have set the following lines wrong:
self.runningBar.anchorPoint = CGPointMake(0.5, 0.5)
self.runningBar.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - (self.runningBar.size.height / 2))
They should be:
self.runningBar.anchorPoint = CGPointMake(0, 0.5)
self.runningBar.position = CGPointMake(CGRectGetMinX(self.frame), CGRectGetMinY(self.frame) + (self.runningBar.size.height / 2))
For a better understanding of the coordinate system, have a look at the documentation.