SpriteKit friction not seeming to working properly - ios

On a project in Xcode 7 I have a few SKSpriteNodes that move back and forth on the screen and another one, called user, that is meant to jump from sprite to sprite, progressively up the screen. However, when user lands on one of the moving sprites the moving sprite just slides right out from under it and user falls back down. I thought that this meant that I needed to increase the friction property on the nodes so that user would "stick" to the nodes, but this just makes it bounce on the other nodes. My problem is that the nodes moving back and forth seem to "slippery," and user just doesn't stay on them.
Here's my code:
My class for user:
class UserNode: SKSpriteNode
{
class func newNode(position position: CGPoint) -> UserNode
{
let position = position
let sprite = UserNode(imageNamed: "userImage")
sprite.position = position
sprite.size = CGSize(width: sprite.size.width * 2, height: sprite.size.height * 2)
sprite.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "userImage"), size: sprite.size)
sprite.physicsBody?.affectedByGravity = true
sprite.physicsBody?.dynamic = true
sprite.physicsBody?.allowsRotation = false
sprite.physicsBody?.friction = 0.2
return sprite
}
}
and for moving user (the methods in my gamescene)
let scale: CGFloat = 2.0
let damping: CGFloat = 0.98
var point = CGPoint?()
func moveNodeToPoint(sprite: SKSpriteNode, point: CGPoint)
{
let dx = (point.x - sprite.position.x) * scale
let dy = (point.y - sprite.position.y) * scale
sprite.physicsBody?.velocity = CGVectorMake(dx, dy)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
/* Called when a touch begins */
for _: AnyObject in touches
{
if !welcomeNode.hidden
{
let fadeAway = SKAction.fadeOutWithDuration(0.3)
welcomeNode.runAction(fadeAway)
directionsNode.runAction(fadeAway)
touchStartNode.runAction(fadeAway)
welcomeNode.hidden = true
directionsNode.hidden = true
touchStartNode.hidden = true
}
//////////
point = CGPointMake(self.frame.midX, user.position.y + 300)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?)
{
point = nil
}
override func update(currentTime: CFTimeInterval)
{
/* Called before each frame is rendered */
if (point != nil)
{
moveNodeToPoint(user, point: point!)
}
else
{
let dx = user.physicsBody!.velocity.dx * damping
let dy = user.physicsBody!.velocity.dy * damping
user.physicsBody?.velocity = CGVectorMake(dx, dy)
}
}
and for moving the platforms:
let screenSize = UIScreen.mainScreen().bounds
let width = screenSize.size.width * 2
let firstAction = SKAction.moveBy(CGVector(dx: width, dy: 0), duration: 2)
let secondAction = SKAction.moveBy(CGVector(dx: -width, dy: 0), duration: 2)
let actions = [firstAction, secondAction]
let barAction = SKAction.sequence(actions)
let mainBarAction = SKAction.repeatActionForever(barAction)
platform.runAction(mainBarAction)

Related

SpriteKit: calculate angle of joystick and change sprite based on that

I am making a RPG Birds-eye style game with SpriteKit. I made a joystick because a D-Pad does not give the player enough control over his character.
I cannot wrap my brain around how I would calculate the neccessary data needed to change the Sprite of my character based on the angle of the joystick thumb Node.
Here is my code I am using
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if isTracking == false && base.contains(location) {
isTracking = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location: CGPoint = touch.location(in: self)
if isTracking == true {
let v = CGVector(dx: location.x - base.position.x, dy: location.y - DPad.position.y)
let angle = atan2(v.dy, v.dx)
let deg = angle * CGFloat(180 / Double.pi)
let Length:CGFloat = base.frame.size.height / 2
let xDist: CGFloat = sin(angle - 1.57079633) * Length
let yDist: CGFloat = cos(angle - 1.57079633) * Length
print(xDist,yDist)
xJoystickDelta = location.x * base.position.x / CGFloat(Double.pi)
yJoystickDelta = location.y * base.position.y / CGFloat(Double.pi)
if base.contains(location) {
thumbNode.position = location
} else {
thumbNode.position = CGPoint(x: base.position.x - xDist, y: base.position.y + yDist)
}
}
}
}
Update method
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if xJoystickDelta > 0 && yJoystickDelta < 0 {
print("forward")
}
}
The way I have set up right now tests the positive or negative state of the Joystick position in a cross method based on where the thumb Node is inside of the four marked sections below
I dont want it to do that
How can I set it up so that it changes the sprite based on where the thumb node is actually pointing inside my joysticks base like so.
I have been struggling with this for 3 days now so any help would be appreciated.
That looks far too complicated. Just compare the x and y components
of the difference vector v. Something like this:
if v.dx > abs(v.dy) {
// right
} else if v.dx < -abs(v.dy) {
// left
} else if v.dy < 0 {
// up
} else if v.dy > 0 {
// down
}

Touch contact with circle sprite oversize

I've got a simple spritekit project in which I'm noticing that the point at which contact is registered with my sprite is occurring well outside the circle physics body.
Demonstrated here:
https://gfycat.com/DentalBriskAfricangroundhornbill
With showPhysics on: http://i.imgur.com/F4BcXhb.png
Code for the object spawning the circles:
func spawnObject() {
object = SKSpriteNode(imageNamed: "Oval")
//object.fillColor = SKColor.white
object.name = "ball"
object.position = CGPoint(x: 280, y: 520)
object.physicsBody = SKPhysicsBody(circleOfRadius: object.size.width / 2)
object.physicsBody!.isDynamic = true
object.physicsBody!.contactTestBitMask = object.physicsBody!.collisionBitMask
object.physicsBody?.friction = 0.2
object.physicsBody?.restitution = 0.2
object.physicsBody?.mass = 5
addChild(object)
}
The circles themselves interact perfectly with one another - it appears touch is the outlier.
Would appreciate any assistance :)
Edit:
Example touch code:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let tappedNodes = nodes(at: location)
for node in tappedNodes {
if node.name == "ball" {
node.alpha = 0.5
}
}
}
}

Simulate zero gravity style player movement

I am having a little trouble modelling the correct way to move my player node in the way
that I want.
This is my first foray into Spritekit and I have the basics up and running (I have a static background, added player node and have a playable bounds rectangle with bounds checking)
The way I have added my player movement is to track the beginning touch position and
store this in scene class scoped variable (Called beginningTouchPosition) and to also store the current touch position (Called currentTouchPosition).
I also track the players sprite node position (currentPlayerPosition)
What I do is onTouchesBegan I update 'beginningTouchPosition' and then within onTouchesMoved I update 'currentTouchPosition', this way I can know the direction the user wants his ship to move by getting the direction relative to the 'beginningTouchPosition' as he/she moves their finger around. Also the distance of 'currentTouchPosition' from 'beginningTouchPosition' determines how fast the ship moves.
I move the player in the update by creating a CGVector using the above points and using this with an SKAction.MoveBy call.
I did it this way as I wanted the user to be able to touch anywhere on the screen to be able to control movement.
How I wanted the player to move. I'd rather have the ship move by applying a certain set velocity with a set acceleration in a certain direction. So that the player will accelerate from zero to say 1 in the space of 1/2 second when the finger is moved and to continue in that direction until the finger is either moved again or lifted.
If the finger is lifted then the ship should continue moving in the last direction but to start decelerate until the velocity is back to zero.
I am basically trying to simulate how a object would move in zero gravity, with the obvious non-realistic feature of deceleration.
I've found tutorials that show how to move an object towards a finger touch but this isnt what I want as I am trying to make a game that is a side scrolling space shooter where the player can go anywhere within the playable area, as opposed to simply up and down. Similar to the old retro game 'Nemesis', see screen below:
I've attached my player class code and scene code for better visualization of how I am currently doing it all.
Any pointers to literature on how to apply velocities with acceleration in a specified direction would be helpful :)
Scene file - Level_1.swift
import SpriteKit
// Global
/*
Level_1 set up and control
*/
class Level_1: SKScene {
// Instance variables
var lastUpdateTime:NSTimeInterval = 0
var dt:NSTimeInterval = 0
var player = Player() // Sub classed SKSpriteNode for all player related stuff
var currentTouchPosition: CGPoint!
var beginningTouchPosition:CGPoint!
var currentPlayerPosition: CGPoint!
let playableRectArea:CGRect
override init(size: CGSize) {
// Constant - Max aspect ratio supported
let maxAspectRatio:CGFloat = 16.0/9.0
// Calculate playable height
let playableHeight = size.width / maxAspectRatio
// Determine margin on top and bottom by subtracting playable height
// from scene height and then divide by 2
let playableMargin = (size.height-playableHeight)/2.0
// Calculate the actual playable area rectangle
playableRectArea = CGRect(x: 0, y: playableMargin,
width: size.width,
height: playableHeight)
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
currentTouchPosition = CGPointZero
beginningTouchPosition = CGPointZero
let background = SKSpriteNode(imageNamed: "background1")
background.position = CGPoint(x: size.width/2, y: size.height/2)
background.zPosition = -1
self.addChild(background)
currentPlayerPosition = CGPoint(x: 100, y: size.height/2)
player.position = currentPlayerPosition
self.addChild(player)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
currentTouchPosition = touch.locationInNode(self)
}
let dxVectorValue = (-1) * (beginningTouchPosition.x - currentTouchPosition.x)
let dyVectorValue = (-1) * (beginningTouchPosition.y - currentTouchPosition.y)
player.movePlayerBy(dxVectorValue, dyVectorValue: dyVectorValue, duration: dt)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
player.removeAllActions()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch: AnyObject in touches {
beginningTouchPosition = touch.locationInNode(self)
currentTouchPosition = beginningTouchPosition
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
currentPlayerPosition = player.position
if lastUpdateTime > 0 {
dt = currentTime - lastUpdateTime
}else{
dt = 0
}
lastUpdateTime = currentTime
player.boundsCheckPlayer(playableRectArea)
}
}
Player node - Player.swift
import Foundation
import SpriteKit
struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Player : UInt32 = 0b1 // 1
static let Enemy : UInt32 = 0b10 // 2
}
class Player: SKSpriteNode{
init(){
// Initialize the player object
let texture = SKTexture(imageNamed: "ship1")
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
self.xScale = 2
self.yScale = 2
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.zPosition = 1
// Player physics
self.physicsBody?.allowsRotation = false
self.physicsBody?.dynamic = false
self.physicsBody?.categoryBitMask = PhysicsCategory.Player
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Check if the player sprite is within the playable area bounds
func boundsCheckPlayer(playableArea: CGRect){
let bottomLeft = CGPoint(x: 0, y: CGRectGetMinY(playableArea))
let topRight = CGPoint(x: playableArea.size.width, y: CGRectGetMaxY(playableArea))
if(self.position.x <= bottomLeft.x){
self.position.x = bottomLeft.x
// velocity.x = -velocity.x
}
if(self.position.x >= topRight.x){
self.position.x = topRight.x
// velocity.x = -velocity.x
}
if(self.position.y <= bottomLeft.y){
self.position.y = bottomLeft.y
// velocity.y = -velocity.y
}
if(self.position.y >= topRight.y){
self.position.y = topRight.y
// velocity.y = -velocity.y
}
}
/*
Move the player in a certain direction by a specific amount
*/
func movePlayerBy(dxVectorValue: CGFloat, dyVectorValue: CGFloat, duration: NSTimeInterval)->(){
let moveActionVector = CGVectorMake(dxVectorValue, dyVectorValue)
let movePlayerAction = SKAction.moveBy(moveActionVector, duration: 1/duration)
self.runAction(movePlayerAction)
}
}
Basically we need a scene with zero gravity and a player where the touches cause force type physics actions. This is instead of moveBy type digital actions that simple move a character on the screen by such and such.
I went ahead and tested the code to try and get you something similar to what you describe. I altered some of your code a tad... to get it to work with my own set-up, as you didn't provide your GameViewController code so ask if you have any questions.
I've provided the code at the end with comments that say // IMPORTANT CODE with a # beside.
Here's details on why you use each piece of "IMPORTANT CODE
We need physics to accomplish what you describe so first ensure the player class will have a physics body. The body will be dynamic and affected by gravity (Zero Gravity), however you may want to fiddle with the gravity slightly for gameplay sake.
let body:SKPhysicsBody = SKPhysicsBody(texture: texture, alphaThreshold: 0, size: texture.size() )
self.physicsBody = body
self.physicsBody?.allowsRotation = false
self.physicsBody?.dynamic = true
self.physicsBody?.affectedByGravity = true
Since you want zero gravity we need to change our physics worlds gravity in our scene
scene?.physicsWorld.gravity = CGVectorMake(0, 0)
Next we change your movePlayerBy() to work with forces instead of simple digital movement. We do this with SKAction.applyForce.
This gives you a set-up based on force that's correlated with the swipe.
However, you may want a constant velocity no matter how hard the swipe. You can do that by normalizing the vector.. See here for somehow who asked that question and how it may apply here
(http://www.scriptscoop2.com/t/adc37b4f2ea8/swift-giving-a-physicsbody-a-constant-force.html)
func movePlayerBy(dxVectorValue: CGFloat, dyVectorValue: CGFloat, duration: NSTimeInterval)->(){
print("move player")
let moveActionVector = CGVectorMake(dxVectorValue, dyVectorValue)
let movePlayerAction = SKAction.applyForce(moveActionVector, duration: 1/duration)
self.runAction(movePlayerAction)
}
If you want the player to decelerate , we must add a function to set the player's velocity to 0. I've made it so this happens 0.5 seconds after the function is initially called.. otherwise the "floating through gravity" effect isn't really noticed as the movement would end with touchesEnded().
You can experiment with other ways to de-accelerate like a negative force of what was used initially, before the pause action in the sequence below.
There's many other ways to make it more of a true deceleration ... like a second sequence that subtracts -1 from velocity at a set time interval until it hits 0, before we hard code velocity to 0.
But, that's up to you from a gameplay standpoint.
So this should be enough to give you an idea.
func stopMoving() {
let delayTime: NSTimeInterval = 0.5 // 0.5 second pause
let stopAction: SKAction = SKAction.runBlock{
self.physicsBody?.velocity = CGVectorMake(0, 0)
}
let pause: SKAction = SKAction.waitForDuration(delayTime)
let stopSequence: SKAction = SKAction.sequence([pause,stopAction])
self.runAction(stopSequence)
}
We alter touchesEnded() to call stopMoving() .. But, try it without this to see it without that "deceleration".
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
player.removeAllActions()
player.stopMoving()
}
Other Notes.
Currently the bounds only catch the player on the left and right with the code I created... I'm not sure if that will happen in your set-up. But, as that's another question to figure out, I didn't further look into it.
Here's my code I used ... I'm providing it since I made a few other minor alterations for the sake of testing. I wouldn't worry about anything other than where I place the new important pieces of code.
GameScene.Swift
import SpriteKit
// Global
/*
Level_1 set up and control
*/
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
class Level_1: GameScene {
// Instance variables
var lastUpdateTime:NSTimeInterval = 0
var dt:NSTimeInterval = 0
var player = Player() // Sub classed SKSpriteNode for all player related stuff
var currentTouchPosition: CGPoint = CGPointZero
var beginningTouchPosition:CGPoint = CGPointZero
var currentPlayerPosition: CGPoint = CGPointZero
var playableRectArea:CGRect = CGRectZero
override func didMoveToView(view: SKView) {
/* Setup your scene here */
// IMPORTANT CODE 2 //
scene?.physicsWorld.gravity = CGVectorMake(0, 0)
// Constant - Max aspect ratio supported
let maxAspectRatio:CGFloat = 16.0/9.0
// Calculate playable height
let playableHeight = size.width / maxAspectRatio
// Determine margin on top and bottom by subtracting playable height
// from scene height and then divide by 2
let playableMargin = (size.height-playableHeight)/2.0
// Calculate the actual playable area rectangle
playableRectArea = CGRect(x: 0, y: playableMargin,
width: size.width,
height: playableHeight)
currentTouchPosition = CGPointZero
beginningTouchPosition = CGPointZero
let background = SKSpriteNode(imageNamed: "Level1_Background")
background.position = CGPoint(x: size.width/2, y: size.height/2)
background.zPosition = -1
self.addChild(background)
// CHANGED TO Put my own texture visible on the screen
currentPlayerPosition = CGPoint(x: size.width/2, y: size.height/2)
player.position = currentPlayerPosition
self.addChild(player)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
currentTouchPosition = touch.locationInNode(self)
}
let dxVectorValue = (-1) * (beginningTouchPosition.x - currentTouchPosition.x)
let dyVectorValue = (-1) * (beginningTouchPosition.y - currentTouchPosition.y)
player.movePlayerBy(dxVectorValue, dyVectorValue: dyVectorValue, duration: dt)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
player.removeAllActions()
// IMPORTANT CODE 5 //
player.stopMoving()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
print("touch")
for touch: AnyObject in touches {
beginningTouchPosition = touch.locationInNode(self)
currentTouchPosition = beginningTouchPosition
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
currentPlayerPosition = player.position
if lastUpdateTime > 0 {
dt = currentTime - lastUpdateTime
}else{
dt = 0
}
lastUpdateTime = currentTime
player.boundsCheckPlayer(playableRectArea)
}
}
GameViewController.swift
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
Player.swift
import Foundation
import SpriteKit
struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Player : UInt32 = 0b1 // 1
static let Enemy : UInt32 = 0b10 // 2
}
class Player: SKSpriteNode{
init(){
// Initialize the player object
let texture = SKTexture(imageNamed: "Player1")
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
self.xScale = 2
self.yScale = 2
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.zPosition = 1
// Player physics
// IMPORTANT CODE 1 //
let body:SKPhysicsBody = SKPhysicsBody(texture: texture, alphaThreshold: 0, size: texture.size() )
self.physicsBody = body
self.physicsBody?.allowsRotation = false
self.physicsBody?.dynamic = true
self.physicsBody?.affectedByGravity = true
self.physicsBody?.categoryBitMask = PhysicsCategory.Player
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// Check if the player sprite is within the playable area bounds
func boundsCheckPlayer(playableArea: CGRect){
let bottomLeft = CGPoint(x: 0, y: CGRectGetMinY(playableArea))
let topRight = CGPoint(x: playableArea.size.width, y: CGRectGetMaxY(playableArea))
if(self.position.x <= bottomLeft.x){
self.position.x = bottomLeft.x
// velocity.x = -velocity.x
}
if(self.position.x >= topRight.x){
self.position.x = topRight.x
// velocity.x = -velocity.x
}
if(self.position.y <= bottomLeft.y){
self.position.y = bottomLeft.y
// velocity.y = -velocity.y
}
if(self.position.y >= topRight.y){
self.position.y = topRight.y
// velocity.y = -velocity.y
}
}
/*
Move the player in a certain direction by a specific amount
*/
// IMPORTANT CODE 3 //
func movePlayerBy(dxVectorValue: CGFloat, dyVectorValue: CGFloat, duration: NSTimeInterval)->(){
print("move player")
let moveActionVector = CGVectorMake(dxVectorValue, dyVectorValue)
let movePlayerAction = SKAction.applyForce(moveActionVector, duration: 1/duration)
self.runAction(movePlayerAction)
}
// IMPORTANT CODE 4 //
func stopMoving() {
let delayTime: NSTimeInterval = 0.5 // 0.5 second pause
let stopAction: SKAction = SKAction.runBlock{
self.physicsBody?.velocity = CGVectorMake(0, 0)
}
let pause: SKAction = SKAction.waitForDuration(delayTime)
let stopSequence: SKAction = SKAction.sequence([pause,stopAction])
self.runAction(stopSequence)
}
}

Calculate rotation velocity and naturally rotate view iOS

I have a circle-shaped view and have it rotate via the following code overriding touchesBegan(touches:, withEvent:) and touchesMoved(touches:, withEvent:).
var deltaAngle = CGFloat(0)
var startTransform: CGAffineTransform?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touchPoint = touches.first!.locationInView(view)
let dx = touchPoint.x - view.center.x
let dy = touchPoint.y - view.center.y
deltaAngle = atan2(dy, dx)
startTransform = arrowPickerView.transform
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touchPoint = touches.first!.locationInView(view)
let dx = touchPoint.x - view.center.x
let dy = touchPoint.y - view.center.y
let angle = atan2(dy, dx)
let angleDifference = deltaAngle - angle
let transform = CGAffineTransformRotate(startTransform!, -angleDifference)
arrowPickerView.transform = transform
}
I want to override touchesEnded(touches:, withEvent:) to calculate the velocity and have the view naturally rotate a little (similar to continuous scrolling). I currently save the original transform and calculate the delta angle. How can I implement this? Thanks in advance!
In my example below, the CGAffineTransformRotate depends on:
direction (if the user moves from left to right, up or down etc)
rotation (a factor dependent on the time between touchesBegan and touchesEnded)
PI
This creates an CGAffineTransformRotate and this rotates with duration 1 (s), to create a feel of kinetic scrolling the UIViewAnimationOptions.CurveEaseOut property is used
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touchPointEnd = touches.first!.locationInView(view)
self.dateTouchesEnded = NSDate()
let delay : NSTimeInterval = NSTimeInterval(0)
let timeDelta : Double = self.dateTouchesEnded!.timeIntervalSince1970 - self.dateTouchesStarted!.timeIntervalSince1970
let horizontalDistance : Double = Double(touchPointEnd.x - self.touchPointStart!.x)
let verticalDistance : Double = Double(touchPointEnd.y - self.touchPointStart!.y)
var direction : Double = 1
if (fabs(horizontalDistance) > fabs(verticalDistance)) {
if horizontalDistance > 0 {
direction = -1
}
} else {
if verticalDistance < 0 {
direction = -1
}
}
let rotation : Double = (0.1 / timeDelta < 0.99) ? 0.1 / timeDelta : 0.99
let duration : NSTimeInterval = NSTimeInterval(1)
UIView.animateWithDuration(duration, delay: delay, options: UIViewAnimationOptions.CurveEaseOut, animations: {
let transform = CGAffineTransformRotate(self.imageView.transform, CGFloat(direction) * CGFloat(rotation) * CGFloat(M_PI))
self.imageView.transform = transform
}, completion: nil)
}
I added the variables to keep track of the start and end time of touches and added the CGPoint location of the touchesBegan function
var dateTouchesStarted : NSDate?
var dateTouchesEnded : NSDate?
var touchPointStart : CGPoint?
In touchesBegan i added:
self.touchPointStart = touchPoint
To set the variable as explained above.

Move the object in a specific area

I have a circle called circle and a line called ground in the View.
I can drag & move the circle in the whole view, but when I try to limit it (in the code) to move only under the ground area - it works (when I try to drag it above the ground) and the circle moves only by my x location dragging, BUT when I try to drag it back under the ground - the circle only moves by the x location dragging and the y location dragging doesn't change (the y is the ground y).
How can I make it move back again, in a free way, under the ground and not only by the x location of the dragging?
Here is what I tried so far:
let ground = SKSpriteNode()
var circle = SKShapeNode(circleOfRadius: 40)
override func didMoveToView(view: SKView) {
/* Setup your scene here */
// ground.physicsBody?.dynamic = false
ground.color = SKColor.redColor()
ground.size = CGSizeMake(self.frame.size.width, 5)
ground.position = CGPoint(x: CGRectGetMidX(self.frame), y: self.frame.size.height / 5)
self.addChild(ground)
circle.position = CGPointMake(CGRectGetMidX(self.frame) ,CGRectGetMinY(ground.frame) - (circle.frame.size.height / 2))
circle.strokeColor = SKColor.blackColor()
circle.glowWidth = 1.0
circle.fillColor = SKColor.orangeColor()
self.addChild(circle)
}
var lastTouch: CGPoint? = nil
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
let touch = touches.first
let touchLocation = touch!.locationInNode(self)
if circle.position.y < ground.position.y - (circle.frame.size.height / 2){
circle.position = touchLocation
} else {
circle.position.x = CGFloat(touchLocation.x)
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.locationInNode(self)
if circle.position.y < ground.position.y - (circle.frame.size.height / 2) {
circle.position = touchLocation
} else {
circle.position.x = CGFloat(touchLocation.x)
}
}
I have solved it:
In touchesMoved func I wrote this code:
for touch in touches {
if touchLocation.y < ground.position.y {
circle.position = touchLocation
} else {
circle.position.x = touchLocation.x
}
}

Resources