Can't reference Xcode GameScene.sks with swift - ios

I am a beginer and starting my first swift game. It seems like the GameScene.sks interface would be extrememly easy to position my labels and nodes for each level, but I am having trouble figuring out how to reference the nodes with swift.
For example I dragged over a label to act as a timer, but I dont know how to update the label text with code.
I was thinking something like:
func timerDidFire(){
countDown--
SKLabelNode.name("countDownTimerLabel").text = countDown
}

You need to use the childNodeWithName method on SKNode. For example:
func timerDidFire() {
let label = childNodeWithName("countDownTimerLabel") as! SKLabelNode
label.text = --countDown
// I believe this is an appropriate case for force unwrapping since you're going to
// want to know if your scene doesn't contain your label node.
}
Since you're going to be accessing the SKLabelNode often, and it takes time to search through the node tree (not very much but something to bear in mind), it may be a good idea to keep a reference to the label. For example, in your SKScene subclass:
lazy var labelNode: SKLabelNode = self.childNodeWithName("countDownTimerLabel") as! SKLabelNode
On a related note, if you're looking for multiple nodes there's the
enumerateChildNodesWithName(_:usingBlock:), also on SKNode.
Finally, for more information have a look at Apple's WWDC 2014 talk: Best Practices for Building SpriteKit Games where they cover both the methods I mentioned.

Related

Accessing SKView methods from an SKNode

In SpriteKit development, how is one supposed to get access to the SKView?
For example; lets say I create a custom SKNode class called Player for use throughout my game:
class Player: SKNode {
/*...*/
}
In this class, if I wanted to generate a SKSpriteNode from an SKShapeNode (in order to improve performance) I need to use the SKView.texture() method. But I don't have access to the SKView that my Player is residing in unless I pass the view to the Player from whoever is instantiating it.
Is there any way to get the current view that an SKNode is attached, so that I'm not having to pass references to the view around my application? Is this just an anomaly of the framework? What is the preferred way of dealing with this problem?
I'm using the Swift language.
Update
Ron Myschuk's answer is correct and I've accepted it as it is probably most useful to the community. However my use-case for wanting to access the view from within a node was in order to use the texture method to create an SKSpriteNode from an SKShapeNode.
Here's a description of the method I wanted to gain access to:
Renders a portion of a node’s contents and returns the rendered image as a SpriteKit texture.
https://developer.apple.com/documentation/spritekit/skview/1519994-texture
This method seems useful outside of the view. For example, when initialising/populating SKNode's that have not yet been added to the display tree.
My solution to this specific problem was simply to create an SKView on the fly and use it for the purposes of my needs, allowing it to be garbage collected by the engine.
let view = SKView()
let texture = view.texture(from: someSkShapeNode)
let sprite = SKSprite(texture: texture)
// we don't need view anymore
Hope this helps someone.
providing that you've added an instance of player to your scene and providing that you are not doing his in the initializer of Player you could use the .scene property
self.scene?.view
You can build a SKTexture from UIImageor CGImage and you can create images from every complex path/drawing you want (see SKTexture from path for example or Drawing and Creating images).

Organising animations in Swift

I have a number of different scenes in Swift. Each scene animates a number of objects briefly. These objects are disparate and all animate in their own unique ways.
The issue is that now I have lines and lines of code for each scene, all of which govern a different SKAction. Is there a way to minify this kind of code, or to restructure the code so that we put it all in one place (and draw from it somewhere else)?
One of my ideas is to make all of the animated nodes a specific class, like AnimatedNode, and then put all the animations in there; as separate functions. This would effectively remove the code from the scene.swift file. Is this the best I can do, or is there some other approach to this that I don't see?
EDIT: My animation code runs 100+ lines across every scene, but as an example of how animations are performed on one such node, this is what it looks like:
// can
let can = bgNode.childNode(withName: "can")
let tip = SKAction.rotate(byAngle: CGFloat(GLKMathDegreesToRadians(10)), duration: 0.5)
let tipSound = SKAction.playSoundFileNamed("pot", waitForCompletion: false)
can?.run(SKAction.sequence([tip, tipSound, tip.reversed()]))
SKActions were meant to be re-used or at least pre-loaded. Your idea is good, because cluttering your scenes with the same code over and over is poor design.
You can make an ActionManager, that has properties for each action you want to run already pre-loaded as a property.. this will increase performance of your game, especially if you are using it more than once.
There are multiple ways to do this, but a plain function (though not as performant as properties) is a good way to at least organize your code.

Setting SKSpriteNode position by using invisible UIView in Storyboard

I just recently started on iOS Spritekit. Most tutorials and StackOverflow topics mentioned that I can't use Storyboard for arranging SKSpriteNode so I've been struggling on layout my views, especially I also have some other components like UILabels.
I hack this by placing invisible UIViews (and do autolayout on them with other components), and pass the values into the SKScene which will place SpriteNode based on the value--after converting the coordinate systems, etc.
Would there be potential issues on this approach? It works for simple cases but it is obviously a hack... :P
Or if there are better way of layout SpriteNode? I am going through RayWenderlich tutorials, but so far many of them seems to be hardcoded positions?
Well, it's certainly more work than needs to be done so there could be performance issues. As an alternative (and the correct way) to do this is use the scene editor in Xcode.
Create a new SpriteKit Scene File and add your nodes to it. Create a new Swift file of class SKScene and set the custom class for the scene file.
Then all you have to do to get those nodes into your class (like IBOutlet) is the following...
override func didMove(to: view){
let shipNode = self.childNode(withName: "shipNodeName") as! SKSpriteNode
}

Is there any way to access a property like SKSpriteNode in Swift?

I have some experience in SKSpriteKit and I am writing code in UIViewController using UIKit library. And I wonder if I could access existing propteries such as ones from UILabel, UITextField, UIView, and so on. In Swift, nodes can be named and called by their names using childWithName(name: String), so is there any way to do this in UIKit. ? I thought doing this would be much more convenienet and my writing would go smooth. I started programming in UIKit so I do not know much about it, so I appreciate if you teach me abou this!
Thankyou
You can't give "names" to UIViews as far as I'm concerned. However, there's something similar.
You can set a view's tag by doing:
someView.tag = 1
Then, assuming that superView is the super view of someView, you can do this to get someView:
superView.viewWithTag(1)
It's not as descriptive as name, but you can use named constants to make it clearer:
let scoreLabelTag = 1
superView.viewWithTag(scoreLabelTag)

Complex enemy behaviors and patterns

I'd like to switch things up a bit with my enemies in my SpriteKit game. They're just so... predictable. I've been inspired by the sorts of enemies that are encountered in The Binding of Isaac: Rebirth.
For example, let's say I'd like to implement enemies which split into two smaller enemies when enemy.health < 50.0f. In a simple scenario, this could be done from my main scene update loop by iterating through all my enemies. Something like:
if(enemy.health < 50 && enemy.type == kEnemyTypeSplitsIntoTwo) {
CGPoint position1 = CGPointMake(enemy.position.x+20,enemy.position.y);
CGPoint position2 = CGPointMake(enemy.position.x-20,enemy.position.y);
[enemy killEnemy];
[self spawnNewEnemyWithType:enemyType andHealth:50 atPosition:position1];
[self spawnNewEnemyWithType:enemyType andHealth:50 atPosition:position2];
}
This will probably work for simple cases. But this is something that I definitely want to keep out of my main scene update loop. The problem is that my enemy class is effectively a subclass of SKSpriteNode.
For situations like this, ideally I'd like a new class such as a ComplexEnemy or EnemyGroup class to handle this so I can implement more complex enemy behaviours and patterns (think epic boss fights with multiple phases). I'd imagine this new class would likely derive from SKNode (or even NSObject?) and generate multiple Enemy instances at certain points.
Am I thinking down the right path? I'd be interested to hear what means people have used to achieve this sort of behaviour, preferably with a couple of examples.
-DC
tl;dr - Groups of enemies, bosses with multiple phases. How do you guys do it?
There's nothing wrong with using SKSpriteNode as an enemy class. I do it all the time. If you want to add a health bar, for example, you can simply add it as child.
Your question is primarily opinion based so expect it to be closed soon enough. However, it is a good question nonetheless. I suggest you use one or more enemy SKSpriteNode classes to handle your workload as it can make things easier for a beginner. You can of course get away with just using one enemy class and have its update method handle various aspects based on properties. For example:
-(void)update:(NSTimeInterval)currentTime {
if(self.enemyType == kDragon) {
// do big stuff
}
if(self.enemyType == kDwarf) {
// do small stuff
}
}
I am currently working on a project which has 10 different kinds of enemies and 4 behavior options for each one. It is all being handled from one class (SKSpriteNode).
It all boils down to what you want and how you arrange your code to do the job. I suggest you think about exactly what it is you are trying to do and if you get stuck, ask for help on a more specific issue.
In your situation, I would subclass the ComplexEnemy as a simple object. This object would be responsible for the instantiation and the adding and removing to the scene of the various enemies that could appear.
Then in your scene update method, I'd call a checking method in that object. The checking method would do the rest of the heavy lifting. You can put that call (the one in the update method) in an array of methods and when the ComplexEnemy gets instantiated, it would register itself in that array so it can be checked. It would remove itself from the array before being removed from memory..

Resources