How to detect if I tapped a SKShapeNode - ios

override func didMove(to view: SKView) {
view.scene?.anchorPoint = CGPoint(x: 0,y : 0)
castle = SKShapeNode(circleOfRadius: ballRadius1)
castle.physicsBody = SKPhysicsBody(circleOfRadius:ballRadius1)
castle.fillColor = .white
castle.name = "castle"
castle.position = CGPoint(x: 0, y: 0)
castle.physicsBody?.isDynamic = false
castle.physicsBody?.affectedByGravity = false;
castle.isUserInteractionEnabled = true
self.addChild(castle)
I have a circle in the middle of the screen, I want to make it disappear when I tap it. Can you please help me? It is an SKShapeNode and has a PhysicsBody with the same radius

You could use touchesBegan and make it look like so
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let nodeITapped = atPoint(pointOfTouch)
let nameOfTappedNode = nodeITapped.name
if nameOfTappedNode == "castle"{
//make it do whatever you want
castle.removeFromParent()
}
}
}
you could also implement it in touchesEnded instead if you want the node to disappear as soon as the user releases the touch.

Related

make SKPhysicsBody unidirectional

I have a SKSpriteNode as a ball, it's been given all the SKPhysicsBody properties move around in all direction. What I want now is to make it unidirectional (only move in that direction it hasn't move to before and not go back in to a path it had move upon). Currently I have following thoughts on this the problem,
make a fieldBitMask, to the path that is iterated by it and repel
the ball to not go back
apply some kind of force/ impulses on the ball from touchesBegan/ touchesMoved method to keep it from going back
something that can be handled in update method
a lifesaver from stackflowoverflow, who is coding even on the weekend :)
Supporting Code snippets for better understanding,
//getting current touch position by using UIEvent methods
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {return}
let location = touch.location(in: self)
lastTouchPoint = location
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {return}
let location = touch.location(in: self)
lastTouchPoint = location
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
lastTouchPoint = nil
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
lastTouchPoint = nil
}
//ball created
func createPlayer(){
player = SKSpriteNode(imageNamed: "player")
player.position = CGPoint(x: 220, y: 420)
player.zPosition = 1
//physics for ball
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 2)
player.physicsBody?.allowsRotation = false
player.physicsBody?.linearDamping = 0.5
player.physicsBody?.categoryBitMask = collisionTypes.player.rawValue
player.physicsBody?.contactTestBitMask = collisionTypes.finish.rawValue
player.physicsBody?.collisionBitMask = collisionTypes.wall.rawValue
addChild(player)
}
//unwarp the optional property, calclulate the postion between player touch and current ball position
override func update(_ currentTime: TimeInterval) {
guard isGameOver == false else { return }
if let lastTouchPosition = lastTouchPoint {
//this usually gives a large value (related to screen size of the device) so /100 to normalize it
let diff = CGPoint(x: lastTouchPosition.x - player.position.x, y: lastTouchPosition.y - player.position.y)
physicsWorld.gravity = CGVector(dx: diff.x/100, dy: diff.y/100)
}
}
Well it was a combination little hacks in touchesBegan/ touchesMoved and update func,
First you need to catch on which touch occurred, get it's name (in my
case I made nodes which had alpha of 0, but become visible upon
moving over them i.e alpha 1). In touchesBegan, touchesMoved as follow
guard let touch = touches.first else {return}
let location = touch.location(in: self)
lastTouchPoint = location
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name
{
if name == "vortex"
{
touching = false
self.view!.isUserInteractionEnabled = false
print("Touched on the interacted node")
}else{
self.view!.isUserInteractionEnabled = true
touching = true
}
}
}
Second use a BOOL touching to track user interactions, on the screen by using getting a tap recogniser setup, as follow
func setupTapDetection() {
let t = UITapGestureRecognizer(target: self, action: #selector(tapped(_:)))
view?.addGestureRecognizer(t)
}
#objc func tapped(_ tap: UITapGestureRecognizer) {
touching = true
}
Finally in update put checks as follow,
guard isGameOver == false else { return }
self.view!.isUserInteractionEnabled = true
if(touching ?? true){
if let lastTouchPosition = lastTouchPoint {
//this usually gives a large value (related to screen size of the device) so /100 to normalize it
let diff = CGPoint(x: lastTouchPosition.x - player.position.x, y: lastTouchPosition.y - player.position.y)
physicsWorld.gravity = CGVector(dx: diff.x/100, dy: diff.y/100)
}
}
}

Need Help giving sprite node button an animation while it is being pressed

I'm sure I sound like a completed noob but I'm working on my first iOS game and it includes a shoot button which is an arcade style button that I would like to animate by swapping to the "shootPressed" image while the user holds it down then swaps back when released.
Here I have my touches began, moved, and ended. Keep in my mind I left out any nonconflicting code. I've been stuck on this problem for the last two days.
UPDATE: I've included the function I used for creating my button node
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in:self)
let node = self.atPoint(location)
if(node.name == "Shoot") {
shootButton.texture = SKTexture(imageNamed: "shootPushed")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in:self)
let node = self.atPoint(location)
if (node.name == "Shoot") {
shootBeams()
shootButton.texture = SKTexture(imageNamed: "shootUnpushed" )
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in:self)
let node = self.atPoint(location)
if node.name == ("shoot") {
shootButton.texture = SKTexture(imageNamed: "shootPushed")
}
}
}
func addButtons() {
var buttonT1 = SKTexture(imageNamed: "shootUnpushed")
var buttonT2 = SKTexture(imageNamed: "shootPushed")
var shootButton = SKSpriteNode(texture: buttonT1)
shootButton.name = "Shoot"
shootButton.position = CGPoint(x: self.frame.maxX - 130, y: leftButton.position.y)
shootButton.zPosition = 7
shootButton.size = CGSize(width: shootButton.size.width*0.2, height: shootButton.size.height*0.2)
self.addChild(shootButton)
}
The shootButton you create in addButtons is a local variable.
So, when you do shootButton.texture = SKTexture(imageNamed: "shootPushed") inside the touchesMoved this is certainly not referring to the same node.
As this apparently compiles this would mean that you already have a property shootButton in the class. The problem would most likely be fixed if you change the var shootButton = SKSpriteNode(texture: buttonT1) in you addButtons function.
It should simply initialize the shootButton-property rather than creating a new local variable:
func addButtons() {
shootButton = SKSpriteNode(texture: buttonT1)
shootButton.name = "Shoot"
shootButton.position = CGPoint(x: self.frame.maxX - 130, y: leftButton.position.y)
shootButton.zPosition = 7
shootButton.size = CGSize(width: shootButton.size.width*0.2, height: shootButton.size.height*0.2)
self.addChild(shootButton)
}

UIImage goes where i tap (But i need it so you can ONLY drag it)

I have a UIImage and I linked it into my code and named it "Person"
At the moment where ever I tap on the screen, the UIImage jumps to that position, but I want it so the image can ONLY move if I drag it. Here's my code:
var location = CGPointMake(0, 0)
#IBOutlet var Person: UIImageView!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch : UITouch = touches.first as UITouch!
location = touch.locationInView(self.view)
Person.center = location
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch : UITouch = touches.first as UITouch!
location = touch.locationInView(self.view)
Person.center = location
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Person.center = CGPointMake(160, 330)
}
I'd use gesture recognizers - much less to code. Also, I use a "three step" gesture in case there is more than one element to move.
Step #1: The user taps on a subview and a dashed border indicates that the subview is in edit mode.
Step #2: The user pans the subview to a new location in the superview.
Step #3: The user taps anywhere else in the superview (including a new subview to edit it next) to take the first subview out of edit mode.
Subview code:
var _editMode = false
var editMode:Bool {
return _editMode
}
var borderPath = UIBezierPath()
var dashedBorder = CAShapeLayer()
func drawDashedBorder() {
borderPath = UIBezierPath()
borderPath.move(to: CGPoint(x: 3, y: 3))
borderPath.addLine(to: CGPoint(x: self.frame.width-3, y: 3))
borderPath.addLine(to: CGPoint(x: self.frame.width-3, y: self.frame.height-3))
borderPath.addLine(to: CGPoint(x: 3, y: self.frame.height-3))
borderPath.close()
dashedBorder = CAShapeLayer()
dashedBorder.strokeColor = UIColor.white.cgColor
dashedBorder.fillColor = UIColor.clear.cgColor
dashedBorder.lineDashPattern = [2, 2]
dashedBorder.lineDashPhase = 0.0
dashedBorder.lineJoin = kCALineJoinMiter
dashedBorder.lineWidth = 1.0
dashedBorder.miterLimit = 10.0
dashedBorder.path = borderPath.cgPath
let animation = CABasicAnimation(keyPath: "lineDashPhase")
animation.fromValue = 0.0
animation.toValue = 15.0
animation.duration = 0.75
animation.repeatCount = 10000
dashedBorder.add(animation, forKey: "linePhase")
self.addSublayer(dashedBorder)
_editMode = true
}
func removeDashedBorder() {
dashedBorder.removeFromSuperlayer()
_editMode = false
}
This is not production code, so I never did quite figure out how to make the "moving dashed" animation run forever (which may be good, as it could be a performance issue).But 10000 seconds seems to be pretty generous for editing.
Here's the superview code, with the assumption you have imageView1 and imageView2 as subviews:
var editMode = false
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer()
let panGesture = UIPanGestureRecognizer()
tapGesture.numberOfTapsRequired = 1
tapGesture.addTarget(self, action: #selector(editImage))
self.addGestureRecognizer(tapGesture)
panGesture.addTarget(self, action: #selector(moveImage))
self.addGestureRecognizer(panGesture)
}
func editEye(_ recognizer:UITapGestureRecognizer) {
let p = recognizer.location(in: self)
if !editMode {
if (imageView1.layer.hitTest(p) != nil) {
imageView1.drawDashedBorder()
self.editMode = true
} else if (imageView2.layer.hitTest(p) != nil) {
imageView2.drawDashedBorder()
self.editMode = true
}
} else {
if imageView1.editMode {
imageView1.removeDashedBorder()
self.editMode = false
} else if imageView2.editMode {
imageView2.removeDashedBorder()
self.editMode = false
}
}
}
func moveEye(_ recognizer:UIPanGestureRecognizer) {
let p = recognizer.location(in: self)
if imageView1.editMode {
imageView1.position = p
}
if imageView2.editMode {
imageView2.position = p
}
}
This may not fit your exact needs, but the combination of tap & pan over tap for editing seems more natural. I'm sure that at the very least, doing a "silent" tap & pan (where it appears to the user they are simply dragging something around but they still have to tap on it first) is a better fit.
You can do this with touchesMoved event. But my recommendation is to use UIPanGestureRecognizer.
Declare a UIPanGestureRecognizer object and add it to your image view.
User interaction enable for image view.
Follow this nice apple documented sample code https://developer.apple.com/library/content/samplecode/Touches/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007435
in the touchesBegan method: check if the touch hit the UIImageView and set a global boolean variable to true. Set it to false on touchesEnded, and check in touchesMoved if the variable is true before you move the UIImageview
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch : UITouch = touches.first as UITouch!
location = touch.locationInView(self.view)
if(person.frame.origin.x<location.x&&person.frame.origin.x+person.frame.size.width>location.x{
// x value for touch is inside uiimageview
if(person.frame.origin.y<location.y&&person.frame.origin.y+person.frame.size.height>location.y{
//x&y value inside
personHit=true
}
}
// Person.center = location
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if(personHit){
let touch : UITouch = touches.first as UITouch!
location = touch.locationInView(self.view)
Person.center = location
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
personHit=false
}
Simple Approach to fix your problem:
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
imageView.isUserInteractionEnabled = true
let panGestureRecognizer = UIPanGestureRecognizer(target:self, action: #selector(panHandler(recognizer:)))
imageView.addGestureRecognizer(panGestureRecognizer)
}
func panHandler(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translation(in: view)
recognizer.view!.center = CGPoint(x: recognizer.view!.center.x + translation.x, y: recognizer.view!.center.y + translation.y)
recognizer.setTranslation(CGPoint.zero, in: view)
}

Giving properties of a UIButton to a SKSpriteNode in SpriteKit

I was wondering if there was a way to give the properties of a UIButton like darkening the button once it has been pressed,... to a SKSpiteNode since an SKSpiteNode has more customization and because I am using SpriteKit. I have seen 1 other question like this but none of the answers worked. Here is the code I have to create the SKSpriteNode and to detect a touch on it:
import SpriteKit
class StartScene: SKScene {
var startButton = SKSpriteNode()
override func didMoveToView(view: SKView) {
startButton = SKSpriteNode(imageNamed: "playButton")
startButton.size = CGSize(width: 100, height: 100)
startButton.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 - 50)
self.addChild(startButton)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
if startButton.containsPoint(location){
// When it has been selected
}
}
}
//...
Pleas help. Thanks in advance... Anton
I have always achieved this by adding code to the touches began, and touches ended method. Inside of these methods I simply set the sprites color to black, and then change its color blend factor. Let me know if this works for you!
//This will hold the object that gets darkened
var target = SKSpriteNode()
//This will keep track if an object is darkened
var have = false
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var first = touches.first as! UITouch
var location:CGPoint = first.locationInNode(self)
touchP = location
mouse.position = touchP
var node:SKNode = self.nodeAtPoint(location)
if let button = node as? SKSpriteNode
{
target = button
have = true
}
else
{
have = false
}
if (have == true)
{
target.color = UIColor.blackColor()
target.colorBlendFactor = 0.2
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
if (havet == true)
{
target.color = UIColor.blackColor()
target.colorBlendFactor = 0
target = SKSpriteNode()
have = false
}
}

Do something every click in Swift

I am trying to make a square appear on a random CGPoint in Swift every time I click the screen. Below is what I have done with no compiler errors, but once I run the application, I can click or drag to move my "person," but every time I do that, a smaller square does not appear. Any advice or solutions are great. Thank you! by the way, person, a UIImageView is no image but has a viable backgroundColor.`
`
#IBOutlet weak var person: UIImageView!
var location = CGPoint(x: 0,y: 0)
var randomPoint = CGPoint(x:Int(arc4random()%1000),y:Int(arc4random()%1000))
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
location = touch!.locationInView(self.view)
person.center = location
for _ in touches {
let mySquare: UIView = UIView(frame: CGRect(x: 0,y: 0,width: 20,height: 20))
mySquare.backgroundColor = UIColor.cyanColor()
mySquare.center = randomPoint
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
location = touch!.locationInView(self.view)
person.center = location
for _ in touches {
let mySquare: UIView = UIView(frame: CGRect(x: 0,y: 0,width: 20,height: 20))
mySquare.backgroundColor = UIColor.cyanColor()
mySquare.center = randomPoint
}
}`
For every touch, you just need create and add(you missed this step) square in touch begin (or touch end), don't do in touch move.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
location = touch!.locationInView(self.view)
person.center = location
for _ in touches {
let mySquare: UIView = UIView(frame: CGRect(x: 0,y: 0,width: 20,height: 20))
mySquare.backgroundColor = UIColor.cyanColor()
mySquare.center = randomPoint
view.addSubview(mySquare) // add square to the view
}
}
You didn't add mySquare view onto any view.
self.view.addSubview(mySquare)

Resources