Swift SpriteKit: Use of unresolved identifier "player"? - swift

When I try to run an action on a sprite I've set a constant for in didMoveToView, I use the same name for it in the touchesBegan function and get a "use of unresolved identifier: "player" error. I have another game where I so the exact same thing and it runs perfectly. Need help! Here is my code:
import SpriteKit
class GameScene: SKScene {
var movingGround : MCTGround!
var fruitGenerator : MCTFruitGen!
var cloudGenerator: MCTCloudGen!
var isStarted = false
override func didMoveToView(view: SKView) {
let player = SKSpriteNode(imageNamed: "koala_idle")
player.position = CGPointMake(95, 150)
addChild(player)
backgroundColor = UIColor(red: 159.0/255.0, green: 201.0/255.0, blue: 244.0/255.0, alpha: 1.0)
movingGround = MCTGround(size: CGSizeMake(view.frame.width, 20))
movingGround.position = CGPointMake(0, view.frame.size.height / 4)
addChild(movingGround)
fruitGenerator = MCTFruitGen(color: UIColor.clearColor(), size: view.frame.size)
fruitGenerator.position = view.center
addChild(fruitGenerator)
let frames = [
SKTexture(imageNamed: "koala_idle"),
SKTexture(imageNamed: "koala_walk01"),
SKTexture(imageNamed: "koala_walk02"),
]
let duration = 1.5 + drand48() * 1.0
let move = SKAction.animateWithTextures(frames, timePerFrame:0.10)
let wait = SKAction.waitForDuration(duration)
let rest = SKAction.setTexture(frames[0])
let sequence = SKAction.sequence([move, rest])
player.runAction(SKAction.repeatActionForever(sequence))
cloudGenerator = MCTCloudGen(color: UIColor.clearColor(), size: view.frame.size)
cloudGenerator.position = view.center
addChild(cloudGenerator)
cloudGenerator.populate(7)
cloudGenerator.startGeneratingWithSpawnTime(1)
}
func start() {
isStarted = true
cloudGenerator.startGeneratingWithSpawnTime(1)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
jumpPlayer()
movingGround.start()
fruitGenerator.startGeneratingFruitEvery(1)
}
override func update(currentTime: CFTimeInterval) {
}
func jumpPlayer() {
let jumpUpAction = SKAction.moveByX(0, y: 40, duration: 0.5)
let jumpDownAction = SKAction.moveByX(0, y: -40, duration: 0.5)
let jumpSequence = SKAction.sequence([jumpUpAction, jumpDownAction])
player.runAction(jumpSequence) // This is where my error is
}
}

Make player a class scope variable (property). Declare it not in a function, but a level above.
var player: SKSpriteNode?
override func didMoveToView(view: SKView) {
player = SKSpriteNode ...
then access it via player?. The safest way.

Your problem is that you are declaring player inside didMoveToView. This makes it private to the didMoveToView method. All you need to do is move let player = SKSpriteNode(imageNamed: "koala_idle") to where you declare movingGround and the other variables with it.
The beginning your code should look like this:
import SpriteKit
class GameScene: SKScene {
var movingGround : MCTGround!
var fruitGenerator : MCTFruitGen!
var cloudGenerator: MCTCloudGen!
let player = SKSpriteNode(imageNamed: "koala_idle")
var isStarted = false
override func didMoveToView(view: SKView) {
player.position = CGPointMake(95, 150)
addChild(player)

Go to File Inspector (right click on the file referenced in the error and select Show File Inspector). In Target Membership make sure your actual app is selected.
In my case only the Test modules were selected.
So as far as I can tell (I'm new to all this), the referenced file was hidden/unregistered with the ViewController.

Related

SpriteKit creating a button issues

Im attempting to use some of the code from a solution found on this page for creating a button Create Button in SpriteKit: Swift
class GameScene: SKScene {
let button = SKSpriteNode(imageNamed: "yourImgName")
override func didMoveToView(view: SKView) {
button.name = "btn"
button.size.height = 100
button.size.width = 100
button.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) + 50)
self.addChild(button)
//Adjust button properties (above) as needed
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.locationInNode(self)
let touchedNode = self.nodeAtPoint(positionInScene)
if let name = touchedNode.name {
if name == "btn" {
let yourNextScene = YourNextScene(fileNamed: "YourNextScene")
self.view?.presentScene(yourNextScene!)
}
}
}
}
and the current code that I have is supposed to make the player jump when the button is pressed, but nothing is currently happening when its pressed
import SwiftUI
import SpriteKit
import UIKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let button = SKSpriteNode(imageNamed: "playerOBJ")
let player = SKSpriteNode(imageNamed: "playerOBJ")
let playerRadius = player.frame.width / 2.0
player.position = CGPoint(x: 200, y: 500)
player.name = "Jimmy"
addChild(player)
player.physicsBody = SKPhysicsBody(circleOfRadius: playerRadius)
player.physicsBody?.allowsRotation = false
player.physicsBody?.friction = 0
player.physicsBody?.restitution = 0
player.zPosition = 100
// Button
button.name = "btn"
button.size.height = 100
button.size.width = 100
button.position = CGPoint(x: 100, y: 100)
self.addChild(button)
// Physics
physicsBody = SKPhysicsBody(edgeLoopFrom: frame.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)))
Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { _ in
player.physicsBody?.applyForce(CGVector(dx: 100, dy: 1000))
}
func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "btn" {
player.physicsBody?.applyForce(CGVector(dx: 0, dy: 10000))
}
}
}
}
override func update(_ currentTime: TimeInterval) { }
}
I'm thinking that maybe this is an issue with the press not being resitered at all but I'm not fully sure
Your main problem is you put all of your code inside the didMove function. You put the touchesBegan function inside the didMove function. When the didMove function finishes, touchesBegan goes away so none of your touches are going to be handled in the game.
You also declared the button and the player as local variables inside the didMove function.
override func didMove(to view: SKView) {
let button = SKSpriteNode(imageNamed: "playerOBJ")
let player = SKSpriteNode(imageNamed: "playerOBJ")
// Rest of function omitted
}
When the didMove function finishes, the button and player go away too. You also have the same image name for the player and button.
The fix is to make the button and player variables properties of the GameScene class. Move touchesBegan out of the didMove function too.
class GameScene: SKScene {
// Declare button and player here.
var button = SKSpriteNode()
var player = SKSpriteNode()
override func didMove(to view: SKView) {
// Initialize button, player, and everything else here.
}
func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Code in touchesBegan func goes here.
}
}

App Crashing when loading Custom Class SKSpriteNode to SKScene

I am creating SKSpriteNode Programmatically with its own SKSpriteNode class. I am wondering how I can add this SKSpriteNode to the scene. My app keeps crashing when I try to addChild() in the didMove() function of my Scene Class.
class ChaosScene: SKScene {
var dragonNode: Dragon!
override func didMove(to view: SKView) {
dragonNode.createDragon()
addChild(dragonNode)
}
}
class Dragon: SKSpriteNode {
var dragonNode: SKSpriteNode!
func createDragon() {
// Create Dragon
dragonNode = SKSpriteNode(imageNamed: "dragon_2_fly_001")
dragonNode.name = "dragon"
dragonNode.physicsBody = SKPhysicsBody(circleOfRadius: 50)
let actionMove = SKAction.move(
to: CGPoint(x: -1000 ,y: dragonNode.position.y),
duration: 2.0)
dragonNode.run(actionMove)
}
}
you're not actually creating anything.
class ChaosScene: SKScene {
var dragonNode: Dragon!
override func didMove(to view: SKView) {
dragonNode = Dragon()
addChild(dragonNode)
}
}
you have to initialize your SpriteNodes before you can add them to the scene
class Dragon: SKSpriteNode {
var dragonNode: SKSpriteNode!
init() {
super.init(texture: nil, color: .clear, size: CGSize.zero)
// Create Dragon
dragonNode = SKSpriteNode(imageNamed: "dragon_2_fly_001")
dragonNode.name = "dragon"
dragonNode.physicsBody = SKPhysicsBody(circleOfRadius: 50)
addChild(dragonNode)
let actionMove = SKAction.move(to: CGPoint(x: -1000 ,y: dragonNode.position.y), duration: 2.0)
dragonNode.run(actionMove)
}
}

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

SKLabelNode Moves. But action does not repeat

I have created a Test Scene to practice some basic Swift 3 and SpriteKit. I'm trying to learn by understanding the basics before moving on to more complex goals.
Here I have a SKLabelNode that is created and then moves to the left. I have created a sequence to repeat the action but it does not work. Please could you help me understand where it fails. NodeCount notes that there is only 1 node.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var testShape = SKLabelNode()
override func didMove(to view: SKView) {
func createShape() {
testShape = SKLabelNode(text: "TEST")
testShape.position = CGPoint(x: 0.5, y: 0.5)
testShape.zPosition = 1
addChild(testShape)
}
let moveTestShape = SKAction.moveBy(x: -500, y: 0, duration: 5)
func repeater() {
createShape()
testShape.run(moveTestShape)
}
let delay = SKAction.wait(forDuration: 2)
let repeatingAction = SKAction( repeater() )
let sequence = SKAction.sequence([ delay, repeatingAction ] )
run(SKAction.repeatForever(sequence))
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
}
}
Are you not getting compiler errors?
Why are you creating methods in didMoveToView?
Your code should look more like this
class GameScene: SKScene {
var testShape = SKLabelNode()
override func didMove(to view: SKView) {
let delay = SKAction.wait(forDuration: 2)
let repeatingAction = SKAction.run(repeater)
let sequence = SKAction.sequence([ delay, repeatingAction ] )
run(SKAction.repeatForever(sequence))
}
func createShape() {
testShape = SKLabelNode(text: "TEST")
testShape.position = CGPoint(x: 0.5, y: 0.5)
testShape.zPosition = 1
addChild(testShape)
}
func repeater() {
createShape()
let moveTestShape = SKAction.moveBy(x: -500, y: 0, duration: 5)
testShape.run(moveTestShape)
}
}
This is how you call functions/code blocks in SKActions.
let repeatingAction = SKAction.run(repeater)
or
let repeatingAction = SKAction.run {
repeater()
}
Also remember our are only repeating the spawn action for new labels. The actual action to move the labels is non repeating. So what you should see is 1 label created and moved once, than 2 seconds later a new label gets created and moved once etc
Hope this helps

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.