How to manipulate a sprite kit's SKNode? - swift

I have coded a starry sky as follows. Now I would like to remove a star when a user touches it. The following code however removes all the stars on the sky. How can I access a single star node to manipulate it?
override func didMoveToView(view: SKView) {
for(var i = 0; i < stars ; i++) {
planetsLayer.addChild(createPlanet(view))
}
self.addChild(planetsLayer)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(planetsLayer)
let touchedLayer = nodeAtPoint(location)
let touchedNode = nodeAtPoint(location)
touchedNode.removeFromParent()
}
func createPlanet() -> SKShapeNode {
...
var shapeNode = SKShapeNode();
...
return shapeNode
}

Give your planet nodes a name when you create them, and check for this name when you remove them. This will keep you from deleting other nodes that are not planets.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(planetsLayer)
let touchedNode = nodeAtPoint(location)
if touchedNode.name == "planet" {
touchedNode.removeFromParent()
}
}
}
func createPlanet() -> SKShapeNode {
...
var shapeNode = SKShapeNode()
shapeNode.name = "planet"
...
return shapeNode
}

Related

Move sprite forever

I am trying to move sprite to the right when I press a button on the screen. However, when I try doing it I only have a solution to move the sprite to a certain point. So... I want the sprite to move to the right forever or until i do something else.
This is in Xcode using Swift in SpriteKit.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch : AnyObject in touches{
let pointInTouch = touch.locationInNode(self)
let tappedNode = nodeAtPoint(pointInTouch)
let tappeNodeName = tappedNode.name
if tappeNodeName == "Btn"{
player.physicsBody?.velocity = CGVectorMake(0, 0)
let action = SKAction.applyImpulse(CGVectorMake(400, 0), duration: 1)
player.runAction(SKAction.repeatActionForever(action))
print("Touched!")
}
}
}
You can simply move your node to the right untill it does exist the scene
class GameScene: SKScene {
private var player: SKSpriteNode!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else { return }
let pointInTouch = touch.locationInNode(self)
let tappedNode = nodeAtPoint(pointInTouch)
if tappedNode.name == "Btn"{
let moveToRight = SKAction.moveToX(self.frame.width + player.frame.width, duration: 5)
player.runAction(moveToRight)
}
}
}
Update: constant speed
If you want the player to move with constant speed you can use this code.
As you can see I am calculating the duration using space / speed. You just need to find the best constant value for speed for your scenario.
class GameScene: SKScene {
private var player: SKSpriteNode!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else { return }
let pointInTouch = touch.locationInNode(self)
let tappedNode = nodeAtPoint(pointInTouch)
let deltaX = self.frame.width + player.frame.width - player.position.x
let speed: CGFloat = 10 // <-- change this to find the best value for you
let duration: NSTimeInterval = NSTimeInterval(deltaX / speed)
if tappedNode.name == "Btn"{
let moveToRight = SKAction.moveByX(deltaX, y:0, duration: duration)
player.runAction(moveToRight)
}
}
}

How to make a node replace another in chronological order?

Lets say I have five dice like so:
When I tap, Id like the die face with one dot to replace the one with two dots and (moving the 1 die across the row while moving the other die down the row) and if the last die is at the end, get it to replace the first die in the row. Would replacing the SKTextures have something to do with this? Thank you in advance.
Edit:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
let texRight = SKAction.setTexture(SKTexture(imageNamed: "DieFace1"))
DieFace2.runAction(texRight)
}
}
Edit 2:
import SpriteKit
var arrayOfDieFaces = [onE, twO, threE, fouR, fivE]
class GameScene: SKScene {
}
func replace() {
var count = 1
while count <= 5 {
let changeTexture = SKAction.setTexture(SKTexture(imageNamed: "Dice\(count)"))
if count == 5 {
arrayOfDieFaces[0].runAction(changeTexture)
}
else{
arrayOfDieFaces[count].runAction(changeTexture)
}
count += 1
}
arrayOfDieFaces.append(arrayOfDieFaces[0])
arrayOfDieFaces.dropFirst()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
replace()
}
}
This approach changes the position of each dice instead of changing its texture. By changing the positions, it maintains the ability to uniquely identify each dice (e.g., by name).
First, create an SKNode called dice and define the size and spacing between each dice.
let dice = SKNode()
let diceSize = CGSizeMake(10, 10)
let spacing:CGFloat = 2
Second, create your dice nodes and add them, in order, to the dice SKNode and set the positions of each dice with
for i in 0..<6 {
let sprite = SKSpriteNode ...
sprite.physicsBody = SKPhysicsBody( ...
sprite.physicsBody?.categoryBitMask = UInt32(0x1 << i)
sprite.position = CGPointMake(CGFloat(i)*(diceSize.width+spacing) , 0)
dice.addChild (sprite)
}
// Center SKNode in the view
dice.position = CGPointMake (CGRectGetMidX(view.frame),CGRectGetMidY(view.frame))
addChild (dice)
Note that the position of each dice is based on the position of the node within the array.
Lastly, add the following to your code.
func rotate() {
// Get last element in the array
if let last = dice.children.last {
// Remove the last dice from the SKNode
last.removeFromParent()
// Move the last to the front of the array
dice.insertChild(last, atIndex: 0)
// Update the positions of the
for i in 0..<dice.children.count {
dice.children[i].position = CGPointMake(CGFloat(i)*(diceSize.width+spacing) , 0)
}
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for _ in touches {
rotate()
}
}
I think this should work (not tested) assuming your DieFaces are from 1 to 6:
var arrayOfDieFaces = [DieFace1, DieFace2, DieFace3, DieFace4, DieFace5, DieFace6]
func changeTextures() {
var count = 1
while count <= 6 {
let changeTexture = SKAction.setTexture(SKTexture(imageNamed: "DieFace\(count)"))
if count == 6 {
arrayOfDieFaces[0].runAction(changeTexture)
}
else{
arrayOfDieFaces[count].runAction(changeTexture)
}
count += 1
}
arrayOfDieFaces.append(arrayOfDieFaces[0])
arrayOfDieFaces.dropFirst()
}
Just call this fucntion inside the touchesBegan() function.

How to move a node to the position I touch in SpriteKit with Swift?

I am creating a game like Doodle Jump (without the accelerometer).
I have been trying to figure this out, but I can't seem to make it run as smoothly as I've been hoping.
Here is my code for my touches functions:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
lastTouch = touchLocation
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
lastTouch = touchLocation
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
lastTouch = nil
}
override func update(currentTime: CFTimeInterval) {
if let touch = lastTouch {
let impulseVector = CGVector(dx: 400, dy: 400)
player.physicsBody?.applyImpulse(impulseVector)
}
}
From what I'm gathering, you want to change the impulseVector that is applied to the player based on where the touch is. I would imagine the code looking something like this:
// At the top of your code
let scale = 0.5
// Update function
override func update(currentTime: CFTimeInterval) {
if let touch = lastTouch {
let xOffset = (touch.x - player.position.x)*scale
let yOffset = (touch.y - player.position.y)*scale
let impulseVector = CGVector(dx: xOffset, dy: yOffset)
player.physicsBody?.applyImpulse(impulseVector)
}
}
This will pull the player node up with more force the further away it is from the touch. If you are trying to pull the player with the same amount of force, no matter where they are (which may be more likely in this case), you would do something like this:
// At the top of your code
let xPlayerForce = 20
let yPlayerForce = 30
// Update function
override func update(currentTime: CFTimeInterval) {
if let touch = lastTouch {
var xForce = 0.0
var yForce = 0.0
let xTouchOffset = (touch.x - player.position.x)
let yTouchOffset = (touch.y - player.position.y)
if xTouchOffset > 0.0 {
xForce = xPlayerForce
} else if xTouchOffset < 0.0 {
xForce = -xPlayerForce
} // else we do nothing
if yTouchOffset > 0.0 {
yForce = yPlayerForce
} // here you can choose whether you want it to push
// the player node down, using similar code from the
// above if statement
let impulseVector = CGVector(dx: xForce, dy: yForce)
player.physicsBody?.applyImpulse(impulseVector)
}
}

Creating a "wall" node in SpriteKit

I'm working with SpriteKit, Xcode 6 and Swift
I have a spritenode declared like this:
let firstCircle = SKSpriteNode(imageNamed: "Circle")
firstCircle.physicsBody = SKPhysicsBody(circleOfRadius: 7)
firstCircle.physicsBody?.affectedByGravity = false
and another spritenode which is bigger circle declared like this :
let wall = SKSpriteNode(imageNamed: "Circle")
wall.physicsBody = SKPhysicsBody(circleOfRadius: 50)
wall.physicsBody?.affectedByGravity = false
The node firstcircle follows my finger and the node wall stay at the middle of the screen, and I want that the two nodes cannot enter in collision together, but with my actual code, firstcircle just cross the wall, what have I to do in order to fix that problem ?
Here is how I move the node :
let scale:CGFloat = 10.0
let damping:CGFloat = 0.94
var departCircleLocation = CGPoint()
var departTouchLocation = CGPoint()
var currentTouchLocation = CGPoint()
var isTouchActive = false
override func touchesBegan(touches: NSSet, withEvent event: UIEvent)
{
isTouchActive = true
for touch: AnyObject in touches
{
departTouchLocation = touch.locationInNode(self)
}
departCircleLocation = firstCircle.position
currentTouchLocation = departTouchLocation
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent)
{
for touch: AnyObject in touches
{
currentTouchLocation = touch.locationInNode(self)
}
moveCircles()
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent)
{
isTouchActive = false
}
override func update(currentTime: NSTimeInterval)
{
moveCircle()
}
func moveCircle()
{
if isTouchActive
{
var dx = CGFloat(departCircleLocation.x - departTouchLocation.x + currentTouchLocation.x - firstCircle.position.x) * scale
var dy = CGFloat(departCircleLocation.y - departTouchLocation.y + currentTouchLocation.y - firstCircle.position.y) * scale
firstCircle.physicsBody?.velocity = CGVectorMake(dx, dy)
}
else
{
let dx = firstCircle.physicsBody!.velocity.dx * damping
let dy = firstCircle.physicsBody!.velocity.dy * damping
firstCircle.physicsBody!.velocity = CGVectorMake(dx, dy)
}
}

SpriteKit childNodeWithName can't find existing node

I have this code in SKScene:
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
var touch: AnyObject = touches.anyObject()
var point = getPoint(touch.locationInNode(self))
var name = NSStringFromCGPoint(point)
for children in self.children {
if (children as SKSpriteNode).name == name {
println("exist!")
}
}
var tempNode = self.childNodeWithName(name)
}
I see "exist!" in log, so there is a node with this name in children array, but tempNode is nil. The self.childNodeWithName("//" + name)call returns nil too.
Here is how to accomplish this in Swift... Hope this helps!
var mySprite: SKSpriteNode = childNodeWithName("mySprite") as SKSpriteNode
I've found this strangeness using Swift 2.2, maybe a bug .. you can't use NSStringFromCGPoint and childNodeWithName without cleaning the string from braces:
Using this little method:
func removeBraces(s:String)->String {
return s.stringByTrimmingCharactersInSet(NSCharacterSet.init(charactersInString: "{}"))
}
when you add your SKSpriteNode do for example:
...
mySpriteNode.name = removeBraces(NSStringFromCGPoint(mySpriteNode.position))
...
and to retrieve it for your case :
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
var touch: AnyObject = touches.anyObject()
var point = getPoint(touch.locationInNode(self))
var name = removeBraces(NSStringFromCGPoint(point))
if let child = self.childNodeWithName(name) {
print("I've found: \(child)")
}
...
}