My game app has randomly moving object. I want to stop that and print some words on the screen when I touch/catch that. But I don't know what can I do this in gamescene.swift page. What should I write in touchesBegan? Here is my code :
import SpriteKit
let BallCategoryName = "ball"
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVectorMake(15, 15)
let ball = childNodeWithName(BallCategoryName) as! SKSpriteNode
ball.physicsBody!.applyImpulse(CGVectorMake(10, -10))
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as! UITouch
}
}
First you need to put the variable ball outside didMoveToView:
class GameScene: SKScene {
let ball = childNodeWithName(BallCategoryName) as! SKSpriteNode
override func didMoveToView(view: SKView) {
[...]
}
}
touchesBegan:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch:AnyObject in touches{
let location = (touch as! UITouch).locationInNode(self)
let nodeTouched = nodeAtPoint(location)
//Detect if the ball is touched
if(nodeTouched == ball){
//Do something
}
}
}
Related
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
}
}
I have a block in my code where the sprite will only move if you tap the screen. I would like to have this behaviour when I swipe up or down instead. Here is my code
import SpriteKit
import UIKit
class GameScene: SKScene {
var porker:Porker!
var touchLocation = CGFloat()
var gameOver = false
override func didMoveToView(view: SKView) {
addBG()
addPig()
}
func addBG() {
let bg = SKSpriteNode(imageNamed: "bg");
addChild(bg)
}
func addPig() {
let Pig = SKSpriteNode(imageNamed: "pig")
porker = Porker(guy:Pig)
addChild(Pig)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches{
if !gameOver {
touchLocation = (touch.locationInView(self.view!).y * -1) + (self.size.height/2)
}
}
let moveAction = SKAction.moveToY(touchLocation, duration: 0.5)
moveAction.timingMode = SKActionTimingMode.EaseOut
porker.guy.runAction(moveAction)
}
}
func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
Replace touchesBegan with this code:
// Store the start touch position
let yTouchCurrentPosition = 0.0
let yTouchDistance = 0.0
let yTouchStartPosition = 0.0
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
yTouchStartPosition = touch.locationInNode(self).y
}
}
// Calculate the distance of the touch movement
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
yTouchCurrentPosition = touch.locationInNode(self).y
yTouchDistance = yTouchStartPosition - yTouchCurrentPosition
}
}
// Reset all movement states and move sprite
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
yTouchCurrentPosition = 0.0
yTouchDistance = 0.0
yTouchStartPosition = 0.0
let moveAction = SKAction.moveToY(CGPoint(porker.guy.location.x, porker.guy.location.y + yTouchDistance), duration: 0.5)
moveAction.timingMode = SKActionTimingMode.EaseOut
porker.guy.runAction(moveAction)
}
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.
I have the following code at the moment. Even though the code build is successful, i cannot seem to get it to work. I am trying to make it so when you flick the object, it moves at the velocity of your begin and end touch.
import SpriteKit
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
override func didMoveToView(view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 50))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
self.addChild(sprite)
}
//for touch: AnyObject in touches {
//let location = touch.locationInNode(self)
//let touchedNode = self.nodeAtPoint(location)
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if sprite.frame.contains(location) {
touchPoint = location
touching = true
}
}
func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
touchPoint = location
}
func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
touching = false
}
func update(currentTime: CFTimeInterval) {
if touching {
let dt:CGFloat = 1.0/60.0
let distance = CGVector(dx: touchPoint.x-sprite.position.x, dy: touchPoint.y-sprite.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity = velocity
}
}
}}}
You accidentally placed touchesMoved, touchesEnded and update inside touchesBegan. Besides that your code works. A hint that there were problems was the fact you didn't need to prefix touchesMoved, touchesEnded or update with override.
In the future, I would recommend using breakpoints and print statements to check the methods you expect to execute, are in fact running. Doing that you'd see that your versions of touchesMoved, touchesEnded and update weren't being called.
Anyway, here's it corrected it and now it works perfectly:
import SpriteKit
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
override func didMoveToView(view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 50))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
self.addChild(sprite)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if sprite.frame.contains(location) {
touchPoint = location
touching = true
}
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
touchPoint = location
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
touching = false
}
override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
touching = false
}
override func update(currentTime: NSTimeInterval) {
if touching {
let dt:CGFloat = 1.0/60.0
let distance = CGVector(dx: touchPoint.x-sprite.position.x, dy: touchPoint.y-sprite.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity = velocity
}
}
}
ABakerSmith's solutions updated for Swift 4:
import SpriteKit
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody.init(edgeLoopFrom: self.frame)
sprite = SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50))
sprite.physicsBody = SKPhysicsBody.init(rectangleOf: sprite.size)
sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
self.addChild(sprite)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first?.location(in: self) {
if sprite.frame.contains(touch) {
touchPoint = touch
touching = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
let location = t.location(in: self)
touchPoint = location
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func update(_ currentTime: TimeInterval) {
if touching {
let dt:CGFloat = 1.0/60.0
let distance = CGVector(dx: touchPoint.x-sprite.position.x, dy: touchPoint.y-sprite.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity = velocity
}
}
}
Solution updated to not have the touch/drag snap to the middle of the sprite. If you're dragging/throwing an object you're going to want that throw to come from where the user touched the object and not snap to the middle of the object. This will make it look a lot smoother
import SpriteKit
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
var xDif = CGFloat()
var yDif = CGFloat()
override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody.init(edgeLoopFrom: self.frame)
sprite = SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50))
sprite.physicsBody = SKPhysicsBody.init(rectangleOf: sprite.size)
sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
self.addChild(sprite)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first?.location(in: self) {
xDif = sprite.position.x - touch.x
yDif = sprite.position.y - touch.y
if sprite.frame.contains(touch) {
touchPoint = touch
touching = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
let location = t.location(in: self)
touchPoint = location
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func update(_ currentTime: TimeInterval) {
if touching {
let dt:CGFloat = 1.0/60.0
let distance = CGVector(dx: touchPoint.x-sprite.position.x + xDif, dy: touchPoint.y-sprite.position.y + yDif)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity = velocity
}
}
}
I'm trying to create a program that prints out something whoever my spaceship goes over a circle, but it's not printing anything when I put the spaceship over the circle. Did I build my didBeginContact method wrong? Did I set up the BitMasks wrong?
import SpriteKit
class GameScene: SKScene {
var spaceship: SKNode!
var circ: SKNode!
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
spaceship = SKSpriteNode(imageNamed: "Spaceship")
spaceship.setScale(0.4)
spaceship.position.x = self.frame.width/2
spaceship.position.y = spaceship.frame.height/2
spaceship.physicsBody = SKPhysicsBody(circleOfRadius: spaceship.frame.height/2)
spaceship.physicsBody?.categoryBitMask = 1
spaceship.physicsBody?.contactTestBitMask = 2
spaceship.physicsBody?.collisionBitMask = 0
spaceship.physicsBody?.dynamic = true
circ = SKShapeNode(circleOfRadius: 50)
circ.position.y = self.frame.height/2
circ.position.x = self.frame.width/2
circ.physicsBody = SKPhysicsBody(circleOfRadius: 50)
circ.physicsBody?.categoryBitMask = 2
circ.physicsBody?.contactTestBitMask = 1
circ.physicsBody?.collisionBitMask = 0
circ.physicsBody?.dynamic = true
self.addChild(circ)
self.addChild(spaceship)
}
func didBeginContact(contact: SKPhysicsContact){
println("colliding!")
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
spaceship.position = location
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
spaceship.position = location
}
}
}
You need to declare yourself as the contact delegate of your physics world:
// add conformance to SKPhysicsContactDelegate:
class GameScene: SKScene, SKPhysicsContactDelegate {
// ...
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
// set as delegate:
self.physicsWorld.contactDelegate = self
// ..
}
// should be called now
func didBeginContact(contact: SKPhysicsContact){
println("colliding!")
}
}