Get location of an SKSpriteNode - swift

I have an SKSpriteNode called "Laser" that is created in a method known as "shootLaser", with its scope being purely local to that method. The shootLaser method is called on a repeating timer, creating a new Laser object every couple of seconds, so I cannot simply declare the Laser object outside any method. I also have a variable known as laserLoc that I want to be always equal to the location of the "Laser" object at any given time. In the update() method (which is called every time a frame is rendered), my idea was to set laserLoc = Laser.position, but since Laser is only local to the shootLaser method, I get a "use of unresolved identifier error". Is there any way around this?

I think you can declare an SKSpriteNode Array and add the Laser to it. Every time a new laser is created you can check that in your update func.
Here is an example:
class GameScene: SKScene{
var checkForNodes = [SKSpriteNode]()
func shootLaser {
/* Your stuff */
var Laser = SKSpriteNode()
checkForNodes.append(Laser)
}
override func update(currentTime: NSTimeInterval) {
super.update(currentTime)
for Laser in checkForNodes {
/* Your Position Setup */
}
}

Related

SpriteKit: How do I remove objects once they leave the screen?

I have a rock sprite that it is falling and every time it goes out of the screen I want to reset it back to the top and have it fall again. It should be a continuous cycle. Here's my code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
func addRock(){
var rock = self.childNode(withName: "rock")
rock?.physicsBody?.affectedByGravity = true
//self.addChild(rock!)
}
override func sceneDidLoad() {
//bRock = self.childNode(withName: "rock")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//addRock()
var rock = self.childNode(withName: "rock")
rock?.physicsBody?.affectedByGravity = true
/*if (Int((rock?.position.y)!) < Int((self.view?.scene?.view?.bounds.minY)!)){
print("out of screen")
rock?.removeFromParent()
addRock()
}*/
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
var rock = self.childNode(withName: "rock")
//rock?.physicsBody?.affectedByGravity = true
if (!intersects(rock!)){
print("out of screen")
rock?.removeFromParent()
addRock()
}
}
}
I have the rock coming on the screen and then falling. Once it leaves the screen, it does not reset and I get an error. I tried placing the reset code in both the touchesBegan and update functions but neither work. If someone could guide me to the correct path, that would be greatly appreciated.
The problem you're having is that you're removing the rock node from the scene but the addRock method doesn't actually add a new rock, it just finds the existing node if there is one and sets a property on its physicsBody. Instead of removing the node, you should just change its position.
Assuming your scene has the default anchor point of (0,0), you can reset the position like this:
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if let rock = self.childNode(withName: "rock") {
if !intersects(rock!) {
print("out of screen")
rock.position.y = size.height/2 // divided by 2 as discussed in comments
}
}
That will reset the rock's position to the top of the scene and it should resume falling from there.
Three ways:
the same rock is newly located at the right place after leaving the screen, so you just need to set the position of it in (misnamed) addRock (resetRock should be better): rock.position = CGPoint(...). But beware that you also need to reset its velocity, etc, as it was previously moved by physical laws...
create a new rock each time one leaves the screen in addRock: let rock = SKSpriteNode(...)... (and all the initializing code for it. Much simpler as all its physical parameters will be initialized with right values by default.
create a clone of an initial rock. I suppose you have a model of it in your sks file, then don't name it rock but something like (rockModel), and just clone it in addRock with giving it the right name rock. Don't forget to remove the model from the scene at the beginning. This is the usual way to do it.
You could also think about using a series of move actions in a sequence to control the rock's behaviour. Given the move actions are already defined, in your defined sequence, pass in sequence[(falling, hide, backtotop, unhide)] - Repeat Forever.
As long as the rocks don't actually interact with anything and are just for the backdrop/background, hiding and unhiding them is a good way to give the affect they are falling.

SKSpriteNode Automated movement

I'm making my first game, it will be similar to Mario. (using swift)
Have started to make some enemies they will need to move left and right and respond to collisions, I can do this for my player node as I call player.update() function from the SKScene update function,
override func update(currentTime: NSTimeInterval)
{
player.update()
}
My question is:
For enemyX , how can I get the node to update since SKSpriteNode does not have a default update function that can be overridden and from my game scene
I do not want really write some thing like:
override func update(currentTime: NSTimeInterval)
{
player.update()
enemyX.update()
enemyY.update()
enemy001.update()
...
enemy100.update
}
Do you see what I mean, how do I get around this, does SKSpriteNode need to be a different class?
Maybe actions?
let moveLeft = SKAction.moveByX( 200, y:0, duration: 5)
self.runAction(moveLeft, completion: {
print("----> move left Complete")
})
Thanks.
you subclass SKSpriteNode and and an update method inside that class. then in your update method in your scene you do exactly what you said
player.update()
enemy.update()
etc.
Don't recommend using SKAction unless the behavior of your enemies is extremely simple.

Reuse same sprite node within multiple scenes in sprite kit using Swift

I create sprite node in my GameScene as the following. I would like to reuse createNodeA1 or nodeA1 in other SKScene. How can I do that?
import SpriteKit
class GameScene: SKScene {
var nodeA1: SKNode!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(size: CGSize) {
super.init(size: size)
// Add sprite node to the scene
nodeA1 = createNodeA1()
addChild(nodeA1)
}
}
// Create dot 1
func createNodeA1() -> SKNode {
let spriteNode = SKNode()
spriteNode.position = CGPointMake(CGRectGetMidX(self.frame)/1.5, CGRectGetMidY(self.frame)/2.0)
let sprite = SKSpriteNode(imageNamed: "dot_1")
sprite.zPosition = 3.0
sprite.name = "A1_Broj"
spriteNode.addChild(sprite)
return spriteNode
}
}
There is a few ways to do this.
You could subclass your other scenes to be subclass of the scene with the loadNode function which gives those scenes access to that function.
I asked a question about this last year
Swift multiple level scenes
Another way that might be a bit easier if you are not comfortable with scene subclassing is to just create a subclass of the node itself.
So you create a class
enum EnemyType {
case Normal
case Special
}
class NodeA1: SKSpriteNode {
init(imageNamed: String, enemyType: EnemyType) {
let texture = SKTexture(imageNamed: imageNamed)
if enemyType == .Normal {
super.init(texture: texture, color: SKColor.clearColor(), size: texture.size())
else {
// other init
}
self.zPosition = 1
self.name = ""
// add physics body, other properties or methods for the node
}
}
Than in your SKScenes you can add the node in the init method like so
nodeA1 = NodeA1(imageNamed: "ImageName", enemyType: .Normal)
nodeA1.position = ....
addChild(nodeA1)
this way ever scene where you add the node will use the subclass and therefore include all the properties, set up etc for that node. Another benefit with subclassing is that you could loop through all your nodes using
self.enumerateChildNodesWithName...
and than call custom methods on all nodes.
If you want to subclass your scenes than you would create your baseScene
class BaseScene: SKScene {
// set up all shared stuff in didMoveToView
// have your node function here
// touches began
// physics word and contact collision
// all other stuff that needs to be shared between all level scenes
}
Than your subsequent level scenes would look something like this
class Level1Scene: BaseScene {
override func didMoveToView(view: SKView) {
super.didMoveToView(view) // This lines imports all stuff in BaseScene didMoveToView
// do level 1 specific setUps.
// you can call any function or property from BaseScene, e.g the loadNode function.
}
You than load you level scenes as usual, e.g you transition to level 1 scene and it will automatically use/have access to all the superclass methods and sprites (BaseScene).
So you never call baseScene directly, its gets called automatically.
This applies for other methods in baseScene too, so say you have a Update method in BaseScene.
override func update(currentTime: CFTimeInterval) {.... }
This will work across all your level scenes which are subclasses of BaseScene.
But what happens if you need to add some specific stuff to the update method only relevant in 1 level scene and not all level scenes?
It would be the same process, you create a new update func in the LevelScene and call super.
override func update(currentTime: CFTimeInterval) {
super.update(currentTime) // this calls the baseScene Update method
/// specific stuff for that level only
}
Super simply means the super class of the currentScene, which is BaseScene if the scene is a subclass of it.
Is this helping?
This is additional answer information in terms of subclass of the baseScene. We can create node1thru node10 all in baseScene. Then in Leve1Scene which is subclass of the baseScene, all we have to do is in didMoveToView function state node1.position = CGPointMake(....) for each node that we need in Level1Scene where we would specify node's position.
If we do not need to load all of the 10 nodes in Level1Scene, for example, let's say we don't need to load to the scene node10 we can simply in didMoveToView function just state node10.removeFromParent() and this node will not be loaded to Level1Scene but rest of 9 nodes will.
Note that this example uses only 10 nodes, but you can go with any number of nodes in your baseScene.
This way of subclassing will save you a lot repeatable code in subclasses.

SpriteKit: Add SKSpriteNodes To The View

I basically want to add a SKSpriteNode to the screen and I have no idea where the mistake it. So I include pictures of my three classes participating in this problem. I created a class for all the background stuff. There I use the function addChild() to add my 56 Nodes to the View but they do not appear on the screen. Theoretically (for me) everything should work.
Hopefully you can help me adding the Nodes to the screen!
Edit: Updated the pictures.
When you call this:
GameScene().addChild(image)
you every time inside for create the local GameScene object, add image to this object and when you move to the next iteration of the loop, this object is destroyed. In fact, after your loop thire is no one GameScene object.
Second, you don't need to use var image = SKSpriteNode() in your hintergrund object. You every time set this reference to new object.
You have the cross references in code, try to make somthing like this:
class GameScene: SKScene {
...
override func didMoveToView(view: UIView) {
hintergrund.setBackgroundForScene(self, width: screenWidht)
}
}
class hintergrund {
...
class func setBackgroundForScene(gameScene: GameScene, width: CGFloat) {
...
for (...) {
...
gameScene.addChild(image)
}
}
}

Get position of private object in update method

Is there a way I can access a property of an SKSpriteNode (Its position) in the update() method if it is declared and initiated within a specific method? I have a method called shootLaser() that creates an SKSpriteNode like so:
var laser = SKSpriteNode(imageNamed: "LaserDot.png")
And I need to perform an action using the position of "laser" every time a frame is rendered, and I only know of doing that by using the update() method.
You can access the certain node by its name property:
laserNode.name = "laserNode"
After that , you can use childNodeWithName to access certain node by its name:
let laserNode: SKSpriteNode = parentNode.childNodeWithName("laserNode") as SKSpriteNode
or if laser is added directly to the scene:
let laserNode: SKSpriteNode = childNodeWithName("laserNode") as SKSpriteNode