Why is init(fileNamed:) of SKSpriteNode generating a nil?
I've tried the following code. I show only the code that is related to the problem:
let road = SKSpriteNode(fileNamed: "road.png")
override func didMove(to view: SKView) {
print("road", road as Any) // road nil
if let road = self.road {
road.position = view.center
road.physicsBody = SKPhysicsBody(rectangleOf: road.size)
print(road.physicsBody?.isDynamic as Any, "!")
road.physicsBody?.pinned = true
addChild(road)
}
}
I get a nil regardless of whether the image file is a regular png or an animated png file.
You should use SKSpriteNode(imageNamed:) instead of SKSpriteNode(fileNamed:).
SKSpriteNode(imageNamed:) - loads image as texture for you and creates a node.
SKSpriteNode(fileNamed:) is actually init method from SKNode, as it says in official doc:
init?(fileNamed: String)
Creates a new node by loading an archive file from the game’s main bundle.
So there are two different methods (constructors), from two different classes, and even though one inherit from another they should not be confused.
Related
I am currently making a game with Spritekit & Swift3 for the first time; it is a 2D, side-scrolling endless runner. I'm trying to have certain nodes move with my camera, but the problem is that they will be procedurally generated, belonging to the same class, so I need them all to have the same name. Here's the relevant sections of code, before I go any further:
(All variables have been initialized)
//move these nodes with the camera
private var ChoosePieces: ChoosePiecesClass?;
override func didMove(to view: SKView) {
initializeGame();
}
override func update(_ currentTime: TimeInterval) {
enumerateChildNodes(withName: "ChoosePieces") {
node, stop in
_ = node as! SKSpriteNode
self.ChoosePieces?.moveWithCamera();
}
}
private func initializeGame() {
ChoosePieces = childNode(withName: "ChoosePieces") as? ChoosePiecesClass;
}
I have tested the moveWithCamera() function and it works well--it simply increments the node's x-value by 10, moving at the same pace as the camera so that the node remains on screen at the same location.
My problem is that I'm not exactly sure how to use enumerateChildNodes(withName: "String") with my nodes, so that it will recognize all of them and move them. Right now the pieces just stay still. I found out about this function from another person's post--he/she was trying to spawn "enemies" in his/her game. Thanks in advance!
Usually with enumerateChildNodes you do something with the node that is returned e.g. modifying your own example:
override func update(_ currentTime: TimeInterval) {
enumerateChildNodes(withName: "ChoosePieces") {
node, stop in
if let piece = node as? SKSpriteNode { // Cast to Sprite if we can
piece.moveWithCamera()
}
}
}
So for every node that is returned, we cast it to a new SKSpriteNode called 'piece (if we can - using the as? operator - because enumerateChildNodes returns SKNodes) ' and then call moveWithCamera on the 'piece' node.
(the line piece.moveWithCamera() I made up - your original code appeared to be calling a class method(?) to do something. You might want self.moveWithCamera(piece) etc)
In your code, you did nothing with the nodes returned by your enumeration.
I made a label in my LaunchScreen.sks file, named it touchLabel, and I can't seem to find a method for accessing it anywhere.
So how do I set a "let" equal to the label that I created in my .sks file, I know how to create one I just can't figure out how to access one I already created.
I figured the code would be something along the lines of:
let touchLabel = SKLabelNode(named: "touchLabel")
but "named:" is not an available overload for that function.
I remembered to set the name of the label in the .sks file to touchLabel and also remembered to set the parent to LaunchScreen.swift
Also Im really sorry if this is super obvious I am new to swift and I've looked for a solution for at least an hour, but I have only found pages on how to create labels from the .swift file. Not how to access labels that have already been created.
You can use childNode(withName:) to find elements in the sks file.
var touchLabel: SKLabelNode!
override func didMove(to view: SKView) {
guard let touchLabel = childNode(withName: "touchLabel") as? SKLabelNode else {
fatalError("touchLabel node not loaded")
}
self.touchLabel = touchLabel
}
So I was reading the apple documentation for best sprite kit practices. I came across this:
For example, if your game uses the same textures for all its gameplay, you might create a special loading class that runs once at startup. You perform the work of loading the textures once, and then leave them in memory. If a scene object is deleted and recreated to restart gameplay, the textures do not need to be reloaded.
And this would significantly help performance in my application. Can someone point me in the right direction to how I would go about achieving this?
I presume I would call a function to load up texture's in my View Controller? And then access that texture atlas?
The thing is, do you really want to cache the resources like that? Can't say I ever found a need for something of that nature. Anyways, if doing that somehow helps with your app's performance, then you can make a TextureManager class which would be a singleton (create separate file for TextureManager class), like this:
class TextureManager{
private var textures = [String:SKTexture]()
static let sharedInstance = TextureManager()
private init(){}
func getTexture(withName name:String)->SKTexture?{ return textures[name] }
func addTexture(withName name:String, texture :SKTexture){
if textures[name] == nil {
textures[name] = texture
}
}
func addTextures(texturesDictionary:[String:SKTexture]) {
for (name, texture) in texturesDictionary {
addTexture(withName: name, texture: texture)
}
}
func removeTexture(withName name:String)->Bool {
if textures[name] != nil {
textures[name] = nil
return true
}
return false
}
}
Here you are using dictionary and associate each texture with its name. Pretty simple concept. If there isn't a texture with the same name in a dictionary, then add it. Just beware of premature optimization.
The usage:
//I used didMoveToView in this example, but more appropriate would be to use something before this method is called, like viewDidLoad, or doing this inside off app delegate.
override func didMoveToView(view: SKView) {
let atlas = SKTextureAtlas(named: "game")
let texture = atlas.textureNamed("someTexture1")
let dictionary = [
"someTexture2": atlas.textureNamed("someTexture2"),
"someTexture3": atlas.textureNamed("someTexture3"),
"someTexture4": atlas.textureNamed("someTexture4"),
]
TextureManager.sharedInstance.addTexture(withName: "someTexture", texture: texture)
TextureManager.sharedInstance.addTextures(dictionary)
}
As I said, you have to put TextureManager implementation in a separate file, to make it real singleton. Otherwise, if you define it in GameScene for example, you will be able to call that private init, and then TextureManager will not be a real singleton.
So, with this code you can create some textures at the very beginning of the app lifecycle, like it is said in the docs:
For example, if your game uses the same textures for all its gameplay,
you might create a special loading class that runs once at startup.
and fill the dictionary with them. Later on, whenever you need a texture, you will not use atlas.textureNamed() method, but rather load it from a dictionary property of a TextureManager class. Also, when transitioning between scenes, that dictionary will survive scene's deinits, and will persist while app is alive.
I am working on Cocos2d iphone SDK and stuck with an issues. Check my code here.
Obstacle Class
#objc class Obstacle: CCNode {
weak var __pipe: CCSprite!
var ignoreCollision:Bool = false
override init!() {
super.init()
//NSLog("init plain")
userInteractionEnabled = true
ignoreCollision = false
}
func didLoadFromCCB() {
...
}
}
The main scene where I have placed collision delegate methods. The method is called once the player object collides with obstacle object.
func ccPhysicsCollisionPreSolve(pair: CCPhysicsCollisionPair!, hero: Player!, platform: Obstacle!) -> ObjCBool {
if !isGameOn {
NSLog("PLATFORM: Game finished")
return false
}
if platform.ignoreCollision {
platform.ignoreCollision = !platform.ignoreCollision
// For score updates
hudLayer.updatePlatform(++scorePlatforms)
}
return true
}
Now here, I am just trying to use simple Bool property from platform object and what I get is a crash. My app crashes on the if... condition statement where I am using that property. I am unable to get what is with this as I am simply using a property from object.
I checked the object and found platform shows me of type Some instead ob Obstacle. I have tried using
var p: Obstacle = platform as Obstacle
and replaced all platform with p but yet I am facing the crash. I thought the type now shows me some random hex number which might be the issue.
Can anyone help me here as I am unable to find out how I should access property from this platform object in ccPhysicsCollisionPreSolve method?
Sorry guys for the trouble but it was my mistake. I was understanding the same incorrectly.
The Obstacle class represents the platform as well as its background layer having tripple height of the device screen. But my ball collides only with that __pipe sprite in Obstacle class and I am referring the whole Obstacle class which is wrong.
I used platform.parent!.ignoreCollision and problem is solved. :)
This little miss costed me 3-4 days of R&D and work extra.
I've just started learning to program iOS games with SpriteKit and I'm a novice to programming (I have only programmed in Java and Swift before this). I started out by doing a tutorial I found online. I'm at the point where I am trying to add a "Game Over" scene, and I keep getting the error
"Thread 1:EXC-BAD_INSTRUCTION(code=EXC_I386_INVOP,subcode=0x0)"
when I am declaring the gameOverLabel constant. It compiles but crashes at run time as soon as the ball hits the bottom of the screen, which is supposed to trigger the 'Game Over' screen.
import SpriteKit
let gameOverLabelCategoryName = "gameOverLabel"
class GameOverScene: SKScene {
var gameWon: Bool = false {
didSet {
let gameOverLabel = childNodeWithName(gameOverLabelCategoryName) as! SKLabelNode
gameOverLabel.text = gameWon ? "Game Won" : "Game Over"
}
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
if let view = view {
let gameScene = GameScene.unarchiveFromFile("GameScene") as! GameScene
view.presentScene(gameScene)
}
}
}
Also if anyone has suggestions of places I can go to learn more about SpriteKit, articles or videos, it would be much appreciated as most things I have found have been in Objective-C and I am doing this in Swift.
Here is the tutorial I have been following..
childNodeWithName will return nil if a node with that name does not exist. Your code is not checking for this possibility (the as! assumes that it is both not nil and of the appropriate type) so this is causing the crash.
The tutorial asks you to create this 'gameOverLabel' node and name it before creating this code. Check that you did that correctly and that you did not misname it (it is case sensitive for example).
It looks like you are declaring your gameOverLabelCategoryName constant outside of the scope of the class which looks a bit odd, but should be fine. It will be scoped to internal (docs).
On line 14: "if let view = view {" you are declaring a constant named view which masks the original self.view instance. Perhaps that could be confusing the compiler and causing an internal crash?