How to make an object (SKSprite node) disappear on click - ios

I want the node to disappear when I click on the object. The object is moving around the screen and currently is removed when you click anywhere on the screen. However, I want it to be removed only when the object has been clicked on.
The code is as follows:
import SpriteKit
import GameplayKit
struct Physics {
static let Enemy: UInt32 = 0x1 << 1
let BorderCategory : UInt32 = 0x1 << 2
let BottomCategory : UInt32 = 0x1 << 3
let BallCategory : UInt32 = 0x1 << 4
}
class GameScene: SKScene {
var Enemy = SKSpriteNode()
var gameStarted = Bool()
var gameState = "running"
var destX : CGFloat = 0.0
var destY : CGFloat = 0.0
var score = 0
override func didMove(to view: SKView) {
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
Enemy.name = "Enemys"
Enemy = SKSpriteNode(imageNamed: "red2")
Enemy.size = CGSize(width: 60, height: 70)
Enemy.position = (CGPoint(x: self.frame.width / 6 - Enemy.frame.width, y: self.frame.height / 10))
Enemy.physicsBody = SKPhysicsBody(circleOfRadius: Enemy.frame.height / 2)
Enemy.physicsBody?.categoryBitMask = Physics.Enemy
//Enemy.physicsBody?.categoryBitMask = Physics.Ground | Physics.wall
//Enemy.physicsBody?.contactTestBitMask = Physics.Ground | Physics.wall
Enemy.physicsBody?.affectedByGravity = false
Enemy.physicsBody?.isDynamic = true
Enemy.physicsBody?.restitution = 1
Enemy.physicsBody?.friction = 0
Enemy.physicsBody?.angularDamping = 0
Enemy.physicsBody?.linearDamping = 0
self.addChild(Enemy)
let force = SKAction.applyForce(CGVector(dx: 300, dy: 300) , duration: 0.1)
Enemy.run(force)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as UITouch!
let touchLocation = touch?.location(in: self)
let targetNode = atPoint(touchLocation!) as! SKSpriteNode
if(targetNode.name == "Enemys"){
Enemy.removeFromParent()
}
}

I personally find that the easiest way to handle this is to subclass SKSpriteNode and dig into the fact that it is descendant of UIResponder.
class AlienSprite: SKSpriteNode {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.removeFromParent()
}
}
You also need to set the isUserInteractionEnabled = true on the instance somewhere.

You have to determine which node is under the touch, SKNode has methods about this: nodeAtPoint and nodesAtPoint:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as UITouch!
let touchLocation = touch?.location(in: self)
if let hit = yourRootNode.nodeAtPoint(touchLocation) {
if hit.name=="Enemy" {
hit.removeFromParent();
}
}
}

Related

How to drag and flick a node in SpriteKit while gravity is present?

With my current code, the node is extremely laggy, and moves or teleports in random directions for some reason when its flicked. How can i fix this, and also can someone explain why it is teleporting and moving to random places in the scene.
Also, is there anyway to allow the node to be moved only when it is dragged from its position, rather than being at the gesturerecognizer's coordinates at all times?
override func didMove(to view: SKView) {
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.pan(_:)))
view.addGestureRecognizer(gestureRecognizer)
circleNode.physicsBody = SKPhysicsBody(circleOfRadius: 20)
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.addChild(circleNode)
}
#objc func pan(_ recognizer: UIPanGestureRecognizer) {
if recognizer.state == .changed {
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
var location = recognizer.location(in: self.view!)
location = self.convertPoint(fromView: location)
circleNode.position = location
}
if recognizer.state == .ended {
self.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
let transformerX = 1024/self.view!.frame.size.width
let transformerY = 768/self.view!.frame.size.height
let velocity = recognizer.velocity(in: self.view)
circleNode.physicsBody?.applyForce(CGVector(dx: velocity.x * transformerX, dy: velocity.y * transformerY))
}
}
Here is some code I was playing around with. I'm able to drag and flick a spear (spear Image) and also "pop" a pig head. This is the whole GameScene.Remove the code you don't need. :)
import SpriteKit
import CoreMotion
class GameScene: SKScene, SKPhysicsContactDelegate {
enum CollisionTypes: UInt32{
case spear = 1
case wall = 2
case head = 4
}
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
//Add contact delegate
physicsWorld.contactDelegate = self
self.backgroundColor = .white
self.addChild(spearNode)
self.addChild(headNode)
}
lazy var spearNode: SKSpriteNode = {
let node = SKSpriteNode(imageNamed: "spear2")
node.name = "Spear"
node.physicsBody = SKPhysicsBody(texture: node.texture!,
size: CGSize(width: node.frame.width , height: node.frame.height))
node.position = CGPoint(x:self.frame.midX , y:self.frame.midY)
node.physicsBody?.affectedByGravity = true
node.physicsBody?.allowsRotation = false
node.size = CGSize(width: node.frame.width , height: node.frame.height )
node.physicsBody?.categoryBitMask = CollisionTypes.spear.rawValue
node.physicsBody?.contactTestBitMask = CollisionTypes.head.rawValue
node.physicsBody?.collisionBitMask = CollisionTypes.head.rawValue
return node
}()
lazy var headNode: SKSpriteNode = {
let node = SKSpriteNode(imageNamed: "Pig")
node.name = "Pig"
node.physicsBody = SKPhysicsBody(texture: node.texture!,
size: CGSize(width: node.frame.width , height: node.frame.height))
node.position = CGPoint(x:self.frame.midX , y:self.frame.maxY - 100)
node.physicsBody?.affectedByGravity = true
node.physicsBody?.allowsRotation = false
node.size = CGSize(width: node.frame.width / 2 , height: node.frame.height / 2 )
node.physicsBody?.categoryBitMask = CollisionTypes.head.rawValue
return node
}()
func didBegin(_ contact: SKPhysicsContact){
guard let nodeA = contact.bodyA.node else {return}
guard let nodeB = contact.bodyB.node else {return}
print("Contacted")
if nodeA.name == "Pig" && nodeB.name == "Spear"{
nodeA.removeFromParent()
}
if nodeA.name == "Spear" && nodeB.name == "Pig"{
nodeB.removeFromParent()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in:self)
if spearNode.frame.contains(location) {
touchPoint = location
touching = true
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: self)
touchPoint = location
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func update(_ currentTime: TimeInterval) {
physicsWorld.gravity = CGVector(dx:0, dy: -9.8)
if touching {
let dt:CGFloat = 1.0/60.0
let distance = CGVector(dx: touchPoint.x-spearNode.position.x, dy: touchPoint.y-spearNode.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
spearNode.physicsBody!.velocity=velocity
}
}
}
Why not simply impart a force to the object based upon the swipe gesture rather than turning off gravity, manually moving the object, and then turning on gravity again when the swipe is over?

Moving the player freely all over the screen

I'm making a game where the ball(the player) is suppose to avoid other balls passing by the screen. Basically I want the ball always to follow the location of the touch. So wherever I'm moving my finger on the screen, the ball follows.
This is the player Class:
import SpriteKit
struct ColliderType {
static let Player: UInt32 = 1
static let Blue: UInt32 = 2
static let Green: UInt32 = 3
static let Yellow: UInt32 = 4
static let Red: UInt32 = 5
}
class Player: SKSpriteNode {
func initialize() {
self.name = "Player"
self.zPosition = 1
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.height /
2)
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.Player
self.physicsBody?.collisionBitMask = ColliderType.Blue |
ColliderType.Green | ColliderType.Red | ColliderType.Yellow
self.physicsBody?.contactTestBitMask = ColliderType.Blue |
ColliderType.Green | ColliderType.Red | ColliderType.Yellow
}
}
This is the GameplayScene:
import SpriteKit
class GameplayScene: SKScene, SKPhysicsContactDelegate {
var player = Player()
var ball = SKSpriteNode()
var scoreLabel = SKLabelNode()
var score = 0
var counter = Timer()
override func didMove(to view: SKView) {
initialize()
}
override func update(_ currentTime: TimeInterval) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event:
UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "Retry" {
self.removeAllActions()
self.removeAllChildren()
initialize()
}
if atPoint(location).name == "Quit" {
let mainmenu = MainMenuScene(fileNamed: "MainMenuScene")
mainmenu!.scaleMode = .aspectFill
self.view?.presentScene(mainmenu!, transition:
SKTransition.fade(withDuration: TimeInterval(1)))
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event:
UIEvent?) {
}
override func touchesMoved(_ touches: Set<UITouch>, with event:
UIEvent?) {
}
func didBegin(_ contact: SKPhysicsContact) {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.node?.name == "Player" {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.node?.name == "Player" && secondBody.node?.name ==
"Red" {
playerDied()
firstBody.node?.removeFromParent()
}
if firstBody.node?.name == "Player" && secondBody.node?.name ==
"Blue" {
playerDied()
firstBody.node?.removeFromParent()
}
if firstBody.node?.name == "Player" && secondBody.node?.name ==
"Green" {
playerDied()
firstBody.node?.removeFromParent()
}
if firstBody.node?.name == "Player" && secondBody.node?.name ==
"Yellow" {
playerDied()
firstBody.node?.removeFromParent()
}
}
func initialize() {
score = 0
physicsWorld.contactDelegate = self
createPlayer()
createBackground()
spawnRedBall()
spawnBlueBall()
spawnGreenBall()
spawnYellowBall()
createLabel()
counter = Timer.scheduledTimer(timeInterval: TimeInterval(0.7),
target: self, selector: "incrementScore", userInfo: nil, repeats: true)
}
func createPlayer() {
player = Player(imageNamed: "Player")
player.initialize()
player.position = CGPoint(x: 0, y: 0)
self.addChild(player)
}
func createBackground() {
let bg = SKSpriteNode(imageNamed: "BG")
bg.name = "BG"
bg.anchorPoint = CGPoint(x: 0.5, y: 0.5)
bg.position = CGPoint(x: 0, y: 0)
self.addChild(bg)
}
func createRedBall() {
let ball = SKSpriteNode(imageNamed: "Red")
ball.name = "Red"
ball.anchorPoint = CGPoint(x: 0.5, y: 0.5)
ball.zPosition = 1
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height /
2)
ball.physicsBody?.categoryBitMask = ColliderType.Red
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.isDynamic = false
ball.position.y = self.size.height + 100
ball.position.x = CGFloat.randomBetweenNumbers(firstNum: -345,
secondNum: 345)
self.addChild(ball)
let destination = self.frame.height * 2
let move = SKAction.moveTo(y: -destination, duration:
TimeInterval(10))
let remove = SKAction.removeFromParent()
ball.run(SKAction.sequence([move, remove]), withKey: "MoveRed")
}
func spawnRedBall() {
let spawn = SKAction.run({ () -> Void in
self.createRedBall()
})
let delay = SKAction.wait(forDuration: TimeInterval(0.5))
let sequence = SKAction.sequence([spawn, delay])
self.run(SKAction.repeatForever(sequence), withKey: "SpawnRed")
}
func createBlueBall() {
let ball = SKSpriteNode(imageNamed: "Blue")
ball.name = "Blue"
ball.anchorPoint = CGPoint(x: 0.5, y: 0.5)
ball.zPosition = 1
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height /
2)
ball.physicsBody?.categoryBitMask = ColliderType.Blue
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.isDynamic = false
ball.position.y = -self.size.height + 100
ball.position.x = CGFloat.randomBetweenNumbers(firstNum: -345,
secondNum: 345)
self.addChild(ball)
let destination = self.frame.height * 2
let move = SKAction.moveTo(y: destination, duration:
TimeInterval(10))
let remove = SKAction.removeFromParent()
ball.run(SKAction.sequence([move, remove]), withKey: "MoveBlue")
}
func spawnBlueBall() {
let spawn = SKAction.run({ () -> Void in
self.createBlueBall()
})
let delay = SKAction.wait(forDuration: TimeInterval(0.5))
let sequence = SKAction.sequence([spawn, delay])
self.run(SKAction.repeatForever(sequence), withKey: "SpawnBlue")
}
func createGreenBall() {
let ball = SKSpriteNode(imageNamed: "Green")
ball.name = "Green"
ball.anchorPoint = CGPoint(x: 0.5, y: 0.5)
ball.zPosition = 1
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height /
2)
ball.physicsBody?.categoryBitMask = ColliderType.Green
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.isDynamic = false
ball.position.x = -self.size.width + 200
ball.position.y = CGFloat.randomBetweenNumbers(firstNum: -637,
secondNum: 637)
self.addChild(ball)
let destination = self.frame.height * 2
let move = SKAction.moveTo(x: destination, duration:
TimeInterval(10))
let remove = SKAction.removeFromParent()
ball.run(SKAction.sequence([move, remove]), withKey: "MoveGreen")
}
func spawnGreenBall() {
let spawn = SKAction.run({ () -> Void in
self.createGreenBall()
})
let delay = SKAction.wait(forDuration: TimeInterval(0.5))
let sequence = SKAction.sequence([spawn, delay])
self.run(SKAction.repeatForever(sequence), withKey: "SpawnGreen")
}
func createYellowBall() {
let ball = SKSpriteNode(imageNamed: "Yellow")
ball.name = "Yellow"
ball.anchorPoint = CGPoint(x: 0.5, y: 0.5)
ball.zPosition = 1
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height /
2)
ball.physicsBody?.categoryBitMask = ColliderType.Green
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.isDynamic = false
ball.position.x = self.size.width + 200
ball.position.y = CGFloat.randomBetweenNumbers(firstNum: -637,
secondNum: 637)
self.addChild(ball)
let destination = self.frame.height * 2
let move = SKAction.moveTo(x: -destination, duration:
TimeInterval(10))
let remove = SKAction.removeFromParent()
ball.run(SKAction.sequence([move, remove]), withKey: "MoveYellow")
}
func spawnYellowBall() {
let spawn = SKAction.run({ () -> Void in
self.createYellowBall()
})
let delay = SKAction.wait(forDuration: TimeInterval(0.5))
let sequence = SKAction.sequence([spawn, delay])
self.run(SKAction.repeatForever(sequence), withKey: "SpawnYellow")
}
func createLabel() {
scoreLabel.zPosition = 3
scoreLabel.position = CGPoint(x: -320, y: 600)
scoreLabel.fontName = "Verdana"
scoreLabel.fontSize = 70
scoreLabel.text = "0"
self.addChild(scoreLabel)
}
func incrementScore() {
score += 1
scoreLabel.text = String(score)
}
func playerDied() {
counter.invalidate()
let highscore = GameManager.instance.getHighscore()
if highscore < score {
GameManager.instance.setHighscore(highscore: score)
}
let retry = SKSpriteNode(imageNamed: "Retry")
let quit = SKSpriteNode(imageNamed: "Quit")
retry.name = "Retry"
retry.anchorPoint = CGPoint(x: 0.5, y: 0.5)
retry.position = CGPoint(x: -150, y: -50)
retry.zPosition = 2
retry.setScale(0)
quit.name = "Quit"
quit.anchorPoint = CGPoint(x: 0.5, y: 0.5)
quit.position = CGPoint(x: 150, y: -50)
quit.zPosition = 2
quit.setScale(0)
let scaleUp = SKAction.scale(to: 1, duration: TimeInterval(0.5))
retry.run(scaleUp)
quit.run(scaleUp)
self.addChild(retry)
self.addChild(quit)
}
}
Add the following property:
var ballIsTouched = false
and then implement the touch methods:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let location = touches.first.location(in: self) {
if ball.containsPoint(location) {
ballIsTouched = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if (ballIsTouched == true) {
ball.position = (touches.first?.location(in: self))!
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
ballIsTouched = false
}
I think it is more natural when implementing a dragging, to drag a sprite from the touch location rather than from the center of the sprite. To do this, you should calculate the offset and add it the new sprite's position, like this:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let current = touch.location(in: self)
let previous = touch.previousLocation(in: self)
if ball.contains(current) {
let offset = CGPoint(x: current.x - previous.x , y: current.y - previous.y)
ball.position = CGPoint(x: ball.position.x + offset.x , y: ball.position.y + offset.y)
}
}
}
Here is a sample project to show how to implement a draggable sprite.:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ballIsTouched = false
var ball = SKSpriteNode()
override func didMove(to view: SKView) {
ball = SKSpriteNode(color: .blue, size: CGSize(width:100, height:100))
ball.position = CGPoint(x: 0, y: 0)
addChild(ball)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
if ball.contains(touch.location(in: self)) {
ballIsTouched = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if (ballIsTouched == true) {
ball.position = (touches.first?.location(in: self))!
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
ballIsTouched = false
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
This only allows for the single specified sprite to be dragged. Here is an example with multiple sprites that can be dragged:
import SpriteKit
import GameplayKit
extension SKColor {
static func random() -> SKColor {
let colours = [SKColor.lightGray, SKColor.white, SKColor.gray, SKColor.red, SKColor.green, SKColor.blue, SKColor.cyan, SKColor.yellow, SKColor.magenta, SKColor.orange, SKColor.purple, SKColor.brown]
return colours[Int(arc4random_uniform(UInt32(colours.count)))]
}
}
class GameScene: SKScene {
var touchedSprite : SKSpriteNode?
override func didMove(to view: SKView) {
for i in -1...1 {
let ball = SKSpriteNode(color: SKColor.random(), size: CGSize(width:100, height:100))
ball.position = CGPoint(x: 0, y: i * 200)
addChild(ball)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
touchedSprite = self.atPoint(touch.location(in: self)) as? SKSpriteNode
touchedSprite?.setScale(1.25)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if (touchedSprite != nil) {
touchedSprite?.position = (touches.first?.location(in: self))!
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touchedSprite?.setScale(1)
touchedSprite = nil
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
You could also implement a draggable protocol or something to limit which sprites can be dragged.

use two sprite objects at the same time with single touch?

Currently, I can only use one at a time, and not make the two paddles (one on each side of the screen) act independently when different finger movements are used with each one. Here is the class. Any ideas?
let BallCategoryName = "ball"
let GameMessageName = "gameMessage"
let BallCategory : UInt32 = 0x1 << 0
let LeftCategory : UInt32 = 0x1 << 1
let BlockCategory : UInt32 = 0x1 << 2
let PaddleCategory : UInt32 = 0x1 << 3
let BorderCategory : UInt32 = 0x1 << 4
class GameScene: SKScene, SKPhysicsContactDelegate {
var isFingerOnPaddleL = false
var isFingerOnPaddleR = false
override func didMove(to view: SKView) {
super.didMove(to: view)
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
physicsWorld.contactDelegate = self
let ball = childNode(withName: BallCategoryName) as! SKSpriteNode
ball.physicsBody!.applyImpulse(CGVector(dx: 2.0, dy: -2.0))
let paddleL = childNode(withName: "paddleL") as! SKSpriteNode
//let paddleR = childNode(withName: "paddleR") as! SKSpriteNode
let leftRect = CGRect(x: frame.origin.x, y: frame.origin.y, width: 1, height: frame.size.height)
let left = SKNode()
left.physicsBody = SKPhysicsBody(edgeLoopFrom: leftRect)
addChild(left)
left.physicsBody!.categoryBitMask = LeftCategory
ball.physicsBody!.categoryBitMask = BallCategory
paddleL.physicsBody!.categoryBitMask = PaddleCategory
borderBody.categoryBitMask = BorderCategory
ball.physicsBody!.contactTestBitMask = LeftCategory
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
if let body = physicsWorld.body(at: touchLocation) {
if body.node!.name == "paddleL" {
print("Began touch on paddleL")
isFingerOnPaddleL = true
}
else if body.node!.name == "paddleR" {
print("Began touch on paddleR")
isFingerOnPaddleR = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// 1
if isFingerOnPaddleL {
// 2
let touch = touches.first
let touchLocation = touch!.location(in: self)
let previousLocation = touch!.previousLocation(in: self)
// 3
let paddle = childNode(withName: "paddleL") as! SKSpriteNode
// 4
var paddleY = paddle.position.y + (touchLocation.y - previousLocation.y)
// 5
paddleY = max(paddle.size.height/2, paddleY)
paddleY = min(size.height - paddle.size.height/2, paddleY)
// 6
paddle.position = CGPoint(x: paddle.position.x, y: paddleY)
}
else if isFingerOnPaddleR {
// 2
let touch = touches.first
let touchLocation = touch!.location(in: self)
let previousLocation = touch!.previousLocation(in: self)
// 3
let paddle = childNode(withName: "paddleR") as! SKSpriteNode
// 4
var paddleY = paddle.position.y + (touchLocation.y - previousLocation.y)
// 5
paddleY = max(paddle.size.height/2, paddleY)
paddleY = min(size.height - paddle.size.height/2, paddleY)
// 6
paddle.position = CGPoint(x: paddle.position.x, y: paddleY)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if (isFingerOnPaddleL == true) {
isFingerOnPaddleL = false
}
else if (isFingerOnPaddleR == true) {
isFingerOnPaddleR = false
}
}
func didBegin(_ contact: SKPhysicsContact) {
// 1
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
// 2
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
// 3
if firstBody.categoryBitMask == BallCategory && secondBody.categoryBitMask == LeftCategory {
print("Hit left. First contact has been made.")
}
}
}
You can process the paddles independently by tracking each touch throughout its lifecycle (began/moved/ended). First, define a dictionary with the touch object as the key and the node's name as the value
var touchTracker:[UITouch:String] = [:]
then in touchesBegan, store the name of paddle(s) touched by the user
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let touchLocation = touch.location(in: self)
let node = atPoint(touchLocation)
if let name = node.name {
if name == "paddleL" {
print("Began touch on paddleL")
touchTracker[touch] = name
}
else if name == "paddleR" {
print("Began touch on paddleR")
touchTracker[touch] = name
}
}
}
}
In touchesMoved, use the touch object to retrieve the node's name from the dictionary, and use the name to access the appropriate paddle.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let touchLocation = touch.location(in: self)
if let name = touchTracker[touch] {
if let paddle = childNode(withName: name) as? SKSpriteNode {
// Move a paddle
paddle.position = CGPoint(x:paddle.position.x, y:touchLocation.y)
}
}
}
}
Lastly, remove the touch/name from the dictionary when a touch ends.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
if touchTracker[touch] != nil {
touchTracker.removeValue(forKey: touch)
}
}
}

Sprite Kit stop Impulse

I want to increase a CGFloat every time while the Screen is tapped.
The float has a set maximum and minimum value.
I tried to go through this suggestion: StackOverflow
However, this only increases the CGFloat after the touch is made, or the maximum is reached. I want to increase the CGFloat during the touch, meaning the longer you touch the higher the Jump/CGFloat.
The problem probably lies within the impulse, that you cant change it after it was applied. That means, after the 'Player' gets an impulse of 20, and the screen is longer touched, the Float may increase, but the impulse won't.
If you look at my current code, the impulse is set at maximum while the screen is touched, but if released the action should be removed. However, it doesn't work, the impulse does not stop.
I know that you can set the velocity of the body at a value after the press is made, and if the press has ended the velocity back to 0 so it stops it 'jump', but that doesn't look quite smooth as it would be with an impulse.
Has anybody a solution?
struct Constants {
static let minimumJumpForce:CGFloat = 20.0
static let maximumJumpForce:CGFloat = 60.0
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var force: CGFloat = 20.0
func longPressed(longPress: UIGestureRecognizer) {
if (longPress.state == UIGestureRecognizerState.Began) {
println("Began")
self.pressed = true
let HigherJump = SKAction.runBlock({Player.physicsBody?.applyImpulse(CGVectorMake(0, Constants.maximumJumpForce))})
self.runAction(HigherJump , withKey:"HighJump")
}else if (longPress.state == UIGestureRecognizerState.Ended) {
println("Ended")
self.pressed = false
self.removeActionForKey("HighJump")
}
}
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)
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
1.Create ‘Game’ from Xcode template based on SpriteKit
2.Copy paste listed code to GameScene class
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var location = CGPoint()
var floorSize = CGSize()
var floorColor = UIColor()
var player = SKSpriteNode()
override func didMoveToView(view: SKView) {
view.showsFPS = true;
view.showsNodeCount = true;
view.showsDrawCount = true;
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody?.categoryBitMask = 1
self.physicsBody?.contactTestBitMask = 1
self.physicsWorld.gravity = CGVectorMake(0, 0)
self.physicsWorld.contactDelegate = self;
location = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
player = SKSpriteNode(imageNamed:"Spaceship")
player.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 320, height: 320))
player.physicsBody?.categoryBitMask = 1
player.physicsBody?.collisionBitMask = 1
player.physicsBody?.contactTestBitMask = 1
player.physicsBody?.linearDamping = 0;
player.xScale = 1
player.yScale = 1
player.position = location
self.addChild(player)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.physicsWorld.gravity = CGVectorMake(0, 0)
let direction = Float(1.5708)//Float(player.zRotation) + Float(M_PI_2)
player.physicsBody?.applyForce(CGVector(dx: 150000*CGFloat(cosf(direction)), dy: 150000*CGFloat(sinf(direction))))
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.physicsWorld.gravity = CGVectorMake(0, -7.9)
}
}
3.Run the app
This should give you start point for you 'Jump' game :)
Try changing this:
if(self.pressed){
let HigherJump = SKAction.runBlock({if(self.force < Constants.maximumJumpForce){
self.force += 2.0
}else{
self.force = Constants.maximumJumpForce
}})
self.runAction(HigherJump)
}
to this:
if(self.pressed){
if(self.force < Constants.maximumJumpForce) {
self.force += 2.0
}
else {
self.force = Constants.maximumJumpForce
}
}
Theres no need to use a runBlock SKAction here.

sprite kit : contact not working

I am trying to make one simple game and this problem happen first my paddle can touch the ball but now it cant do that and
this code also should give me message or in simulator but it is not showing any idea?
import SpriteKit
class GameScene: SKScene , SKPhysicsContactDelegate{
var istouchingpaddle = false
let ballcatagery:UInt32 = 0 * 1 << 0
let paddlecategary :UInt32 = 0 * 1 << 1
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let border = SKPhysicsBody(edgeLoopFromRect: self.frame)
border.friction = 0
self.physicsBody = border
self.physicsWorld.gravity = CGVectorMake(0,-9.8)
self.physicsWorld.contactDelegate = self
let ball = childNodeWithName ("ball") as SKSpriteNode
ball.physicsBody?.applyImpulse(CGVectorMake(30, -30))
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.restitution = 1
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.angularDamping = 0
ball.physicsBody!.categoryBitMask = ballcatagery
let paddle = childNodeWithName("paddle") as SKSpriteNode
paddle.physicsBody!.categoryBitMask = paddlecategary
ball.physicsBody?.contactTestBitMask = paddlecategary
}
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == ballcatagery && contact.bodyB.categoryBitMask == paddlecategary{
println("working ")
}
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
var touch = touches.anyObject() as UITouch
var location = touch.locationInNode(self)
if let body = self.physicsWorld.bodyAtPoint(location){
if body.node!.name == "paddle" {
istouchingpaddle = true
}
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
if istouchingpaddle{
var touch = touches.anyObject() as UITouch
var location = touch.locationInNode(self)
var prevlocation = touch.previousLocationInNode(self)
var paddle = childNodeWithName("paddle") as SKSpriteNode
var position = paddle.position.x + (location.x - prevlocation.x)
position = max(position,paddle.size.width/2)
position = min(position, size.width - paddle.size.width/2)
paddle.position = CGPoint(x: position, y: paddle.position.y)
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
istouchingpaddle = false
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
} }
I can produce good results with your code if I change the way of defining categories. Try setting categories like this :
let ballcatagery: UInt32 = 0x1 << 0 // Not that you have misspelled word "category", which can lead to errors if you expect somewhere a word "category" instead of your actual "categary".
let paddlecategary : UInt32 = 0x1 << 1
I can post you the whole code , but you should be good with this.

Resources