I'm trying to create a button that switches the scene. I know the code to switch the scene but the code I'm using to do it with a button isn't working.
UPDATE: I have the code working on the first scene, but when I use the same code on another scene (i switched the button and scene) it doesnt work.
anyone know why?
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let gamestartScene = GameStartScene(size: self.frame.size)
var location = CGPoint(x: 0, y: 0)
var touch = touches.anyObject() as UITouch
location = touch.locationInView(self.view)
if menuButton.containsPoint(location){
self.removeChildrenInArray([menuButton,replayButton,highScoreLabel,scoreLabela])
self.view?.presentScene(gamestartScene)
score = 0
}
}
Use locationInNode instead of locationInView
let location = touch.locationInNode(self)
The origin of UIView coordinates and the SKScene coordinates are different. The origin (0,0) of the UIView is at the top left. The origin of the SKScene is at the bottom left. So the functions locationInNode and locationInView will return different results.
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if(button.containsPoint(location)) {
}
}
}
Related
I would like to get the location of the touch in the UI, and use that location to determine what direction the sprite runs. For example, if the touch is on the left side of the screen, the user runs left, and if the touch is on the right side of the screen, the user runs right.
Try something like this
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let position = touch.locationInView(view)
print(position)
}
}
#KnightOfDragon's answer is the right one here.
You should use directly
func locationInNode(_ node: SKNode) -> CGPoint
More about Touch events in SpriteKit over here: documentation
As KnightOfDragon mentioned for SpriteKit the correct way would be
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self) // self is the current SKScene
let node = nodeAtPoint(location)
// To get the touched half of the screen I do this
if location.x < CGRectGetMidX(self.frame) {
// left half touched, do something
}
if location.x > CGRectGetMidX(self.frame) {
// right half touched, do something
}
}
}
I have an SKNode on SpriteKit, which I basically want to be able to drag around the screen, but without having to touch it! Imagine I press the screen anywhere. I then want my SKNode to keep its distance to my finger, so that when I drag it I can see it.
I have this working but the object snaps to the touch.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
let location = touch.locationInNode(self)
circle.position = location
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
let location = touch.locationInNode(self)
circle.position = location
}
}
It's a classic basic problem in game engineering.
There are two solutions...
First solution: when finger first goes down, make a note of the "grab" delta, being the delta from the position of the object, to, the finger.
When the finger moves each time to new position P, subtract the "grab" delta from P before setting the position of the object to P.
"It's that easy"
Second solution: each time the finger moves, don't bother at all with the position P of the finger.
Instead, calculate the delta the finger moved from the previous frame.
(So, simply store the previous position each time so you can calculate that - some systems give you the previous position as a property since it's so common, indeed some systems just give you the delta automatically as a property!)
Then just move the object by that delta.
"It's that easy"
Here is precisely the first solution, in iOS/SpriteKit
class FingerFollower: SKSpriteNode {
var grab: CGVector = CGVector.zero
// "grab" is the usual term for the delta from the object
// to where the finger "grabbed" it...
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let t: UITouch = touches.first! as UITouch
let l = t.location(in: parent!)
grab = (l - position).vector
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let t: UITouch = touches.first! as UITouch
let loc = t.location(in: parent!)
position = loc - grab
}
// NOTE this code uses the absolutely obvious overrides for
// subtraction etc between vectors, which you will need in 100%
// of spritekit projects (Apple forgot about them)
}
Here is precisely the second solution, in iOS/SpriteKit
class FingerFollower: SKSpriteNode {
func setup() {
// NOTE, you MUST have a "setup" call in your sprite subclasses;
// apple forgot to include a "didAppear" for SKNodes
isUserInteractionEnabled = true
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// note that Apple do in fact include the "previous location"
// in sprite kit touches. (In systems where they don't do that,
// you just make a note of it each time.)
let prev = t.previousLocation(in: parent!)
let quickDelta = loc - prev
position = position + quickDelta
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?){
let touch = touches.anyObject() as UITouch!
let touchLocation = touch.locationInNode(self)
let previousLocation = touch.previousLocationInNode(self)
let distanceX = touchLocation.x - previousLocation.x
let distanceY = touchLocation.y - previousLocation.y
circle.position = CGPointMake(circle.position.x + distanceX, circle.position.y + distanceY)
}
I am creating a game for iOS using sprite kit written in swift. I have a bunch of SKSpriteNode on a screen that can be dragged around. They are various shapes such as a person, an apple, a book etc. The problem is, say the person is in front of the apple but you can see the apple behind the person through the transparent pixels in the persons image (png). When you go to touch the apple to move it, the person gets selected because of those transparent pixels instead of the apples.
How do I make a SKSpriteNode that does not respond to touch if it's pixels are transparent?
If you want to select a sprite that is partially hidden by another sprite, you can use nodesAtPoint:CGPoint to obtain an array of nodes that are under the user's touch. You can then iterate over the nodes in the array to find and select the node closest to the touch point. Here's a quick example of how to do that in Swift:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
if let touch = touches.first as? UITouch {
let location = touch.locationInNode(self)
var closest:CGFloat?
let nodes = nodesAtPoint(location)
for node in nodes as! [SKNode] {
if let sprite = node as? SKSpriteNode {
// Calculate the distance from the node to the touch
let dx = location.x - node.position.x
let dy = location.y - node.position.y
let distance = dx*dx + dy*dy
// Find the closest
if closest == nil || distance < closest! {
closest = distance
selected = sprite
}
}
}
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
if let sprite = selected {
if let touch = touches.first as? UITouch {
let location = touch.locationInNode(self)
sprite.position = location
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
selected = nil
}
Here's a clip of shapes being moved by touch with the above code:
In my SpriteKit game I'm trying to add a SKEmitterNode to a SKSpriteNode.
As I move my finger across the screen the fire in this case should move with it.
override func touchesBegan(touches: NSSet, withEvent event:UIEvent) {
var touch = touches.anyObject() as UITouch!
var touchLocation = touch.locationInNode(self)
let body = self.nodeAtPoint(touchLocation)
if body.name == FireCategoryName {
println("Began touch on body")
firePosition = body.position
isFingerOnGlow = true
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
if isFingerOnGlow{
let touch = touches.anyObject() as UITouch!
let touchLocation = touch.locationInNode(self)
let previousLocation = touch.previousLocationInNode(self)
let distanceX = touchLocation.x - previousLocation.x
let distanceY = touchLocation.y - previousLocation.y
firePosition = CGPointMake(firePosition.x + distanceX, firePosition.y + distanceY)
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
isFingerOnGlow = false
}
I have firePosition like this, allowing it to change position on the SKSpriteNode fire, and the SKEmitterNode bloss.
var bloss = SKEmitterNode()
var fire = SKSpriteNode()
var firePosition : CGPoint = CGPointMake(100, 200) { didSet { fire.position = firePosition ; bloss.position = firePosition } }
As expected the two show up at the same place but even though the bloss node has its .targetNode set to fire it doesn't look natural. With natural I mean that if the targetNode moves the emitter will spread particles on the path the body is moving on. For me the emitter just burns upwards and doesn't change form or anything. When I tried letting firePosition only set the sprites position and having a constant position for the emitter, it spread it's particles to different sides according to how I moved the sprite. Sorry if the explanation was vague... Is there a way to fix this?
set the bloss emitterNode's targetNode property to the scene.
bloss.targetNode = self
it doesn't look natural because you made the targetNode the sprite. the emitter is always in the same position relative to the sprite, so you dont get that wavy effect i think you're going for.
So I am super new to swift and iOS development but not totally new to programming, and was just going through some tutorials, but basically my code looks like this:
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let circle = SKShapeNode(circleOfRadius: 30.0)
circle.position = CGPointMake(100, 200)
addChild(circle)
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
let location = touch.locationInNode(self)
let touchedNode = nodeAtPoint(location)
touchedNode.position = location;
}
}
}
When I build this it recognizes and moves the circle when I drag it, but only for about 30 pixels and then I have to touch it again to move it. What am I doing wrong here?
I might be wrong but I believe your finger is leaving the node limits and enters the background. I would set it up as such:
// First make the shape available throughout the code and make sure you have a place to save the touch event for later use.
var circle: SKShapeNode!
var circleTouch: UITouch?
// Create the Shape
override func didMoveToView(view: SKView) {
circle = SKShapeNode(circleOfRadius: 30.0)
circle.position = CGPointMake(100, 200)
addChild(circle)
}
// When a finger is placed on the screen, check if it's in the circle, if it is, keep that touch even in memory so we can reference it later because other fingers could touch other things in the scene.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
if nodeAtPoint(touch.locationInNode(self)) == circle {
circleTouch = touch as? UITouch
}
}
}
// Check if the touch event that is moving is the one that anchored the circle to our finger, if yes, move the circle to the position of the finger.
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
if circleTouch != nil {
if touch as UITouch == circleTouch! {
let location = touch.locationInNode(self)
circle.position = location
}
}
}
}
// Clean up the touch event when the finger anchoring the circle is raised from the screen.
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
if circleTouch != nil {
if touch as UITouch == circleTouch! {
circleTouch = nil
}
}
}
}
You can also use if let statements in there but I checked for nil instead for clarity.
Tokuriku, thanks so much, you we're not quite right but it got me to the eventual answer, here it is
import SpriteKit
class GameScene: SKScene {
var circleTouch: UITouch?
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let circle = SKShapeNode(circleOfRadius: 40.0)
circle.fillColor = UIColor.blackColor()
circle.position = CGPoint(x: size.width * 0.5, y: size.height * 0.2)
circle.name = "userCircle"
addChild(circle)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
if nodeAtPoint(touch.locationInNode(self)).name == "userCircle" {
circleTouch = touch as? UITouch
}
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
if circleTouch != nil {
if touch as UITouch == circleTouch! {
let location = touch.locationInNode(self)
let touchedNode = nodeAtPoint(location)
touchedNode.position = location
}
}
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
if circleTouch != nil {
if touch as UITouch == circleTouch! {
circleTouch = nil
}
}
}
}