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
}
}
}
Related
I have a circle on the screen that will slowly get bigger (incidentally, Balloon class is a type of SKShapeNode). In touchesBegan I click on the circle and it prints that I touched it, however no impulse is applied. How can I find the issue? I'm fairly new to SpriteKit.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var radius = 40.0
var balloon : Balloon? = nil
override func didMove(to view: SKView) {
balloon = Balloon(radius: radius, position: CGPoint(x: frame.midX, y: frame.minY + 250))
balloon?.name = "balloon"
let physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(radius))
physicsBody.affectedByGravity = false
balloon?.physicsBody = physicsBody
balloon?.physicsBody?.isDynamic = true
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
self.addChild(balloon!)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let node : SKNode = self.atPoint(location)
if node.name == "balloon" {
print("touching balloon.")
balloon?.physicsBody?.applyImpulse(CGVector(dx: 10.0, dy: 10.0), at: location)
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
radius += 0.05
self.balloon?.radius = radius
let physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(radius))
physicsBody.affectedByGravity = false
balloon?.physicsBody = physicsBody
balloon?.physicsBody?.isDynamic = true
}
}
You are creating a new body every update. If you want to inflate your balloon, use scale:
var scale = 1.0
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
scale += 0.05
self.balloon?.setScale(scale)
}
Then your physics body will be able to have the impulse applied to it because you won't be creating a new body each update.
The following line:
let location = touch.location(in: self)
finds the location of the touch in the scene. However, in the function call applyImpulse, it is relative to the node. Instead of
balloon?.physicsBody?.applyImpulse(CGVector(dx: 10.0, dy: 10.0), at: location)
use:
balloon?.physicsBody?.applyImpulse(CGVector(dx: 10.0, dy: 10.0), at: touch.location(in: balloon))
I would like to have the following interplay between line and ball in my game: a line gives direction and speed to the ball. The longer the line, the faster the ball.
What I have now: a ball is following the line and stops at the end of it. But it shouldn't stop here. Of course, I understand that the ball is only following the path I made. But how could I change it?
Here is my code:
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
// Basic for dynamic sizes step01
var width = CGFloat()
var height = CGFloat()
var ball:SKSpriteNode!
var line:SKShapeNode!
var startPoint: CGPoint!
var location = CGPoint()
override func didMove(to view: SKView) {
self.backgroundColor = .purple
//declare dynamic size of the screen
width = self.frame.size.width
height = self.frame.size.height
self.physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
self.physicsWorld.gravity = CGVector.zero
createBall()
}
func createBall(){
ball = SKSpriteNode(imageNamed: "yellowBtn")
ball.position = CGPoint(x:0, y: -(height/2.5))
ball.size = CGSize(width: width/6, height: width/6)
self.addChild(ball)
}
func drawLine(endPoint:CGPoint){
if(line != nil ){ line.removeFromParent() }
startPoint = ball.position
let path = CGMutablePath()
path.move(to: startPoint)
path.addLine(to: endPoint)
line = SKShapeNode()
line.path = path
line.lineWidth = 5
line.strokeColor = UIColor.white
self.addChild(line)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if(line != nil ){ line.removeFromParent()
for touch in (touches ) {
let location = touch.location(in: self)
drawLine(endPoint: location)
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ) {
let location = touch.location(in: self)
drawLine(endPoint: location)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ) {
location = touch.location(in: self)
drawLine(endPoint: location)
}
let moveAction = SKAction.move(to: location, duration: 10)
ball.run(moveAction)
}
override func update(_ currentTime: TimeInterval) {
}
}
I have found at least a part of answer to my question: I need to calculate CGVector manually (lastPoint - firstPoint) and then apply impulse to the ball:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ) {
location = touch.location(in: self)
drawLine(endPoint: location)
}
let dx = location.x - startPoint.x
let dy = location.y - startPoint.y
let movement = CGVector(dx: dx, dy: dy)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 50)
ball.physicsBody?.applyImpulse(movement)
}
Now I should find the second part: how to set the speed of the ball accordingly to the length of the line.
I'm trying to throw a node around the screen, I've taken the below code but nothing actually happens.
When the scene loads the node spawns in the middle of the screen then drops to the bottom.
The code was taken from elsewhere in the site and granted it was a bit old so I'm assuming I'm missing something?
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
sprite = SKSpriteNode(color: UIColor.red, size: CGSize(width: 50, height: 50))
sprite.physicsBody = SKPhysicsBody(rectangleOf: sprite.size)
sprite.position = CGPoint(x: 50.0, y: 50.0)
self.addChild(sprite)
}
func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.location(in: 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.location(in: self)
touchPoint = location
}
}
func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
touching = false
}
func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
touching = false
}
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
}
}
}
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 have a sprite defined and it isn't appearing when I run the app. Here is my code and some images to help figure out the error:
import SpriteKit
class GameScene: SKScene {
var SingleplayerButton: SKSpriteNode! = nil
override func didMoveToView(view: SKView) {
SingleplayerButton = SKSpriteNode(imageNamed: "Unknown")
SingleplayerButton.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
SingleplayerButton.size = CGSize(width: 200, height: 50)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if SingleplayerButton.containsPoint(location) {
}
}
}
}
you forget addChild to your scene