SpriteKit SKTileMapNode same physics body for multiple tiles - ios

The following code is correctly adding physicsBodies for every "wall" tile in my SKTileMapNode. However, I would like to group them all under the same node with one physicsBody for the entire group. How do I achieve that?
private func setupTileMap() {
let tileSize = self.tileMap.tileSize
let halfWidth = CGFloat(self.tileMap.numberOfColumns) / 2.0 * tileSize.width
let halfHeight = CGFloat(self.tileMap.numberOfRows) / 2.0 * tileSize.height
for column in 0..<self.tileMap.numberOfColumns {
for row in 0..<self.tileMap.numberOfRows {
let tileDefinition = self.tileMap.tileDefinition(atColumn: column, row: row)
let tileX = CGFloat(column) * tileSize.width - halfWidth
let tileY = CGFloat(row) * tileSize.height - halfHeight
let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)
let tileNode = SKShapeNode(rect: rect)
tileNode.strokeColor = .clear
tileNode.position = CGPoint(x: tileX, y: tileY)
let physicsBodyCenter = CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0)
let physicsBody = SKPhysicsBody(rectangleOf: tileSize, center: physicsBodyCenter)
physicsBody.isDynamic = false
tileNode.physicsBody = physicsBody
if tileDefinition?.name == Nodes.wall.rawValue {
physicsBody.categoryBitMask = CategoryMask.wall.rawValue
}
self.tileMap.addChild(tileNode)
}
}
}

Related

iOS Radar Chart with 3D Effect

I would like to replicate this graph in my app.
I tried to search online but I found only pods, specifically Charts.
I tried to customize it but I was unable to give it the 3d effect by assigning each "triangle" a different color shade.
How can I replicate it?
Uibezierpath or something else?
Just have fun.
class GraphView: UIView {
let cirleSegnaposto:CGFloat = 20.0
let labelSize:Double = 50
let spacingGraphLabel:Double = 0
let widthOfZero:Double = 30
let labels = ["Label 1", "Label 2", "Label 3", "Label 4", "Label 5"]
let firstColors:[UIColor] = [.darkGray, .black, .darkGray, .lightGray, .white]
let secondColors:[UIColor] = [.orange, .brown, .orange, .yellow, .red]
var values: [Int]? = nil
var secondValues: [Int]? = nil
override func draw(_ rect: CGRect) {
for i in 0 ..< 4 {
let cirleLayer = CAShapeLayer()
let delta = Double(15 * i) + labelSize
let path = UIBezierPath(ovalIn: CGRect(x: delta,
y: delta,
width: Double(rect.width) - delta * 2,
height: Double(rect.width) - delta * 2))
cirleLayer.path = path.cgPath
cirleLayer.lineWidth = 1
cirleLayer.strokeColor = UIColor.lightGray.cgColor
cirleLayer.fillColor = UIColor.clear.cgColor
self.layer.addSublayer(cirleLayer)
}
let radius:Double = Double(rect.width/2) - (labelSize - spacingGraphLabel)
let labelRadius:Double = Double(rect.width/2) + (spacingGraphLabel)
let origin = CGPoint(x: rect.width/2, y: rect.height/2)
for i in 0..<5 {
let cirleLayer = CAShapeLayer()
let angle:Double = Double(i)/5.0 * (2 * .pi)
let centerX = Double(origin.x) + radius * cos(angle)
let centerY = Double(origin.y) - radius * sin(angle)
let path = UIBezierPath(ovalIn: CGRect(x: CGFloat(centerX) - cirleSegnaposto/2,
y: CGFloat(centerY) - cirleSegnaposto/2,
width: cirleSegnaposto,
height: cirleSegnaposto))
cirleLayer.path = path.cgPath
cirleLayer.fillColor = UIColor.lightGray.cgColor
cirleLayer.lineWidth = 0.5
cirleLayer.strokeColor = UIColor.black.cgColor
self.layer.addSublayer(cirleLayer)
let label = UILabel(frame: .zero)
label.font = UIFont.systemFont(ofSize: 12)
label.text = labels[i]
label.frame.size = CGSize(width: labelSize, height: labelSize/2)
let labelCenterX = Double(origin.x) + labelRadius * cos(angle)
let labelCenterY = Double(origin.y) - labelRadius * sin(angle)
label.center = CGPoint(x: labelCenterX, y: labelCenterY)
label.transform = label.transform.rotated(by: .pi/2)
self.addSubview(label)
}
if let values = secondValues {
drawGraph(values: values, center: origin, maxValue: radius, colors: secondColors.map({$0.cgColor}))
}
if let values = values {
drawGraph(values: values, center: origin, maxValue: radius, colors: firstColors.map({$0.cgColor}))
}
}
func drawGraph(values: [Int], center: CGPoint, maxValue: Double, colors: [CGColor]) {
var points = [CGPoint]()
for i in 0 ..< values.count {
let radius = Double(values[i])/10.0 * (maxValue - widthOfZero) + widthOfZero
let angle:Double = Double(i)/5.0 * (2 * .pi)
let x = Double(center.x) + radius * cos(angle)
let y = Double(center.y) - radius * sin(angle)
let point = CGPoint(x: x, y: y)
points.append(point)
}
for (i, point) in points.enumerated() {
let secondPoint = point == points.last ? points[0] : points[i+1]
let path = UIBezierPath()
path.move(to: center)
path.addLine(to: point)
path.addLine(to: secondPoint)
path.close()
let layer = CAShapeLayer()
layer.path = path.cgPath
layer.fillColor = colors[i]
layer.lineWidth = 1
layer.lineJoin = .round
layer.strokeColor = UIColor.black.cgColor
self.layer.addSublayer(layer)
}
}
}

How to find the endangle for beizerpath for a curve drawn in swift?

I have drawn a beizer path with start & end angle which create a whole circle
circle = UIView(frame: CGRect(x: 100, y: 100, width: 200, height:200))
circle.layoutIfNeeded()
self.view.addSubview(circle)
let centerPoint = CGPoint (x: circle.bounds.width / 2, y: circle.bounds.width / 2)
let circleRadius : CGFloat = circle.bounds.width / 2 * 0.83
circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(-0.5 * M_PI), endAngle: CGFloat(1.5 * M_PI), clockwise: true)
After that i draw a Curve on that circle & does not fill it full to the circle.
progressCircle = CAShapeLayer ()
progressCircle.path = circlePath?.cgPath
progressCircle.strokeColor = UIColor.red.cgColor
progressCircle.fillColor = UIColor.clear.cgColor
progressCircle.lineWidth = 4.0
progressCircle.strokeStart = 0
progressCircle.strokeEnd = 0.7
circle.layer.addSublayer(progressCircle)
After that i want to add image at then end of curve so to do that i created another biezer path.But here problem is it should be ended as the curve end.So i am not able to find the end angle for the beizer path based on the endStroke of CASHAPELAYER. Please tell me how can i find the end angle of new beizer path based on the curve end point.
let centerPoint = CGPoint (x: circle.bounds.width / 2, y: circle.bounds.width / 2)
let circleRadius : CGFloat = circle.bounds.width / 2 * 0.83
let arcStartAngle: Double = 0.0
let rotationDiff = 360 - abs((0.0 - 270))
let startAngle: CGFloat = -1.57
let endAngle: CGFloat = CGFloat(Double.pi * 0.7)
let bpath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
class ViewController: UIViewController {
var array = [[CGFloat:CGFloat]]()
var midX : CGFloat = CGFloat()
var midY : CGFloat = CGFloat()
let radius : CGFloat = 100.0
override func viewDidLoad() {
super.viewDidLoad()
midX = self.view.frame.midX
midY = self.view.frame.midY
let path = UIBezierPath()
path.addArc(withCenter: CGPoint.init(x: midX, y: midY), radius: radius, startAngle: 0, endAngle:2 * .pi, clockwise: true)
let shape = CAShapeLayer()
shape.path = path.cgPath
shape.lineWidth = 5.0
shape.fillColor = UIColor.clear.cgColor
shape.strokeColor = UIColor.lightGray.cgColor
self.view.layer.addSublayer(shape)
var temp090x = [CGFloat:CGFloat]()
var temp90180x = [CGFloat:CGFloat]()
var temp180270x = [CGFloat:CGFloat]()
var temp270360x = [CGFloat:CGFloat]()
for i in 1...360 {
let path3 = UIBezierPath()
path3.move(to: CGPoint.init(x: midX, y: midY))
path3.addLine(to:CGPoint.init(x: midX + radius * sin(CGFloat(i)), y:midY + radius*cos(CGFloat(i))))
//design path in layer
if midX + radius * sin(CGFloat(i)) > midX && midY + radius*cos(CGFloat(i)) < midY {
temp090x[midX + radius * sin(CGFloat(i))] = midY + radius*cos(CGFloat(i))
}else if midX + radius * sin(CGFloat(i)) > midX && midY + radius*cos(CGFloat(i)) > midY {
temp90180x[midX + radius * sin(CGFloat(i))] = midY + radius*cos(CGFloat(i))
}else if midX + radius * sin(CGFloat(i)) < midX && midY + radius*cos(CGFloat(i)) > midY {
temp180270x[midX + radius * sin(CGFloat(i))] = midY + radius*cos(CGFloat(i))
}else if midX + radius * sin(CGFloat(i)) < midX && midY + radius*cos(CGFloat(i)) < midY {
temp270360x[midX + radius * sin(CGFloat(i))] = midY + radius*cos(CGFloat(i))
}
let shapeLayer3 = CAShapeLayer()
shapeLayer3.path = path3.cgPath
shapeLayer3.strokeColor = UIColor.clear.cgColor
shapeLayer3.lineWidth = 1.0
self.view.layer.addSublayer(shapeLayer3)
}
let tempSorted090x = BubbleAsceSort(array: temp090x)
let tempSorted90180x = BubbleDescSort(array: temp90180x)
let tempSorted180270x = BubbleDescSort(array: temp180270x)
let tempSorted270360x = BubbleAsceSort(array: temp270360x)
for item in tempSorted090x {
array.append(item)
}
for item in tempSorted90180x {
array.append(item)
}
for item in tempSorted180270x {
array.append(item)
}
for item in tempSorted270360x {
array.append(item)
}
let lbl = UILabel()
lbl.frame = CGRect.init(x: 0, y: 0, width: 30, height: 30)
lbl.backgroundColor = UIColor.black
lbl.layer.cornerRadius = 15
for i in 1...180 {
let xpos = Array(array[i-1].keys)[0]
let ypos = Array(array[i-1].values)[0]
let xpos1 = Array(array[i].keys)[0]
let ypos1 = Array(array[i].values)[0]
let path3 = UIBezierPath()
path3.move(to: CGPoint.init(x: xpos, y: ypos))
path3.addLine(to:CGPoint.init(x: xpos1, y:ypos1))
let shapeLayer3 = CAShapeLayer()
shapeLayer3.path = path3.cgPath
shapeLayer3.strokeColor = UIColor.red.cgColor
shapeLayer3.lineWidth = 5.0
self.view.layer.addSublayer(shapeLayer3)
}
lbl.center = CGPoint.init(x: Array(array[180].keys)[0], y: Array(array[180].values)[0])
self.view.addSubview(lbl)
}
func BubbleDescSort(array : [CGFloat:CGFloat]) -> [[CGFloat:CGFloat]] {
var sortedArray = Array(array.keys)
var sortedvalueArray = Array(array.values)
var sortedAboveIndex = sortedArray.count-1 // Assume all values are not in order
repeat {
var lastSwapIndex = 0
for i in 1...sortedAboveIndex{
if (sortedArray[i] as AnyObject) as! CGFloat > (sortedArray[i - 1] as AnyObject) as! CGFloat {
sortedArray.swapAt(i, i-1)
sortedvalueArray.swapAt(i, i-1)
lastSwapIndex = i
}
}
sortedAboveIndex = lastSwapIndex
} while (sortedAboveIndex != 0)
var index = 0
var arr = [[CGFloat:CGFloat]]()
for item in sortedArray {
var dic = [CGFloat:CGFloat]()
dic[item] = sortedvalueArray[index]
arr.append(dic)
index += 1
}
return arr
}
func BubbleAsceSort(array : [CGFloat:CGFloat]) -> [[CGFloat:CGFloat]] {
var sortedArray = Array(array.keys)
var sortedvalueArray = Array(array.values)
var sortedAboveIndex = sortedArray.count-1 // Assume all values are not in order
repeat {
var lastSwapIndex = 0
for i in 1...sortedAboveIndex{
if (sortedArray[i - 1] as AnyObject) as! CGFloat > (sortedArray[i] as AnyObject) as! CGFloat {
sortedArray.swapAt(i, i-1)
sortedvalueArray.swapAt(i, i-1)
lastSwapIndex = i
}
}
sortedAboveIndex = lastSwapIndex
} while (sortedAboveIndex != 0)
var index = 0
var arr = [[CGFloat:CGFloat]]()
for item in sortedArray {
var dic = [CGFloat:CGFloat]()
dic[item] = sortedvalueArray[index]
arr.append(dic)
index += 1
}
return arr
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In this code you need to Set Progress Percentage based on the 360 degree. in the upper limit of for loop and Round lbl Center point x and y pos. Here is need only 180 replace to your progress percentage

SpriteKit - Collision detection not working

i've got a problem with the collisions.
adding physicbody depending on tilemap:
guard let tilemap = childNode(withName: "LevelGround") as? SKTileMapNode else { return }
let tileSize = tilemap.tileSize
let halfWidth = CGFloat(tilemap.numberOfColumns) / 2.0 * tileSize.width
let halfHeight = CGFloat(tilemap.numberOfRows) / 2.0 * tileSize.height
for row in 0..<tilemap.numberOfRows {
for col in 0..<tilemap.numberOfColumns {
if tilemap.tileDefinition(atColumn: col, row: row) != nil {
let x = CGFloat(col) * tileSize.width - halfWidth
let y = CGFloat(row) * tileSize.height - halfHeight
let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)
let tileNode = SKShapeNode(rect: rect)
tileNode.position = CGPoint(x: x, y: y)
tileNode.physicsBody = SKPhysicsBody(rectangleOf: tileSize, center: CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0))
tileNode.physicsBody?.isDynamic = false
tileNode.physicsBody?.affectedByGravity = false
tileNode.physicsBody?.collisionBitMask = Physics().player | Physics().bullet
tileNode.physicsBody?.categoryBitMask = Physics().ground
tileNode.physicsBody?.contactTestBitMask = Physics().ground | Physics().player
tileNode.physicsBody?.friction = 0.1
tileNode.physicsBody?.restitution = 0
tileNode.strokeColor = .white
tileNode.name = "Ground"
self.world.addChild(tileNode)
}
}
}
Let the bullet spawn on tapping the action2 button:
if self.ctrlAction2.contains(touch.location(in: cam)) {
let bullet = SKShapeNode(ellipseOf: CGSize(width: 5, height: 4))
bullet.fillColor = .orange
bullet.physicsBody = SKPhysicsBody(edgeLoopFrom: bullet.path!)
bullet.physicsBody?.affectedByGravity = true
bullet.physicsBody?.categoryBitMask = Physics().bullet
bullet.physicsBody?.collisionBitMask = Physics().enemy | Physics().ground
bullet.physicsBody?.contactTestBitMask = Physics().bullet | Physics().enemy | Physics().ground
bullet.position.y = charakter.position.y + charakter.size.height / 7
bullet.physicsBody?.isDynamic = true
bullet.name = "bullet"
if facing == "right" {
bullet.position.x = charakter.position.x + charakter.size.width / 1.9
self.world.addChild(bullet)
bullet.run(SKAction.moveBy(x: 2000, y: 0, duration: 4))
} else if facing == "left" {
bullet.position.x = charakter.position.x - charakter.size.width / 1.9
self.world.addChild(bullet)
bullet.run(SKAction.moveBy(x: -2000, y: 0, duration: 4))
}
}
The bullet collides with enemies, the enemies collides with the player and the player collides with the ground - everything works, except the collision between bullet and ground.
Thank you for your help in advance
Pascal

Swift UITableView zigzag borders

I am trying to achieve this aspect for an UITableView : https://www.dropbox.com/s/bcp86myyjgek1kt/Screenshot%202016-11-04%2014.04.14.png?dl=0 and I am stuck.
I followed Atul Manwar's answer :
func applyZigZagEffect(givenView: UIView) {
let width = givenView.frame.size.width
let height = givenView.frame.size.height
let givenFrame = givenView.frame
let zigZagWidth = CGFloat(7)
let zigZagHeight = CGFloat(5)
let yInitial = height-zigZagHeight
var zigZagPath = UIBezierPath()
zigZagPath.moveToPoint(CGPointMake(0, 0))
zigZagPath.addLineToPoint(CGPointMake(0, yInitial))
var slope = -1
var x = CGFloat(0)
var i = 0
while x < width {
x = zigZagWidth * CGFloat(i)
let p = zigZagHeight * CGFloat(slope)
let y = yInitial + p
let point = CGPointMake(x, y)
zigZagPath.addLineToPoint(point)
slope = slope*(-1)
i++
}
zigZagPath.addLineToPoint(CGPointMake(width, 0))
var shapeLayer = CAShapeLayer()
shapeLayer.path = zigZagPath.CGPath
givenView.layer.mask = shapeLayer
}
The result is not the one I am looking for, because I only obtain the bottom border: Achieved using Atul's answer and I have no clue how to change it for both borders ( bottom and top ).
Tried with images, but is not scaled correctly, and I find this solution better, but I am not able to produce the effect for top border.
Thanks!
I had been working on your question and this are my results, use this code,
func applyZigZagEffect(givenView: UIView) {
let width = givenView.frame.size.width
let height = givenView.frame.size.height
let givenFrame = givenView.frame
let zigZagWidth = CGFloat(7)
let zigZagHeight = CGFloat(5)
var yInitial = height-zigZagHeight
var zigZagPath = UIBezierPath(rect: givenFrame)
zigZagPath.move(to: CGPoint(x:0, y:0))
zigZagPath.addLine(to: CGPoint(x:0, y:yInitial))
var slope = -1
var x = CGFloat(0)
var i = 0
while x < width {
x = zigZagWidth * CGFloat(i)
let p = zigZagHeight * CGFloat(slope)
let y = yInitial + p
let point = CGPoint(x: x, y: y)
zigZagPath.addLine(to: point)
slope = slope*(-1)
i += 1
}
zigZagPath.addLine(to: CGPoint(x:width,y: 0))
yInitial = 0 + zigZagHeight
x = CGFloat(width)
i = 0
while x > 0 {
x = width - (zigZagWidth * CGFloat(i))
let p = zigZagHeight * CGFloat(slope)
let y = yInitial + p
let point = CGPoint(x: x, y: y)
zigZagPath.addLine(to: point)
slope = slope*(-1)
i += 1
}
var shapeLayer = CAShapeLayer()
shapeLayer.path = zigZagPath.cgPath
givenView.layer.mask = shapeLayer
}
I hope this helps you, this code works and was tested
Edited
With this method you can get curved zigzag instead of lines
class func pathSemiCirclesPathForView(givenView: UIView, ciclesRadius:CGFloat = 4, circlesDistance : CGFloat = 3, top:Bool = true, bottom:Bool = true ) ->UIBezierPath
{
let width = givenView.frame.size.width
let height = givenView.frame.size.height
let semiCircleWidth = CGFloat(ciclesRadius*2)
let semiCirclesPath = UIBezierPath()
semiCirclesPath.move(to: CGPoint(x:0, y:0))
if(bottom) {
var x = CGFloat(0)
var i = 0
while x < width {
x = (semiCircleWidth) * CGFloat(i) + (circlesDistance * CGFloat(i))
let pivotPoint = CGPoint(x: x + semiCircleWidth/2, y: height)
semiCirclesPath.addArc(withCenter: pivotPoint, radius: ciclesRadius, startAngle: -180 * .pi / 180.0, endAngle: 0 * .pi / 180.0, clockwise: true)
semiCirclesPath.addLine(to: CGPoint(x: semiCirclesPath.currentPoint.x + circlesDistance, y: height))
i += 1
}
}
else {
semiCirclesPath.addLine(to: CGPoint(x: 0, y: height))
semiCirclesPath.addLine(to: CGPoint(x: width, y: height))
}
semiCirclesPath.addLine(to: CGPoint(x:width,y: 0))
if(top) {
var x = CGFloat(width)
var i = 0
while x > 0 {
x = width - (semiCircleWidth) * CGFloat(i) - (circlesDistance * CGFloat(i))
let pivotPoint = CGPoint(x: x - semiCircleWidth/2, y: 0)
semiCirclesPath.addArc(withCenter: pivotPoint, radius: ciclesRadius, startAngle: 0 * .pi / 180.0, endAngle: -180 * .pi / 180.0, clockwise: true)
semiCirclesPath.addLine(to: CGPoint(x: semiCirclesPath.currentPoint.x - circlesDistance, y: 0))
i += 1
}
}
semiCirclesPath.close()
return semiCirclesPath
}
RESULTS
In case somebody else will need it somewhere, I am posting here the result I was looking for with #Reinier Melian 's help. I will soon post another version, customising only the first and last cell of a UITableView.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var customView: UIView!
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.applyZigZagEffect(givenView: customView)
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func pathZigZagForView(givenView: UIView) ->UIBezierPath
{
let width = givenView.frame.size.width
let height = givenView.frame.size.height
let givenFrame = givenView.frame
let zigZagWidth = CGFloat(7)
let zigZagHeight = CGFloat(5)
var yInitial = height-zigZagHeight
let zigZagPath = UIBezierPath(rect: givenFrame.insetBy(dx: 5, dy: 5))
zigZagPath.move(to: CGPoint(x:0, y:0))
zigZagPath.addLine(to: CGPoint(x:0, y:yInitial))
var slope = -1
var x = CGFloat(0)
var i = 0
while x < width
{
x = zigZagWidth * CGFloat(i)
let p = zigZagHeight * CGFloat(slope) - 5
let y = yInitial + p
let point = CGPoint(x: x, y: y)
zigZagPath.addLine(to: point)
slope = slope*(-1)
i += 1
}
zigZagPath.addLine(to: CGPoint(x:width,y: 0))
yInitial = 0 + zigZagHeight
x = CGFloat(width)
i = 0
while x > 0
{
x = width - (zigZagWidth * CGFloat(i))
let p = zigZagHeight * CGFloat(slope) + 5
let y = yInitial + p
let point = CGPoint(x: x, y: y)
zigZagPath.addLine(to: point)
slope = slope*(-1)
i += 1
}
zigZagPath.close()
return zigZagPath
}
func applyZigZagEffect(givenView: UIView)
{
let shapeLayer = CAShapeLayer(layer: givenView.layer)
givenView.backgroundColor = UIColor.clear
shapeLayer.path = self.pathZigZagForView(givenView: givenView).cgPath
shapeLayer.frame = givenView.bounds
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.masksToBounds = true
shapeLayer.shadowOpacity = 1
shapeLayer.shadowColor = UIColor.black.cgColor
shapeLayer.shadowOffset = CGSize(width: 0, height: 0)
shapeLayer.shadowRadius = 3
givenView.layer.addSublayer(shapeLayer)
}
}
Result
Here is the code in a Swift 3 playground if anyone wants to check out the solution in action:
https://gist.github.com/thexande/9f93b3c899af63108415936bf13a43da

how to make a node stack on top of another node and follow the object?

I'm trying to create a game in which i have a object 1 rotatiing in a circle and another object appears and places itself ontop of object 1. currently the object just rotates around object 1 without stacking ontop of it. how do i get the object to stack itself on top and then follow it's orbit? here's my code now.
let player = SKSpriteNode(imageNamed: "Light")
override func didMoveToView(view: SKView) {
// 2
backgroundColor = SKColor.whiteColor()
// 3
player.position = CGPoint(x: size.width * 0.5, y: size.height * 0.5)
// 4
player.size = CGSize(width:70, height:60 )
addChild(player)
let dx = player.position.x - self.frame.width / 2
let dy = player.position.y - self.frame.height / 2
let rad = atan2(dy, dx)
circle = UIBezierPath(arcCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: 120, startAngle: rad, endAngle: rad + CGFloat(M_PI * 4), clockwise: true)
let follow = SKAction.followPath(circle.CGPath, asOffset: false, orientToPath: true, speed: 100)
player.runAction(SKAction.repeatActionForever(follow))
}
func addMonster() {
let monster = SKSpriteNode(imageNamed: "plate")
// Determine where to spawn the monster along the Y axis
let actualY = random(min: monster.size.height/1, max: size.height - monster.size.height/1)
// Position the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
monster.position = CGPoint(x: size.width * 0.5 + monster.size.width/2, y: actualY)
// Add the monster to the scene
addChild(monster)
// Determine speed of the monster
let actualDuration = random(min: CGFloat(2.0), max: CGFloat(3.0))
// Create the actions
let actionMove = SKAction.moveTo(CGPoint(x: -monster.size.width/2, y: actualY), duration: NSTimeInterval(actualDuration))
let follow = SKAction.followPath(circle.CGPath, asOffset: false, orientToPath:true, speed: 100)
monster.runAction(SKAction.repeatActionForever(follow))
}
Your verbal description appears to have something like the following in mind (you can copy and paste this into an iOS playground):
import UIKit
import SpriteKit
import XCPlayground
let scene = SKScene(size: CGSize(width: 400, height: 400))
let view = SKView(frame: CGRect(origin: .zero, size: scene.size))
view.presentScene(scene)
XCPlaygroundPage.currentPage.liveView = view
scene.backgroundColor = .darkGrayColor()
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let player = SKShapeNode(rectOfSize: CGSize(width: 70, height: 60), cornerRadius: 5)
let radius: CGFloat = 120
let circle = CGPathCreateWithEllipseInRect(CGRect(x: -radius, y: -radius, width: radius * 2, height: radius * 2), nil)
let followCircle = SKAction.followPath(circle, asOffset: false, orientToPath: true, speed: 100)
player.runAction(.repeatActionForever(followCircle))
let monsterSize = CGSize(width: 35, height: 30)
let monster = SKShapeNode(rectOfSize: monsterSize, cornerRadius: 4)
monster.fillColor = .redColor()
monster.runAction(.repeatActionForever(followCircle))
scene.addChild(player)
scene.addChild(monster)
Where you make use of a random(min:max:) function that is probably implemented along the lines of:
public func random(min min: CGFloat, max: CGFloat) -> CGFloat {
return min + (CGFloat(arc4random()) / CGFloat(UInt32.max)) * (max - min)
}
But you also have the code that seems to be saying:
let y = random(
min: scene.size.height / -2 + monsterSize.height / 2,
max: scene.size.height / 2 - monsterSize.height / 2
)
let xFrom = scene.size.width / 2
let xTo = scene.size.width / -2
monster.position = CGPoint(x: xFrom, y: y)
monster.runAction(
.moveToX(xTo, duration: NSTimeInterval(random(min: 2, max: 3)))
)
But this is unused. Instead the monster is set to follow the same circle path as the player. And so I am not sure really what exactly you are trying to achieve. (By the way, the simplest way of getting the monster "stack on top" of the player would be to make it the child node of the player...)
Hope some of this guesswork helps you in some way (to refine your question and code sample if nothing else).

Resources