Move the object in a specific area - ios

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
}
}

Related

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
}
}
}
}

SpriteKit - Mouse Snaps to Center On Sprite Drag

I'm trying to create an iOS game and I have it so that when the item is touched and moved it is dragged. When it is dragged the mouse is snapped to the center of the sprite. How could I make it so that this wouldn't happen?
Here is an example of what is happening.
Here are the functions dealing with touch input
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var papers = 0
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let touchedNode = nodeAtPoint(location)
if ((touchedNode.name?.contains("paper")) != nil) {
touchedNode.position = location
touchedNode.position.x = CGRectGetMidX(self.frame)
}
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let touchedNode = nodeAtPoint(location)
if ((touchedNode.name?.contains("paper")) != nil) {
touchedNode.position = location
touchedNode.position.x = CGRectGetMidX(self.frame)
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let touchedNode = nodeAtPoint(location)
if ((touchedNode.name?.contains("paper")) != nil) {
touchedNode.zPosition = 0
touchedNode.position.x = CGRectGetMidX(self.frame)
}
}
}
P.S. contains is an extension off of the String class to check if a substring is in a string
Thanks in advance!
It's fairly straightforward to drag a sprite from the touch location instead of from the center of the sprite. To do this, calculate and store the difference (i.e., offset) between the touch location and the center of the sprite. Then, in touchesMoved, set the sprite's new position to the touch location plus the offset.
You can optionally overload the + and - operators to simplify adding and subtracting CGPoints. Define this outside of the GameScene class:
func - (left:CGPoint,right:CGPoint) -> CGPoint {
return CGPoint(x: right.x-left.x, y: right.y-left.y)
}
func + (left:CGPoint,right:CGPoint) -> CGPoint {
return CGPoint(x: right.x+left.x, y: right.y+left.y)
}
In GameScene, define the following instance variable
var offset:CGPoint?
and then in touchesBegan, replace
touchedNode.position = location
with
offset = location - touchedNode.position
and in touchesMoved, replace
touchedNode.position = location
with
if let offset = self.offset {
touchedNode.position = location + offset
}
I generalized the solution to offset the sprite's position in both the x and y dimensions. In your app, you can simply offset the sprite's y position since x is ignored.
You don't need touchesBegan and touchesEnded for this. You can use just touchesMoved:
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let touchedNode = nodeAtPoint(location)
let previousPosition = touch.previousLocationInNode(self)
if (touchedNode.name == "paper") {
var translation:CGPoint = CGPoint(x: location.x - previousPosition.x , y: location.y - previousPosition.y )
touchedNode.position = CGPoint(x: touchedNode.position.x , y: touchedNode.position.y + translation.y)
}
}
The idea is to calculate translation. You can read more about this solution here. For future readers Obj-C solution on StackOverflow can be found on this link.

nodeAtPoint wrong node

I'm making a game with swift and spritekit and I have a function where I have multiple sprites falling from the top of the screen. I also made it so I can be able to detect a touch on the sprite using nodeAtPoint, and made it possible to flick the sprites. My problem is that due to nodeAtPoint dragging the deepest node in the tree, when i click and drag on a sprite, the newest sprite in the scene gets pulled toward my touch instead of the one i originally touched. If anyone has some suggestions on how to only affect the node I touched I'd really appreciate it.
class GameScene: SKScene {
var ball = SKSpriteNode(imagedNamed: "blueBlue")
var touchedNode: SKNode? = SKNode()
override func didMoveToView(view: SKView) {
var create = SKAction.runBlock({() in self.createTargets()})
var wait = SKAction.waitForDuration(2)
var waitAndCreateForever = SKAction.repeatActionForever(SKAction.sequence([create, wait]))
self.runAction(waitAndCreateForever)
}
func createTargets() {
ball = SKSpriteNode(imageNamed: "blueBlue")
let randomx = Int(arc4random_uniform(170) + 290)
ball.zPosition = 0
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width / 11)
ball.physicsBody?.dynamic = true
ball.size = CGSize(width: ball.size.width / 1.5, height: ball.size.height / 1.5)
let random = Int(arc4random_uniform(35))
let textures = [texture1, texture2, texture3, texture4, texture5, texture6, texture7, texture8, texture9, texture10, texture11, texture12, texture13, texture14, texture15, texture16, texture17, texture18, texture19, texture20, texture21, texture22, texture23, texture24, texture25, texture1, texture7, texture18, texture24, texture25, texture1, texture7, texture18, texture24, texture25]
ball.texture = textures[random]
ball.position = CGPoint(x: randomx, y: 1400)
addChild(ball)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
touchedNode = self.nodeAtPoint(touchLocation)
if touchedNode.frame.contains(touchLocation) {
touching = true
touchPoint = touchLocation
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
touching = true
touchPoint = touchLocation
}
override func update(currentTime: NSTimeInterval) {
if touching {
if touchPoint != touchedNode.position {
let dt: CGFloat = 0.1
let distance = CGVector(dx: touchPoint.x - touchedNode.position.x, dy: touchPoint.y - touchedNode.position.y)
let vel = CGVector(dx: distance.dx / dt, dy: distance.dy / dt)
if touchedNode.parent != nil {
touchedNode.physicsBody?.velocity = vel
}
}
}
}
}
I suggest you make the following changes to the code:
Add a check to see if a sprite is already being moved (if yes, do nothing)
Turn off the affectedByGravity property for the sprite being moved
Reset touchedNode and touching variables when the touch ends
Here's an implementation with the above changes...
Replace touchesBegan and touchesMoved with the following
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
if (!touching) {
let node = self.nodeAtPoint(touchLocation)
if node is SKSpriteNode {
touchedNode = node
touching = true
touchPoint = touchLocation
touchedNode!.physicsBody?.affectedByGravity = false
}
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
if (touching) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
touchPoint = touchLocation
}
}
And add this method
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
if (touchedNode != nil) {
touchedNode!.physicsBody!.affectedByGravity = true
}
touchedNode = nil
touching = false
}

Dragging and flicking/throwing skspritenode [duplicate]

This question already has answers here:
how to throw SKSpriteNode?
(3 answers)
Closed 7 years ago.
I have ball sprite that i want to be able to flick towards other sprites. Right now, I have the function TouchesMoved below, but it moves my sprite towards any touch movement on the screen, when I want my user to first touch the sprite and drag their finger towards the target, causing the sprite to move. This is the dysfunctional function.. Any help would be appreciated!
EDIT: I wanted the ball velocity to remain constant no matter the flick length.. not addressed by other answers.
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
if firstTimerStarted == false {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
sceneTouched(touchLocation)
}
This should work, I just dug it out from an old project.
CGFloat dt is for changing the speed/power of the movement.
var touching = false
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let location = touch.locationInNode(self)
if sprite.frame.contains(location) {
touchPoint = location
touching = true
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let location = touch.locationInNode(self)
touchPoint = location
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
touching = false
}
override func update(currentTime: CFTimeInterval) {
if touching {
if touchPoint != sprite.position
{
let dt:CGFloat = 0.15
let distance = CGVector(dx: touchPoint.x-sprite.position.x, dy: touchPoint.y-sprite.position.y)
let vel = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity = vel
}
}
}
EDIT:
The reason it gets stronger the farther the distance, is because the vector IS the distance between the the sprite and the touch point.
Try popping this in as the update function. It should work...
override func update(currentTime: CFTimeInterval) {
if touching {
if touchPoint != sprite.position
{
let pointA = touchPoint
let pointB = sprite.position
let pointC = CGPointMake(sprite.position.x + 2, sprite.position.y)
let angle_ab = atan2(pointA.y - pointB.y, pointA.x - pointB.x)
let angle_cb = atan2(pointC.y - pointB.y, pointC.x - pointB.x)
let angle_abc = angle_ab - angle_cb
let vectorx = cos(angle_abc)
let vectory = sin(angle_abc)
let dt:CGFloat = 15
let vel = CGVector(dx: vectorx * dt, dy: vectory * dt)
sprite.physicsBody!.velocity = vel
}
}
}
With the touchPoint (pointA), and the sprite's position (pointB) we can create an angle.
atan2, is a very famous function from C, it creates the angle between two points.
BUT, it's 0 degrees is in a different location than usual.
So, we need our own 0 degrees marker, I use the mid-right of the point as my marker.
It's the common 0 degree placement:
Since it's to the right of the sprite's position, we create a point just to the right of the sprite (pointC).
We now use atan2 to find the angle.
To create a vector from an angle, we just use cos and sin for the x and y values.

Changing SKScene causes game lags

I'm developing a game in iOS Spritekit using Swift. I created a menu and then I'm calling a GameScene appear jerking lags.
But! If I load only game scene everything all right, if I'm load game, and change to another scene, it's causes lags.
I`m tested on iPhone 5 (iOS 8.1.x/8.2 beta) Xcode(6.1.x/6.2 beta). In simulator everything all right
You can see that on this simple code:
import SpriteKit
class GameScene: SKScene {
let kMovementSpeed:CGFloat = 3.8
var firstX:CGFloat = 0
override func didMoveToView(view: SKView) {
self.backgroundColor = SKColor.whiteColor()
self.anchorPoint = CGPointMake (0.5,0.5)
self.physicsBody = SKPhysicsBody (edgeLoopFromRect: self.frame)
addBall()
}
func addBall(){
var ball = SKShapeNode(circleOfRadius: CGFloat(30))
ball.name = "ball"
ball.fillColor = .redColor()
ball.position = CGPointMake (-200, 0)
ball.physicsBody = SKPhysicsBody (circleOfRadius: 30)
self.addChild(ball)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
var touch:UITouch!
touch = touches.anyObject() as UITouch
let location = touch.locationInNode(self)
if ((location.x <= 0) & (location.y <= 0)) {
firstX = location.x
}
else if ((location.x >= 0) & (location.y <= 0)) {
var ball = self.childNodeWithName("ball") as SKShapeNode
ball.physicsBody?.applyImpulse(CGVector(dx: 0,dy: 113))
}
else if ((location.x <= 0) & (location.y >= 0)) {
var ball = self.childNodeWithName("ball") as SKShapeNode
ball.removeAllActions()
var game:GameScene!
self.removeAllActions()
var moveSequence = SKAction.runBlock({
game = GameScene(size:(self.view?.frame.size)!)
game.scaleMode = .AspectFill
})
self.runAction(moveSequence, completion: {
self.view?.presentScene(game)
return
})
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if ((location.x <= 0) & (location.y <= 0)) {
var ball = self.childNodeWithName("ball") as SKShapeNode
let ySpeed = ball.physicsBody?.velocity.dy
ball.physicsBody?.velocity.dx = kMovementSpeed * (location.x - firstX)
}
}
}
}
upper-left corner - represent scene.
bottom left corner - simple joystick.
bottom-right corner - jump.
What I'm doing wrong?

Resources