Dragging of SKShapeNode Not Smooth - ios

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
}
}
}
}

Related

How to detect precisely when a SKShapeNode is touched?

I'm working with Swift and SpriteKit.
I have the following situation :
Here, each of the "triangles" is a SKShapenode.
My problem is that I would like to detect when someone touches the screen which triangle is being touched.
I assume that the hitbox of all these triangles are rectangles so my function returns me all the hitboxes touched while I only want to know which one is actually touched.
Is there any way to have a hitbox that perfectly match the shape instead of a rectangle ?
Here's my current code :
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touch = touches.first
let touchPosition = touch!.locationInNode(self)
let touchedNodes = self.nodesAtPoint(touchPosition)
print(touchedNodes) //this should return only one "triangle" named node
for touchedNode in touchedNodes
{
if let name = touchedNode.name
{
if name == "triangle"
{
let triangle = touchedNode as! SKShapeNode
// stuff here
}
}
}
}
You could try to use CGPathContainsPoint with a SKShapeNode instead of nodesAtPoint, which is more appropriate:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touch = touches.first
let touchPosition = touch!.locationInNode(self)
self.enumerateChildNodesWithName("triangle") { node, _ in
// do something with node
if node is SKShapeNode {
if let p = (node as! SKShapeNode).path {
if CGPathContainsPoint(p, nil, touchPosition, false) {
print("you have touched triangle: \(node.name)")
let triangle = node as! SKShapeNode
// stuff here
}
}
}
}
}
This would be the easiest way of doing it.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
for touch in touches {
let location = touch.locationInNode(self)
if theSpriteNode.containsPoint(location) {
//Do Whatever
}
}
}
The way I do this with Swift 4:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
let touchPosition = touch.location(in: self)
let touchedNodes = nodes(at: touchPosition)
for node in touchedNodes {
if let mynode = node as? SKShapeNode, node.name == "triangle" {
//stuff here
mynode.fillColor = .orange //...
}
}
}

SpriteKit - How can I catch the event when the touch moves over a sprite, but didn't actually begin on the sprite

In SpriteKit, I want to catch event when touch moves over a sprite, but hasn't actually started on this sprite, but on another piece of SKScene.
I can catch touchesBegan inside the SKSpriteNode A if the touch starts on it and then dragged over it, but not when touch started on another node - B - and then dragged over my node - A. Anyone knows how to catch this one, because I think I am doing something wrong here.
try this:
sorry this is swift.. but you can easily do the same thing in obj c
import SpriteKit
class GameScene: SKScene {
let sprite = SKSpriteNode(color: SKColor.redColor(), size: CGSizeMake(100, 100))
var startedOutsideSprite = true
override init(size: CGSize) {
super.init(size: size)
sprite.position = CGPointMake(size.width/2, size.height/2)
addChild(sprite)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let location = touch.locationInNode(self)
if !sprite.containsPoint(location) {
startedOutsideSprite = true
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let location = touch.locationInNode(self)
if sprite.containsPoint(location) && startedOutsideSprite {
print("yayyy")
// your code here
}
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
startedOutsideSprite = false
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Thanks #Christian W. but I have a simpleer solution for now, although it is not what I had on mind actually:
Just put this in Scene and catch the touchesMoved inside it, having this code inside it:
SKNode * draggedOverNode = [self nodeAtPoint:location];
[draggedOverNode touchesMoved:touches withEvent:event];
And implement the touchesMoved function inside that object that actually extends the SKNode (which most of your classes will do).

How do I use buttons or a slider to rotate a sprite?

I am making a game for iOS in Swift with SpriteKit, the game is currently just a ball moving around with a sword.
I have anchored the sword to the bottom of the sword, but I need to know how to control the direction of rotation with 2 buttons or a slider of some sort.
Here is my code:
import SpriteKit
let sprite = SKSpriteNode(imageNamed:"Player")
let weapon = SKSpriteNode(imageNamed: "weapon")
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let initialPlayerLocation = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
/* Setup your scene here */
//player
sprite.setScale(1.0)
sprite.position = initialPlayerLocation
sprite.zPosition = 20
self.addChild(sprite)
//weapon
weapon.setScale(1.0)
weapon.position = initialPlayerLocation
weapon.zPosition = -20
weapon.anchorPoint = CGPointMake(0.5,0.0);
self.addChild(weapon)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
var move = SKAction.moveTo(location, duration:1.0)
sprite.runAction(move)
weapon.runAction(move)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Okay, I think I understand what you are trying to do. It involves a lot of code, but in theory it is pretty simple. We detect if the touch is inside the left or right buttons (which in this example, I've made them SKSpriteNodes), and then set boolean values for the update method to use and rotate the weapon node (I'm assuming this is the sword you were talking about).
I've also included a variable that lets you set the speed of rotation. As you did not say what speed you were looking for, I left it up to you.
At the top of the program:
let sprite = SKSpriteNode(imageNamed:"Player")
let weapon = SKSpriteNode(imageNamed: "weapon")
let leftButton = SKSpriteNode(imageNamed: "leftButton")
let rightButton = SKSpriteNode(imageNamed: "rightButton")
var leftPressed = false
var rightPressed = false
let weaponRotateSpeed = 0.01 // Change this for rotation speed of weapon
And then modify touchesBegan to look like this:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if (leftButton.containsPoint(p: location))
leftPressed = true
else if (rightButton.containsPoint(p: location))
rightPressed = true
else {
var move = SKAction.moveTo(location, duration:1.0)
sprite.runAction(move)
weapon.runAction(move)
}
}
In update, add this code:
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if (leftPressed && rightPressed) {
// Do nothing, as both buttons are pressed
} else if (leftPressed) {
weapon.zRotation -= weaponRotateSpeed
} else if (rightPressed) {
weapon.zRotation += weaponRotateSpeed
}
}
We want to stop the weapon from rotating when we detect when the finger has moved off the button like so:
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if (leftButton.containsPoint(p: location))
leftPressed = true
else
leftPressed = false
if (rightButton.containsPoint(p: location))
rightPressed = true
else
rightPressed = false
}
}
And at last, we need to detect when your finger has left the screen:
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (leftButton.containsPoint(p: location))
leftPressed = false
if (rightButton.containsPoint(p: location))
rightPressed = false
}
}

Detect touch on SKNode (swift)

I have created a container node to put in all my SKSpriteNodes that need to be moved all in one touch, I can detect touches on them normally in iOS 8 but in iOS 7 I can only detect touches on my main node and when I touch an SKSpriteNode that it's in the container node nothing happens.. How can I fix this?
let lvlsNode = SKNode()
override func didMoveToView(view: SKView) {
self.addChild(lvlsNode)
axe = SKSpriteNode(imageNamed:"axe")
axe.anchorPoint = CGPointMake(1, 0)
axe.size = CGSizeMake(axe.size.width/1.4, axe.size.height/1.4)
axe.position = CGPointMake(0+screenWidth/7, shield.position.y-shield.size.width*1.4)
axe.zPosition = 12
axe.name = "axe"
lvlsNode.addChild(axe)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let node = nodeAtPoint(location)
if node.name == "axe" {
// do something.... this work in iOS8 but not in iOS 7.1
}
Yournode.name = "nodeX"
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as UITouch!
if atPoint((touch?.location(in: self))!).name == Yournode.name {
//Your code
}
}

SKNode containsPoint is not working

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)) {
}
}
}

Resources