I have three nodes lineBlock of the same class MovableBlock on the screen. I want to rotate the lineBlock node someone is touching on the screen.
I've solved this for moving the correct lineBlock node in touchedMoved:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
touch = touches.first!
positionInScene = self.touch?.location(in: self)
let previousPosition = self.touch?.previousLocation(in: self)
let translation = CGVector(dx: (positionInScene?.x)! - (previousPosition?.x)!, dy: (positionInScene?.y)! - (previousPosition?.y)!)
if touchedNode?.name?.contains("LineBlock") == true {
(touchedNode as! MovableBlock).selected = true
(touchedNode as! MovableBlock).parent!.parent!.run(SKAction.move(by: translation, duration: 0.0))
}
}
But I have not been able to do the same within my UIRotationRecognizer function. In my rotate function, it simply rotates the first node, regardless of which lineBlock (of class MovableBlock) I am touching:
func rotate(_ sender: UIRotationGestureRecognizer){
if lineBlock.selected == true {
lineBlock.run(SKAction.rotate(byAngle: (-(self.rotationRecognizer?.rotation)!*2), duration: 0.0))
rotationRecognizer?.rotation = 0
}
}
For reference, here's how I define touchedNode (in touchBegan):
touches: Set<UITouch>, with event: UIEvent?) {
touch = touches.first!
positionInScene = self.touch?.location(in: self)
touchedNode = self.atPoint(positionInScene!)
UIGestureRecognizers have a location(in:UIView) method. You can use self.view?.convert(sender.location(in: self.view), to: self) to get the UIRotationGestureRecognizer's location and use logic similar to that in touchesBegan.
convert(_:to:) will make sure that the point is in your scene's coordinate space.
Related
I'm creating a SpriteKit game that involves touching falling targets. Currently, the targets are too difficult to catch on first touch (touchesBegan:), and only seem to be touchable by positioning your finger ahead of time (touchesMoved:). Is there a technique for dampening touches or widening the touch location to make the first touch more effective? My code looks something like this right now:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let positionInScene = touch.location(in: self)
print(positionInScene)
guard let touchedNode = self.nodes(at: positionInScene).first as? SKSpriteNode else { return }
if let dot = touchedNode.name {
if dot == "dot" {
removeTarget(touchedNode)
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let positionInScene = touch.location(in: self)
print(positionInScene)
guard let touchedNode = self.nodes(at: positionInScene).first as? SKSpriteNode else { return }
if let dot = touchedNode.name {
if dot == "dot" {
removeTarget(touchedNode)
}
}
}
Are your entities too small? With the given info, I recreated a small scene in a playground, and all is working as expected. If I have a presented child node, defined as
let circle = SKShapeNode(circleOfRadius: 20)
And I have defined both the physicsWorld of the SKScene, and physicsBody of the SKNode, with the given touchesBegan, there is no problem in detecting a collision.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let location = touches.first?.location(in: self)
if self.atPoint(location) === circle {
print("TOUCH")
}
}
}
I have a sprite node in game that when touched makes an action of the node being pressed and when the touches began event is called and then goes back to the normal size when the touches ended event is called. my problem is when i press down on the node and then move my finger outside of the node it doesn't go back to its original size after I take my finger off the screen.
I tried using multiple things in the touches moved section of the code to try and get it to go back to its original size after i've moved my finger outside of the node while holding the touch down but it didn't work. My code is below
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
let node = self.atPoint(touch.location(in: self))
let pushdown = SKAction.scale(to: 0.8, duration: 0.1)
if node == mainMenu.settingsButton {
node.run(pushdown)
} else if node == mainMenu.viewMapButton {
node.run(pushdown)
}else if node == mainMenu.shopButton {
node.run(pushdown)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
var location: CGPoint = touch.location(in: self)
let node: SKNode = atPoint(location)
let pushUp = SKAction.scale(to: 1.0, duration: 0.2)
if node != mainMenu.settingsButton {
//node.run(pushUp)
} else if touch.phase == .moved || touch.phase == .cancelled {
node.run(pushUp)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
let node = self.atPoint(touch.location(in: self))
let pushUp = SKAction.scale(to: 1.0, duration: 0.2)
if node == mainMenu.settingsButton {
node.run(pushUp)
//Run Sound Here
let scene = SettingsMenu(fileNamed:"SettingsMenu")
scene?.scaleMode = .aspectFill
//scene?.backgroundColor = UIColor.lightGray
let transition = SKTransition.crossFade(withDuration: 0.5)
self.scene?.view?.presentScene(scene!, transition: transition)
} else if node == mainMenu.viewMapButton {
node.run(pushUp)
}
}
How can i get it to go back to the original size after i've moved my finger outside of the nodes location while holding the touch down?
In touchesBegan, touchesMoved, touchesEnded you are referring always the current node at point. let node = self.atPoint(touch.location(in: self))
Why not to keep a reference to the initial node which was detected in touchesBegan to scale it back if the current node atPoint is not equal to the initial one. I assume this would solve your issue.
EDIT:
To capture the node on first touch ...
private var currentTouchedNode : SKNode?
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
let touch: UITouch = touches.first!
currentTouchedNode = self.atPoint(touch.location(in: self))
let pushdown = SKAction.scale(to: 0.8, duration: 0.1)
if currentTouchedNode == mainMenu.settingsButton {
currentTouchedNode.run(pushdown)
} else if currentTouchedNode == mainMenu.viewMapButton {
currentTouchedNode.run(pushdown)
}else if currentTouchedNode == mainMenu.shopButton {
currentTouchedNode.run(pushdown)
}
}
In touchesMoved, touchesEnded you would compare on if the current node is equal currentTouchedNode and resize it if needed
Ok so currently in my game I have a ball that can be thrown. However, I want to make so that the user can only throw the ball past a certain point. I have been trying this for a while but I can't figure it out. How can I do this?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch?
let location = touch?.location(in: self)
if ball.frame.contains(location!) {
touchPoint = location!
touching = true
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch?
let location = touch?.location(in: self)
touchPoint = location!
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func update(_ currentTime: TimeInterval) {
if touching {
let dt:CGFloat = 2.8/60.0
let distance = CGVector(dx: touchPoint.x-ball.position.x, dy: touchPoint.y-ball.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
ball.physicsBody!.velocity=velocity
}
}
Here, I have an image of my game so far. Currently, the ball can be dragged, thrown, or swiped anywhere on the screen. However, I want to make so the ball can only be touched, thrown, or dragged below the middle of the screen. But I want the ball to continue being thrown if the user's finger accidently goes about the limit.
you will have to adjust the coordinates for your bottom area based on actual size and game scene anchorPoint. for example "100" in the sample assumes that your scene has an anchorPoint of (0,0) and the size of the bottom scroll area is 100.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let location = touch.location(in: self)
guard location < 100 else { return }
//if ball.frame.contains(location!) {
touchPoint = location!
touching = true
//}
}
}
I have this node I can rotate if my finger is on it. The problem Im having is that when Im rotating the node and my finger slides off the node and onto the view it stops rotating. How would I still be able to have that touch if my finger isn't on the node anymore?
For example I have a UISlider in my app and if I have my finger on my slider and I move down and then try to move the slider to the left and right it still works. How would I be able to do the same for my node where the touch event is still there? Let me know if you don`t get what I'm saying. Here is the code I'm using to rotate my node:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if node.name == "circle"
//lets user rotate when there is one finger on node.
let dy = circle.position.y - location.y
let dx = circle.position.x - location.x
let angle2 = atan2(dy, dx)
circle.zRotation = angle2
}
You should store the last node in a instance var and then using that try to keep rotating even if it didnt touch it.
class MyClass {
var lastNodeSelected:SKNode?
override func touchesStart(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if node.name == "circle"
lastNodeSelected = node
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
if lastNodeSelected != nil {
let touchedNode = lastNodeSelected!
let dy = touchedNode.position.y - location.y
let dx = touchedNode.position.x - location.x
let angle2 = atan2(dy, dx)
touchedNode.zRotation = angle2
}
override func touchesEnd(touches: Set<UITouch>, withEvent event: UIEvent?) {
lastNodeSelected = nil
In the scene of a finger you can move the object. But if you just speed up the movement of the finger on the screen - the object remains in place. Is it possible to accelerate the speed of its movement? Duration is already set to 0
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.locationInNode(self)
let node = self.nodeAtPoint(touchLocation)
if (node.name == "circle") {
let moveAction = SKAction.moveTo(touchLocation, duration: 0)
figureUser.runAction(moveAction)
}
}
Your problem is not the speed, your problem is you are moving so fast on the screen, that your touch is not recognizing a node underneath it anymore because the node is physically not underneath it. How you handle this problem is on the touch begin event, you check for a node, and assign this node to a variable. Then on the touch move event, update the new variable. Finally on touch end, clear the variable. Note, you would need to handle this code for things like multi touch
var movingNode : SKNode?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.locationInNode(self)
let node = self.nodeAtPoint(touchLocation)
if (node.name == "circle") {
movingNode = node
let moveAction = SKAction.moveTo(touchLocation, duration: 0)
figureUser.runAction(moveAction)
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.locationInNode(self)
let moveAction = SKAction.moveTo(touchLocation, duration: 0)
//at this point I am lost, how does node even relate here,
//is figureUser suppose to be node?
figureUser.runAction(moveAction)
}