tilemap in sprite kit - swift

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

How to display a Game Center leaderboard in SpriteKit?

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. 😃

How to set up an SKScene with an SKNode with a texture in Swift Playgrounds?

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)
...

Error when trying to initialize a class as a SKSpriteNode subclass and use it in GameScene - Swift 2

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?

Swift Center on Camera not working when ApplyingImpulse in another class

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

Issue with positioning SKSpriteNode in SKScene

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.