can't rotate a circle node around a fix point - sprite-kit

I'm trying to set a SKShapeNode that rotating around fixed point depend on user's swipe gesture. to do so i'm using the code that answered here.
when I set the node as a rectangle - it's working just fine, the problem is when I trying to set the node as a circle (as I needed it to be). I have no idea why its happening, and what makes the difference.
here is the code that works (SKShapeNode as a rectangle):
override func didMove(to view: SKView) {
let ball = SKShapeNode(rect: CGRect(x: 100, y: 100, width: 100, height: 100))
ball.fillColor = SKColor.red
ball.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2)
ball.name = "BALL"
ball.physicsBody = SKPhysicsBody(circleOfRadius: 200)
ball.physicsBody?.angularDamping = 0.25
ball.physicsBody?.pinned = true
ball.physicsBody?.affectedByGravity = false
self.addChild(ball)
let om = 5.0
let er = 4.0
let omer = atan2(om, er)
print("om = \(om), er = \(er), atan2(omer) = \(omer)")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in:self)
let node = atPoint(location)
if node.name == "BALL" {
let dx = location.x - node.position.x
let dy = location.y - node.position.y
// Store angle and current time
startingAngle = atan2(dy, dx)
startingTime = touch.timestamp
node.physicsBody?.angularVelocity = 0
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let location = touch.location(in:self)
let node = atPoint(location)
if node.name == "BALL" {
let dx = location.x - node.position.x
let dy = location.y - node.position.y
let angle = atan2(dy, dx)
// Calculate angular velocity; handle wrap at pi/-pi
var deltaAngle = angle - startingAngle!
if abs(deltaAngle) > CGFloat.pi {
if (deltaAngle > 0) {
deltaAngle = deltaAngle - CGFloat.pi * 2
}
else {
deltaAngle = deltaAngle + CGFloat.pi * 2
}
}
let dt = CGFloat(touch.timestamp - startingTime!)
let velocity = deltaAngle / dt
node.physicsBody?.angularVelocity = velocity
// Update angle and time
startingAngle = angle
startingTime = touch.timestamp
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
startingAngle = nil
startingTime = nil
}
but when i change the SKShapeNode (inside my didMove function) to circle is just dont work - here is the changed code:
override func didMove(to view: SKView) {
let ball = SKShapeNode(circleOfRadius: 200)
ball.fillColor = SKColor.red
ball.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2)
ball.name = "BALL"
ball.physicsBody = SKPhysicsBody(circleOfRadius: 200)
ball.physicsBody?.angularDamping = 0.25
ball.physicsBody?.pinned = true
ball.physicsBody?.affectedByGravity = false
self.addChild(ball)
}
does anyone see/ know what am I dong wrong?

How would you expect to see that the shape is turning? Do you have something inside the shapenode with a texture that you can actually see rotating?
A turning circle shapenode looks the same as a static one. In the update loop of the scene grab the ball and print its zRotation to see if it's actually turning.
override func update(_ currentTime: TimeInterval) {
var ball: SKShapeNode = childNodeWithName("BALL") as SKShapeNode
print(ball.zRotation)
}
(this code was written without access to Swift so you might have to fix the syntax a tiny bit)
Remember the zRotation is measured in radians so it might not look how you expect but it it's changing then the SKShapeNode is actually rotating, you just can't see it.
You'll have to add a child node that has something visible on it.

Related

SpriteKit: How to create a region that's the same size as the device screen?

I'm working in SpriteKit and I need to create a 'playable area', an area that is same size as the device screen so that I can stop my player from moving off-screen.
I'm using the following line of code:
var playableRect: CGRect = UIScreen.main.bounds
But the resulting rectangle is about a quarter of the device screen, with a corner of that rectangle at what looks to be the center of the screen. And the device orientation doesn't change that.
I've tried everything I can think of. Nothing is working.
How do I create a rectangle that's the same size as the device screen?
Here's my full coding, after changing to include Gene's suggestion. I incorporated Gene's suggestion by revising playableRect inside the didMove method. But the result is unchanged with that coding.
import SpriteKit
import GameplayKit
import CoreMotion
#objcMembers
class GameSceneUsingTilt: SKScene {
let player = SKSpriteNode(imageNamed: "player-motorbike")
let motionManager = CMMotionManager()
var playableRect = UIScreen.main.bounds
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "road")
background.zPosition = -1
addChild(background)
if let particles = SKEmitterNode(fileNamed: "Mud") {
let farRightPt = frame.maxX // start the emitter at the far right x-point of the view
particles.advanceSimulationTime(10)
particles.position.x = farRightPt
addChild(particles)
}
let nearLeftPt = frame.minX * 3 / 4 // start the player at a quarter of the way to the far left x-point of the view
player.position.x = nearLeftPt
player.zPosition = 1
addChild(player)
motionManager.startAccelerometerUpdates()
// coding below shows outline of playableRect
let bounds = UIScreen.main.bounds
let scale = UIScreen.main.scale
// let size = CGSize(width: bounds.size.width * scale, height: bounds.size.height * scale)
playableRect = CGRect(x: 0, y: 0, width: bounds.size.width * scale, height: bounds.size.height * scale)
drawPlayableRect(rect: playableRect)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
func boundsCheck() {
let playableRect = CGRect(x: frame.minX, y: frame.minY, width: frame.width, height: frame.height)
let bottomLeft = CGPoint(x: playableRect.minX, y: playableRect.minY)
let topRight = CGPoint(x: playableRect.maxX, y: playableRect.maxY)
if player.position.x <= bottomLeft.x {
player.position.x = bottomLeft.x
}
if player.position.x >= topRight.x {
player.position.x = topRight.x
}
if player.position.y <= bottomLeft.y {
player.position.y = bottomLeft.y
}
if player.position.y >= topRight.y {
player.position.y = topRight.y
}
}
override func update(_ currentTime: TimeInterval) {
if let accelerometerData = motionManager.accelerometerData {
// note that since the game will be running in landscape mode, up and down movement is controlled by the x axis and right to left movement is controlled by the y-axis...so this is the inverse of what you'd normally think.
let changeX = CGFloat(accelerometerData.acceleration.y) * 4
let changeY = CGFloat(accelerometerData.acceleration.x) * 4
// you have to subtract from x position and add to the y position because device is rotated so the axises aren't what you would normally expect.
player.position.x -= changeX
player.position.y += changeY
// check to make sure position isn't outside payable area:
boundsCheck()
}
}
// function below shows outline of playableRect
func drawPlayableRect(rect: CGRect) {
let shape = SKShapeNode()
let path = CGMutablePath()
path.addRect(rect)
shape.path = path
shape.strokeColor = .red
shape.lineWidth = 4.0
addChild(shape)
}
}
Use: UIScreen.main.nativeBounds

Objects Position Not Matching Up (Spritekit)

I am building a spritekit game and have just started the project. I have a circle on the screen which starts in the center, and when i drag my finger from the circle outward, it will show a dotted line/bezierpath connected to the ball which will help the user see where it is aiming the ball. When the user lifts their finger, the ball will shoot in the opposite direction of the aim line. (Think a game like soccer stars or pool). The issue is that the maneuver works the first time when everything starts in the middle: I drag my finger and the ball shoots in opposite direction then stops. But when I try it again, the position of the aiming line says it is the same as the ball (It should be), but then it shows up like an inch away from the ball on the screen. I feel like this may be an issue that the scene(s) behind the objects may not be the same size? But I'm confused because I think I'm only using one scene.
GameViewController viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
scene.size = view.bounds.size
//scene.anchorPoint = CGPoint(x: 0.0, y: 0.0)
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
GameScene Code (Doubt you need all of it but whatever):
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKShapeNode(circleOfRadius: 35)
var touchingBall = false
var aimLine = SKShapeNode()
var startAimPoint = CGPoint()
var endAimPoint = CGPoint()
let damping:CGFloat = 0.94
override func didMove(to view: SKView) {
ball.fillColor = SKColor.orange
ball.name = "ball"
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
var physicsBody = SKPhysicsBody(circleOfRadius: 35)
ball.physicsBody = physicsBody
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.friction = 10.0
ball.position = CGPoint(x: frame.midX, y: frame.midY)
self.addChild(ball)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOUCHES BEGAN.")
for touch in touches {
print("TB: \(touchingBall)")
let location = touch.location(in: self)
let node : SKNode = self.atPoint(location)
if node.name == "ball" {
// touched inside node
if ball.physicsBody!.angularVelocity <= 0.0{
touchingBall = true
startAimPoint = ball.position
print(touchingBall)
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOCUHES MOVED.")
for touch in touches {
let location = touch.location(in: self)
if touchingBall{
endAimPoint = location
assignAimLine(start: startAimPoint, end: endAimPoint)
print("Moving touched ball")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches ended. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x - startAimPoint.x) * 3, dy: -(endAimPoint.y - startAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches cancelled. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x - startAimPoint.x) * 3, dy: -(endAimPoint.y - startAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
print(ball.physicsBody!.velocity)
let dx2 = ball.physicsBody!.velocity.dx * damping
let dy2 = ball.physicsBody!.velocity.dy * damping
ball.physicsBody!.velocity = CGVector(dx: dx2, dy: dy2)
}
func assignAimLine(start: CGPoint, end: CGPoint){
aimLine.removeFromParent()
var bezierPath = UIBezierPath()
bezierPath.move(to: start)
bezierPath.addLine(to: shortenedEnd(startPoint: start, endPoint: end))
var pattern : [CGFloat] = [10.0, 10.0]
let dashed = SKShapeNode(path: bezierPath.cgPath.copy(dashingWithPhase: 2, lengths: pattern))
aimLine = dashed
aimLine.position = ball.position
aimLine.zPosition = 0
self.addChild(aimLine)
}
func hypotenuse(bp: UIBezierPath) -> Double{
var a2 = bp.cgPath.boundingBox.height * bp.cgPath.boundingBox.height
var b2 = bp.cgPath.boundingBox.width * bp.cgPath.boundingBox.width
return Double(sqrt(a2 + b2))
}
func hypotenuse(startP: CGPoint, endP: CGPoint) -> Double{
var bezierPath = UIBezierPath()
bezierPath.move(to: startP)
bezierPath.addLine(to: endP)
return hypotenuse(bp: bezierPath)
}
func shortenedEnd(startPoint: CGPoint, endPoint: CGPoint) -> CGPoint{
var endTemp = endPoint
//while hypotenuse(startP: startPoint, endP: endTemp) > 150{
endTemp = CGPoint(x: endTemp.x / 1.01, y: endTemp.y / 1.01)
//}
return endTemp
}
func addTestPoint(loc: CGPoint, color: UIColor){
var temp = SKShapeNode(circleOfRadius: 45)
temp.fillColor = color
temp.position = loc
self.addChild(temp)
}
}
I tried printing the frame size for the scene and it says 400 something x 700 something (I am testing on iPhone 6 Plus), and it says the UIScreen is same size so i don't know what issue is. Overall, I just need the aiming line to be on the center of the circle more than just the first time I try the maneuver. Thanks.
Like I mentioned in the comments, your problem was how you were laying out your paths. The code below makes the path relative to the ball instead of absolute to the scene. I also fixed the issue with creating new shapes every time.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKShapeNode(circleOfRadius: 35)
var touchingBall = false
var aimLine = SKShapeNode()
var endAimPoint = CGPoint()
override func didMove(to view: SKView) {
ball.fillColor = SKColor.orange
ball.name = "ball"
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
ball.position = CGPoint(x: frame.midX, y: frame.midY)
let physicsBody = SKPhysicsBody(circleOfRadius: 35)
physicsBody.affectedByGravity = false
physicsBody.friction = 10.0
physicsBody.linearDamping = 0.94
ball.physicsBody = physicsBody
self.addChild(ball)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOUCHES BEGAN.")
for touch in touches {
print("TB: \(touchingBall)")
let location = touch.location(in: self)
let node : SKNode = self.atPoint(location)
if node.name == "ball" {
// touched inside node
if ball.physicsBody!.angularVelocity <= 0.0{
touchingBall = true
aimLine.path = nil
self.addChild(aimLine)
print(touchingBall)
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOCUHES MOVED.")
for touch in touches {
let location = touch.location(in: self)
if touchingBall{
endAimPoint = self.convert(location, to: ball)
assignAimLine(end: endAimPoint)
print("Moving touched ball")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches ended. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x) * 3, dy: -(endAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches cancelled. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x) * 3, dy: -(endAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
print(ball.physicsBody!.velocity)
//let dx2 = ball.physicsBody!.velocity.dx * damping
//let dy2 = ball.physicsBody!.velocity.dy * damping
//ball.physicsBody!.velocity = CGVector(dx: dx2, dy: dy2)
}
func assignAimLine(end: CGPoint){
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint.zero)
bezierPath.addLine(to: end)
let pattern : [CGFloat] = [10.0, 10.0]
aimLine.position = ball.position
aimLine.path = bezierPath.cgPath.copy(dashingWithPhase: 2, lengths: pattern)
aimLine.zPosition = 0
}
func addTestPoint(loc: CGPoint, color: UIColor){
var temp = SKShapeNode(circleOfRadius: 45)
temp.fillColor = color
temp.position = loc
self.addChild(temp)
}
}

Move Sprite Based Off of Degrees

I've been able to detect degrees, but I'm unsure on how to move the sprite in 360 degrees.
I don't want the sprite to be able to only move in certain sections, as shown in the picture above, but rather have it be able to move in a full circle.
Code:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if (ball.frame.contains(location)) {
stickActive = true
}else {
stickActive = false
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if (stickActive == true) {
var v = CGVector(dx: location.x - base.position.x, dy: location.y - base.position.y)
let angle = atan2(v.dy, v.dx)
var deg = angle * CGFloat(180 / M_PI)
print(deg + 180)
let lenght:CGFloat = base.frame.size.height / 2 - 20
let xDist: CGFloat = sin(angle - 1.57079633) * lenght
let yDist: CGFloat = cos(angle - 1.57079633) * lenght
ball.position = CGPoint(x: base.position.x - xDist, y: base.position.y + yDist)
if (base.frame.contains(location)) {
ball.position = location
}else {
ball.position = CGPoint(x: base.position.x - xDist, y: base.position.y + yDist)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if (stickActive == true) {
let move: SKAction = SKAction.move(to: base.position, duration: 0.2)
move.timingMode = .easeOut
ball.run(move)
}
}
This is super simple, although it took me quite awhile to figure out how to do it.
TouchesBegan method:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if isTracking == false && DPad.contains(location) {
isTracking = true
}
}
}
TouchesMoved method:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location: CGPoint = touch.location(in: self)
if isTracking == true {
v = CGVector(dx: location.x - DPad.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 = DPad.frame.size.height / 2
let xDist: CGFloat = sin(angle - 1.57079633) * Length
let yDist: CGFloat = cos(angle - 1.57079633) * Length
xJoystickDelta = location.x - DPad.position.x
yJoystickDelta = location.y - DPad.position.y
if DPad.contains(location) {
thumbNode.position = location
} else {
thumbNode.position = CGPoint(x: DPad.position.x - xDist, y: DPad.position.y + yDist)
}
}
}
}
TouchesEnded method:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
isTracking = false
thumbNode.run(SKAction.move(to: DPad.position, duration: 0.01))
xJoystickDelta = 0
yJoystickDelta = 0
}
The update(_ currentTime:) method:
if v.dx > abs(v.dy) {
yourPlayer.texture = SKTexture(imageNamed: "rightTexture")
} else if v.dx < -abs(v.dy) {
player.texture = SKTexture(imageNamed: "leftTexture")
} else if v.dy < 0 {
yourPlayer.texture = SKTexture(imageNamed: "frontTexture")
} else if v.dy > 0 {
yourPlayer.texture = SKTexture(imageNamed: "backTexture")
}
//This code moves your character where-ever you want it too
let xScale = CGFloat(4) //adjust to your preference. Higher means slower, lower means faster
let yScale = CGFloat(4) //adjust to your preference. Higher means slower, lower means faster
let xAdd = xScale * self.xJoystickDelta
let yAdd = yScale * self.yJoystickDelta
yourPlayerNode.position.x += xAdd
yourPlayerNode.position.y += yAdd
These things need to be outside your didMove(toView:) method:
var xJoystickDelta:CGFloat = 0
var yJoystickDelta:CGFloat = 0
var v = CGVector()
var isTracking:Bool = false
var DPad = SKSpriteNode()
var thumbNode = SKSpriteNode()
-Explanation-
In the touchesBegan method, the if-statement is testing to see if you are not controlling the thumbNode and if your touch is inside the DPad Node. Then it starts the tracking.
In the touchesMoved once isTracking == true it starts calculating the neccessary math and then adjusting the various things required. (its complicated, what matters most is that it works.)
In the touchesEnded method, it is testing to see when you lift your finger off of the screen, then it resets everything for the next usage.
In the update(_ current:) method, the code is calculating the angle of the CGVector and then setting a texture (or whatever you want to do) inside of the various cases. Then its calculating the position of the thumbNode inside the DPad and moving your player (or whatever you need to move) around in the scene. Adjust the xScale and yScale floats higher to slow down the movement, and lower to increase the movement of whatever you are trying to move.
-Extra neccessary stuff-
You need to set up the DPad and thumbNode inside your didMove(toView:) method:
thumbNode.size = CGSize(width: 50, height: 50)
DPad.size = CGSize(width: 150, height: 150)
DPad.position = CGPoint(x: 0, y: 0)
thumbNode.position = DPad.position
DPad.zPosition = 3
thumbNode.zPosition = 4
DPad.texture = SKTexture(imageNamed: "yourBaseTexture")
thumbNode.texture = SKTexture(imageNamed: "yourStickTexture")
self.addChild(thumbNode)
self.addChild(DPad)
You only have to move the DPad.position to wherever you want. The thumbNode will move along with it. Also, if you have any problems be sure to ask me so I can help you.

How to move sprite with a joystick within a certain limit?

I want to make it so that the player can move with a joystick but cant go out of the circle. I made the joystick but I don't know how to do the other stuff. There is an example in the image below and also my code. Hope someone can help me, thanks.
class GameScene: SKScene {
var circuloPrincipal = SKSpriteNode(imageNamed: "circulo")
var circuloFondo = SKSpriteNode(imageNamed: "circuloFondo")
let base = SKSpriteNode(imageNamed: "circuloFondo")
let ball = SKSpriteNode(imageNamed: "circulo")
var stickActive:Bool = false
override func didMoveToView(view: SKView) {
/* Setup your scene here */
base.size = CGSize(width: 100, height: 100)
base.alpha = 0.3
base.zPosition = 2.0
base.position = CGPoint(x: frame.width / 2, y: frame.height / 2 - 310)
self.addChild(base)
ball.size = CGSize(width: 50, height: 50)
ball.color = circuloPrincipal.color
//ball.alpha = 0
ball.zPosition = 3.0
ball.position = base.position
self.addChild(ball)
circuloPrincipal.size = CGSize(width: 35, height: 35)
circuloPrincipal.position = CGPoint(x: frame.width / 2, y: frame.height / 2)
self.addChild(circuloPrincipal)
circuloPrincipal.color = UIColor(red: 0.75, green: 0.75, blue: 0.75, alpha: 1.0)
circuloPrincipal.colorBlendFactor = 1.0
circuloPrincipal.zPosition = 3.0
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
if (CGRectContainsPoint(ball.frame, location)) {
stickActive = true
}else {
stickActive = false
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
if (stickActive == true) {
let v = CGVector(dx: location.x - base.position.x, dy: location.y - base.position.y)
let angle = atan2(v.dy, v.dx)
let deg = angle * CGFloat(180 / M_PI)
print(deg + 180)
let lenght:CGFloat = base.frame.size.height / 2 - 20
let xDist: CGFloat = sin(angle - 1.57079633) * lenght
let yDist: CGFloat = cos(angle - 1.57079633) * lenght
ball.position = CGPointMake(base.position.x - xDist, base.position.y + yDist)
if (CGRectContainsPoint(base.frame, location)) {
ball.position = location
}else {
ball.position = CGPointMake(base.position.x - xDist, base.position.y + yDist)
}
} // termina stickActive
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if (stickActive == true) {
let move: SKAction = SKAction.moveTo(base.position, duration: 0.2)
move.timingMode = .EaseOut
ball.runAction(move)
}
}
Look into SKConstraint - you can set a constraint that a node cannot move further than a specified distance from another node. You'd set this 'other' node to the centre of the area that your player is limited to. Sprite-Kit will automatically move your node back should it go to far. As this is being in the game loop at 60fps before the node is redrawn after it's moved, you won't get any weird 'jumping' effects.
https://developer.apple.com/reference/spritekit/skconstraint
That documentation is a bit lacking compared to some of the other SK documentation. Here's and example that make a green rectangle 'follow' a yellow triangle within a specified distance:
let distanceRange = SKRange(lowerLimit: 200, upperLimit: 400)
let distanceConstraint = SKConstraint.distance(distanceRange, toNode: yellowTriangle)
greenRect.constraints = [distanceConstraint]
The constraints property of an SKNode (greenRect) is an array, so if you also want an orientation constraint (to keep greenRect facing the yellow triangle, for example), you would code something like this:
let orientRange = SKRange(lowerLimit: 0.0, upperLimit: 0.0)
let orientConstraint = SKConstraint.orientToNode(yellowTriangle, offset: orientRange)
greenRect.constraints = [orientatConstraint, distanceConstraint]
For your particular example, you might want to set the constraint as:
let distanceRange = SKRange(lowerLimit: 0, upperLimit: limitcircle.size/2)
let distanceConstraint = SKConstraint.distance(distanceRange, toPoint: limitCircle.position)
player.constraints = [distanceConstraint]
This assumes that the circle from which the player cannot move is an SKNode called limitcircle and it's anchor point is set to (0.5, 0.5) i,e, it's center. The above code would constrain the player to a distance of between 0 and 1/2 the width of the circle (i.e. it's radius) from a point at the center of the circle.

how do i make a node go from one side of the screen to the other?

I'm just starting in sprite kit and have hit a road block. I'm trying to recreate the old atari game asteroids. I'm currently trying to find out how to move the node "ship" from one side of the screen and come out the opposite side. An example of this would be pacman going from the right side of the screen and out the left side of the screen. Help is greatly appreciated.
Thanks in advance,
Jared
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate{
let base = SKSpriteNode(imageNamed: "Base")
let ball = SKSpriteNode(imageNamed: "Ball")
let ship = SKSpriteNode(imageNamed: "Ship")
let shoot = SKSpriteNode(imageNamed: "shootButton")
override func didMoveToView(view: SKView){
// var DynamicView=UIView(frame: CGRectMake(100, 200, 100, 100))
// DynamicView.backgroundColor=UIColor.greenColor()
// DynamicView.layer.cornerRadius=2
// DynamicView.layer.borderWidth=2
// self.view!.addSubview(DynamicView)
self.anchorPoint = CGPointMake(0.5, 0.5)
self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0)
self.addChild(base)
base.position = CGPointMake(-350, -200)
self.addChild(shoot)
shoot.position = CGPointMake(350, -200)
self.addChild(ball)
ball.position = base.position
self.addChild(ship)
ship.position = CGPointMake(20, 47)
ship.xScale = 0.7
ship.yScale = 0.7
ship.physicsBody?.dynamic = true
ship.physicsBody?.allowsRotation = true
ship.physicsBody?.affectedByGravity = true
ship.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "Ship"), size: ship.size)
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
ball.alpha = 0.4
base.alpha = 0.4
}
// 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 touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
let v = CGVector(dx: location.x - base.position.x, dy: location.y - base.position.y)
let angle = atan2(v.dy, v.dx)
let deg = angle * CGFloat(180 / M_PI)
// print( deg + 180)
let length:CGFloat = base.frame.size.height / 2
let xDist:CGFloat = sin(angle - 1.57079633) * length
let yDist:CGFloat = cos(angle - 1.57079633) * length
ball.position = CGPointMake(base.position.x - xDist, base.position.y + yDist)
if CGRectContainsPoint(base.frame, location) {
ball.position = location
}
else{
ball.position = CGPointMake(base.position.x - xDist, base.position.y + yDist)
}
ship.zRotation = angle - 1.57079633
ship.physicsBody?.mass = 2
var shipRotation : CGFloat = ship.zRotation
var calcRotation : Float = Float(angle - 1.57079633) + Float(M_PI_2);
let intensity : CGFloat = 2000.0 // put your value
let xVelocity = intensity * CGFloat(cosf(calcRotation))
let yVelocity = intensity * CGFloat(sinf(calcRotation))
let vector : CGVector = CGVectorMake(xVelocity, yVelocity)
//Apply force to spaceship
ship.physicsBody?.applyForce(vector)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let move:SKAction = SKAction.moveTo(base.position, duration: 0.2)
move.timingMode = .EaseOut
ball.runAction(move)
}
}
// overridefunc update(currentTime: CFTimeInterval) {
// /* Called before each frame is rendered */
//
//}
In func update(currentTime) check if ship.position.x < 0 or ship.position.x > scene.width. If true, set ship.position.x to the opposite side.
It looks like you're using physics to move the ship (as opposed to updating its position by a certain amount in the update() method)
So what you could do is to override the didiSimulatePhysics() method (which is called after the SpriteKit game engine has done all the physics calculations and moved all the nodes) and if your ship is off screen (which you could do by seeing if it's position is outside the screne's x & y boundaries, or by using the intersectsRect method to see if. The ship's frame no longer overlaps the screen frame) and if it is, simply wrap it to the other side of the screen.