Expand SKSpritenode in one direction, in Xcode SpriteKit, using Swift - ios

I want to make an SKSpritenode expand only in one direction, in this case only upwards.
override func didMoveToView(view: SKView) {
var rightlegs = SKSpriteNode()
rightlegs.size = CGSize(width: 10, height: 50)
rightlegs.color = SKColor.yellowColor()
rightlegs.position = CGPointMake(self.frame.width / 2 + 20, self.frame.height / 2 - 40)
self.addChild(rightlegs)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
So.. When i press the screen I want the height to expand; however, not from both direction. How do I solve this problem? Thank for your time:)

You can do it by changing the anchorPoint property of the sprite to CGPoint(x:0.5, y:0.0) (this will bottom-align sprite's texture) and scaling the sprite's height using yScale property, like this:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let rightlegs = SKSpriteNode(color: .yellowColor(), size: CGSize(width: 10, height: 50))
override func didMoveToView(view: SKView) {
rightlegs.anchorPoint.y = 0.0
rightlegs.position = CGPointMake(self.frame.width / 2 + 20, self.frame.height / 2 - 40)
self.addChild(rightlegs)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//Without animation
rightlegs.yScale += 0.2
}
}
Or you can make it animated using some of the SKAction scaling methods:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//With animation
rightlegs.runAction(SKAction.scaleYTo(rightlegs.yScale + 0.2, duration: 0.2))
}
About anchorPoint from docs:
Defines the point in the sprite that corresponds to the node’s
position.
You specify the value for this property in the unit coordinate space.
The default value is (0.5,0.5), which means that the sprite is
centered on its position.
Basically what anchorPoint does is that it defines how texture will be drawn relative to node's position and affects on rotation and scaling of a node...
A good explanation about how anchor point works and what it does can be found in Apple's Working with Sprites section of documentation.

Related

applyImpulse not working when touching node

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

Swift: Agar.io-like smooth SKCameraNode movement?

If anyone here has played Agar.io before, you'll probably be familiar with the way the camera moves. It slides toward your mouse depending on the distance your mouse is from the center of the screen. I'm trying to recreate this movement using an SKCameraNode and touchesMoved:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let pos = touches.first!.location(in: view)
let center = view!.center
let xDist = pos.x - center.x
let yDist = pos.y - center.y
let distance = sqrt((xDist * xDist) + (yDist * yDist)) * 0.02
camera?.position.x += xDist * distance * 0.02
camera?.position.y -= yDist * distance * 0.02
}
Surprisingly, this doesn't look too bad. However, there are two problems with this.
The first is that I want to use a UIPanGestureRecognizer for this, since it'd work far better with multiple touches. I just can't imagine how this could be done, since I can't think of a way to use it to get the distance from the touch to the center of the screen.
The second problem is that this movement isn't smooth. If the user stops moving their finger for a single frame, there is a huge jump since the camera snaps to an absolute halt. I'm awful at maths, so I can't think of a way to implement a nice smooth decay (or attack).
Any help would be fantastic!
EDIT: I'm now doing this:
private var difference = CGVector()
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let pos = touches.first!.location(in: view)
let center = view!.center
difference = CGVector(dx: pos.x - center.x, dy: pos.y - center.y)
}
override func update(_ currentTime: TimeInterval) {
let distance = sqrt((difference.dx * difference.dx) + (difference.dy * difference.dy)) * 0.02
camera?.position.x += difference.dx * distance * 0.02
camera?.position.y -= difference.dy * distance * 0.02
}
All I need to know now is a good way to get the difference variable to smoothly increase from the time the user touches the screen.
EDIT 2: Now I'm doing this:
private var difference = CGVector()
private var touching: Bool = false
private var fade: CGFloat = 0
private func touched(_ touch: UITouch) {
let pos = touch.location(in: view)
let center = view!.center
difference = CGVector(dx: pos.x - center.x, dy: pos.y - center.y)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = true
touched(touches.first!)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
touched(touches.first!)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
touched(touches.first!)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func update(_ currentTime: TimeInterval) {
if touching {
if fade < 0.02 { fade += 0.0005 }
} else {
if fade > 0 { fade -= 0.0005 }
}
let distance = sqrt((difference.dx * difference.dx) + (difference.dy * difference.dy)) * 0.01
camera?.position.x += difference.dx * distance * fade
camera?.position.y -= difference.dy * distance * fade
}
Can someone please help me before it gets any worse? The fade variable is incremented in an awful way and it's not smooth, and I just need someone to give me a slight hint of a better way to do this.
Try Linear Interpolation. Linear interpolation can make it so your object will slowly and smoothly speed up or slow down over time.

SpriteKit Physics - Nodes not staying in bounds of screen

Simple code below creates a ball (on touch) and set physics body to make it fall and bounce. I set the bounds to match the frame, wanting the ball to remain 100% within the screen. However when running the code, the ball will move off the screen out of view left and right. How can I keep the sprite within the bounds of the screen?
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
if let touch = touches.first {
let location = touch.locationInNode(self)
let ball = SKShapeNode(circleOfRadius: 20)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 20)
ball.physicsBody!.restitution = 0.4
ball.position = location
addChild(ball)
}
}
}

How would I move my image with a touch and hold on the left and right side of the screen? - Swift

I need help with the movement in Swift. I have a SKSpriteNode and I need help with touch and drag movement left or right to move and the SKSpriteNode. But only the left and right. When I click on the other side than the x position of the picture is to just run over smoothly. Could anyone help how to do it? Thanks.
import SpriteKit
class PlayScene: SKScene {
let rocket = SKSpriteNode(imageNamed: "rocket")
override func didMoveToView(view: SKView) {
rocket.position = CGPoint(x: self.frame.width/2, y: self.frame.height * 0.8)
self.addChild(rocket)
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
rocket.position = CGPoint(x: location.x, y: self.frame.height * 0.8)
}
}
In this, the rocket animates for 1 second to the location of the touch and then moves with the touch if the user moves their finger.
class GameScene: SKScene {
let rocket = SKSpriteNode(imageNamed: "rocket")
override func didMoveToView(view: SKView) {
rocket.position = CGPoint(x: self.frame.width/2, y: self.frame.height * 0.8)
self.addChild(rocket)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
rocket.position = location
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
let moveAction = SKAction.moveTo(location, duration: 1/*Or as long as you want it to move*/)
rocket.runAction(moveAction)
}
}
}
Hope this helps.

Creating a UIView with PanGestureRecognizer and activating it without lifting the finger

I've created a circle on the screen by creating a UIView at the finger's location when touchesBegan():
In ViewController:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let pos = touch.locationInView(view)
let radius: CGFloat = touch.majorRadius * 2
let circleView = CircleView(frame: CGRectMake(pos.x - radius / 2, pos.y - radius / 2, radius, radius))
view.addSubview(circleView)
}
}
In CircleView:
override init(frame: CGRect) {
super.init(frame: frame)
let recognizer = UIPanGestureRecognizer(target: self, action: Selector("handlePan:"))
recognizer.delegate = self
self.addGestureRecognizer(recognizer)
}
This creates the circle, but does not move it immediately when I move my finger. Instead, I have to pick my finger up and place it back on the circle before handlePan() kicks in.
Is there a way to start tracking a pan gesture without lifting the finger that spawned it's parent view, taking into account there may be multiple fingers touching and moving around the screen?
The issue here is that you're using both touchesBegan, and a UIPanGestureRecognizer. For best results, use one or the other. If you're going to do it with just a pan gesture (which is what I would do), do the following:
func handlePan(gesture: UIPanGestureRecognizer) {
if gesture.state == UIGestureRecognizerState.Began {
//handle creating circle
} else if gesture.state == UIGestureRecognizerState.Changed {
//handle movement
}
}
Hope this helps!
If you have already created the circle view successfully in method touchesBegan:withEvent:
you can
Make that circle view a property
Move that circle view directly in method touchesMoved:withEvent:
This won't require you pick your finger up first
I was able to accomplish multiple independent touches by keeping a dictionary of all active touches and creating the circle views in touchesBegan() and updating their position in touchesMoved() by querying that dictionary.
var fingerTouches = [UITouch: CircleView]()
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let pos = touch.locationInView(view)
let radius: CGFloat = touch.majorRadius * 2
let circle = CircleView(frame: CGRectMake(pos.x - radius / 2, pos.y - radius / 2, radius, radius))
view.addSubview(circle)
fingerTouches[touch] = circle
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let pos = touch.locationInView(view)
let radius: CGFloat = touch.majorRadius * 2
fingerTouches[touch]!.frame = CGRectMake(pos.x - radius / 2, pos.y - radius / 2, radius, radius)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
fingerTouches[touch]!.removeFromSuperview()
}
}

Resources