Does anyone know how to bring progressive darkness on a SKScene using SpriteKit?
I tried using a SKLightNode, which works great to have "full" darkness in the scene and some light sources.
I also tried to have a Node in front of everything else where I adjust the alpha to get darker and darker. which works great if there is on light source
But the 2 solutions doesn't work together.
In my example, the blue bar on the buttom control the alpha of the "darkness" node (left = 0 so fully transparent, right = 1 so fully dark), and the white part on the buttom left is to swift on and off the light.
My goal would be to use the bar on the buttom to go from light on to light off, with a gradual transition.
class GameScene: SKScene {
override func didMove(to view: SKView) {
let background = SKSpriteNode(color: .lightGray, size: view.frame.size) //imageNamed: "background")
background.zPosition = 1
background.position = CGPoint(x: view.frame.midX, y: view.frame.midY)
background.lightingBitMask = 0b0001
addChild(background)
let character = SKSpriteNode(color: .blue, size: CGSize(width: 100, height: 100))//imageNamed: "character")
character.zPosition = 2
character.position = CGPoint(x: view.frame.midX, y: view.frame.midY)
character.lightingBitMask = 0b0001
character.shadowCastBitMask = 0b0001
addChild(character)
let lightNode = SKLightNode()
lightNode.name = "SKLightNode"
lightNode.position = CGPoint(x: view.frame.midX, y: view.frame.midY)
lightNode.categoryBitMask = 0b0001
lightNode.lightColor = .white
//lightNode.ambientColor = .white
lightNode.isEnabled = false
addChild(lightNode)
// Control
let elementSize: CGFloat = 40
let lightToggleSize = CGSize(width: elementSize, height: elementSize)
let lightToggle = SKSpriteNode(color: .white, size: lightToggleSize)
lightToggle.name = "LightToggle"
lightToggle.zPosition = 10000
lightToggle.position = CGPoint(x: view.frame.width - (elementSize / 2), y: elementSize / 2)
addChild(lightToggle)
let sideBarSize = CGSize(width: view.frame.width - elementSize, height: elementSize)
let sideBar = SKSpriteNode(color: .blue, size: sideBarSize)
sideBar.name = "SideBar"
sideBar.position = CGPoint(x:(view.frame.width - elementSize) / 2, y: elementSize / 2)
sideBar.zPosition = lightToggle.zPosition
addChild(sideBar)
let darknessNodeSize = CGSize(width: view.frame.width , height: view.frame.height)
let darknessNode = SKSpriteNode(color: .black, size: darknessNodeSize)
darknessNode.name = "DarknessNode"
darknessNode.position = CGPoint(x: view.frame.width / 2, y: view.frame.height / 2)
darknessNode.alpha = 0
darknessNode.zPosition = lightToggle.zPosition - 1
addChild(darknessNode)
}
func handleTouches(_ point: CGPoint) {
let darknessNode = childNode(withName: "DarknessNode") as! SKSpriteNode
let lightNode = childNode(withName: "SKLightNode") as! SKLightNode
let sideBar = childNode(withName: "SideBar")!
let lightToggle = childNode(withName: "LightToggle") as! SKSpriteNode
if lightToggle.contains(point) {
lightNode.isEnabled = !lightNode.isEnabled
lightNode.position = CGPoint(x: lightNode.position.x + 1, y: lightNode.position.y)
} else if sideBar.contains(point) {
darknessNode.alpha = point.x / sideBar.frame.width
} else {
lightNode.position = point
}
}
override func touchesBegan(_ touches: Set<UITouch>,
with event: UIEvent?) {}
override func touchesMoved(_ touches: Set<UITouch>,
with event: UIEvent?) {
handleTouches(touches.first?.location(in: self))
}
override func touchesEnded(_ touches: Set<UITouch>,
with event: UIEvent?) {
handleTouches(touches.first?.location(in: self))
}
}
If anyone is interested by this topic, here is the solution I found: change the alpha of the source.
It works well on a Playground project but on my real one the local light is having a weird flat shape (I posted a question here
This is what it looks like.
And the solution is bellow.
There is a green background and blue box on the scene.
There are 2 white boxes that are switches for the Background light (that simulate the sunlight, which is off screen) and the "local" light (that simulate a light source)
The white bar is to control the alpha of the background light (on the left the alpha is 0 and there is no light and the right the alpha is 1 and the light is full on). In the same time as I change the alpha of the background light, I also change the alpha of the local light for better look.
import Foundation
import SpriteKit
class GameScene: SKScene {
var background: SKSpriteNode!
var object: SKSpriteNode!
var backgroundLight: SKLightNode!
var localLight: SKLightNode!
var backgroundLightSwitch: SKSpriteNode!
var backgroundLightBar: SKSpriteNode!
var localLightSwitch: SKSpriteNode!
var isBackgroundLightOn = false
var isLocalLightOn = false
var selectedElement: SKSpriteNode? = nil
class func newGameScene() -> GameScene {
let scene = GameScene()
scene.scaleMode = .resizeFill
return scene
}
override func didMove(to view: SKView) {
super.didMove(to: view)
let objectSize = CGSize(width: 100, height: 100)
background = createSpriteNode(color: .green, x: view.frame.width / 2, y: view.frame.height / 2, width: view.frame.width, height: view.frame.height, lightMask: 1)
object = createSpriteNode(color: .blue, x: background.position.x, y: background.position.y, width: objectSize.width, height: objectSize.height, lightMask: 1)
backgroundLightSwitch = createSpriteNode(color: .white, x: view.frame.width / 4, y: view.frame.height / 4, width: objectSize.width / 2, height: objectSize.height / 2, lightMask: 2)
backgroundLightBar = createSpriteNode(color: .white, x:view.frame.width * 2 / 3, y: backgroundLightSwitch.position.y, width: view.frame.width / 2, height: objectSize.height / 2, lightMask: 2)
localLightSwitch = createSpriteNode(color: .white, x: backgroundLightSwitch.position.x, y: view.frame.height / 8, width: objectSize.width / 2, height: objectSize.height / 2, lightMask: 2)
let color = NSColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5)
backgroundLight = createLightNode(color: nil, ambiantColor: color, x: -view.frame.width * 2, y: -view.frame.height * 2)
localLight = createLightNode(color: color, ambiantColor: nil, x:view.frame.width * 2 / 3, y: view.frame.height * 2 / 3)
}
func createSpriteNode(color: NSColor, x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat, lightMask: UInt32) -> SKSpriteNode {
let nodePosition = CGPoint(x: x, y: y)
let nodeSize = CGSize(width: width, height: height)
let node = SKSpriteNode(color: color, size: nodeSize)
node.zPosition = 1
node.position = nodePosition
node.lightingBitMask = lightMask
addChild(node)
return node
}
func createLightNode(color: NSColor?, ambiantColor: NSColor? , x: CGFloat, y: CGFloat) -> SKLightNode {
let light = SKLightNode()
light.falloff = 1.5
light.position = CGPoint(x:x, y: y)
light.isEnabled = false
light.categoryBitMask = 1
if color != nil {
light.lightColor = color!
}
if ambiantColor != nil {
light.ambientColor = ambiantColor!
}
addChild(light)
return light
}
func getSelectElement(location: CGPoint) -> SKSpriteNode? {
let elements: [SKSpriteNode] = [backgroundLightSwitch, localLightSwitch, backgroundLightBar]
for element in elements {
if element.contains(location) {
return element
}
}
return nil
}
func switchLight(_ light: SKLightNode, selected: Bool) -> Bool {
light.isEnabled = selected
light.position.x += selected ? 1 : -1
return selected
}
func getAlpha(node: SKSpriteNode, location: CGPoint) -> CGFloat {
if location.x < node.frame.minX {
return 0
} else if location.x > node.frame.maxX {
return 1
} else {
return (location.x - node.frame.minX) / node.frame.width
}
}
func changeAlpha(_ alpha: CGFloat) {
backgroundLight.ambientColor = NSColor(red: 1.0, green: 1.0, blue: 1.0, alpha: alpha)
localLight.lightColor = NSColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1 - alpha)
backgroundLight.position.x -= 1
}
override func mouseDown(with event: NSEvent) {
let location = event.location(in: self)
selectedElement = getSelectElement(location: location)
}
override func mouseDragged(with event: NSEvent) {
if selectedElement == backgroundLightBar {
let location = event.location(in: self)
let newAlpha = getAlpha(node: backgroundLightBar, location: location)
changeAlpha(newAlpha)
}
}
override func mouseUp(with event: NSEvent) {
let location = event.location(in: self)
let newSelectedElement = getSelectElement(location: location)
if selectedElement == newSelectedElement {
if selectedElement == backgroundLightSwitch {
isBackgroundLightOn = switchLight(backgroundLight, selected: !isBackgroundLightOn)
} else if selectedElement == localLightSwitch {
isLocalLightOn = switchLight(localLight, selected: !isLocalLightOn)
}
}
}
}
Related
I'm drawing lines with UIBezierPath in draw(_ rect: CGRect) method previously i did tried that with CAShapeLayer but i wasn't able to select particular path and move it so i'm trying this, but after drawing i'm not able to access that BezierPath from view's subviews or it's sublayers and not directly from UIView. How can i access that bezierpath drawn in drawRect method so i can change it's position according to touches.
private var bezierPaths: [MyBezierPath] = [MyBezierPath]()
override func draw(_ rect: CGRect) {
super.draw(rect)
UIColor.orange.set()
for path in bezierPaths {
path.lineWidth = 4
path.lineCapStyle = .round
path.lineJoinStyle = .round
path.stroke()
}
}
func drawingPath(_ path: [MyBezierPath]) {
bezierPaths = path
}
like creating CAShapeLayer and setting layer.path = BezierPath like this :
let shapeLayer = CAShapeLayer()
shapeLayer.lineWidth = 1
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = .round
shapeLayer.lineJoin = .round
shapeLayer.lineDashPattern = [10, 10]
shapeLayer.name = "ShapeLayer"
self.canvas.layer.addSublayer(shapeLayer)
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if canvas.layer.sublayers != nil && canvas.layer.sublayers?.last?.name == "ShapeLayer" {
guard let layer = canvas.layer.sublayers?.last as? CAShapeLayer else { return }
layer.path = path.cgPath
}
}
like this i can access layer.path but how can i do it for path drawn in draw(Rect:) method ?
Depending on what you're actually trying to do, overriding draw(_:) may not be the best approach.
For example, if you want to animate the drawn paths, it will be much easier if you are using CAShapeLayer as sublayers, or using subviews.
Also, shape layers are highly optimized ... if you write your own draw / animate functions you risk ending up with lesser-optimized code.
However, whichever approach you take, to "find the path" you want to use contains(_ point: CGPoint)
For example, if you have an array of UIBezierPath, with all paths relative to the top-left (0,0) of the view, on touch you could do:
// find a path that contains the touch point
if let touchedPath = bezierPaths.first(where: {$0.contains(point)}) {
// do something with that path
}
If the paths are not relative to the top-left of the view - for example, if the path is relative to the layer position, or is part of a subview - you'd need to convert the touch point.
Here's a quick example that looks like this - on load, and then after dragging a few shapes around:
We'll start with a simple "path" struct, which contains the bezier path and the color to use. We could add various other properties, such as line width, dash pattern, etc:
struct MyPath {
var color: UIColor = .white
var path: UIBezierPath = UIBezierPath()
}
then we'll use this UIView subclass that will handle the drawing, as well as touches began/moved/ended:
class BezDrawView: UIView {
var myPaths: [MyPath] = [] {
didSet {
setNeedsDisplay()
}
}
// used to track path to move
var activePath: MyPath?
var startPoint: CGPoint = .zero
override func draw(_ rect: CGRect) {
super.draw(rect)
if myPaths.count > 0 {
myPaths.forEach { p in
p.color.set()
p.path.stroke()
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let t = touches.first else { return }
let point = t.location(in: self)
// find a path that contains the touch point
if let touchedPath = myPaths.first(where: {$0.path.contains(point)}) {
self.activePath = touchedPath
self.startPoint = point
return
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let ap = activePath, let t = touches.first else { return }
let point = t.location(in: self)
// move the path by the distance the touch moved
let tr = CGAffineTransform(translationX: point.x - startPoint.x, y: point.y - startPoint.y)
ap.path.apply(tr)
startPoint = point
// this triggers draw(_:)
setNeedsDisplay()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// done dragging
activePath = nil
}
}
and an example controller which defines some sample shapes (paths):
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
let testBezDrawView = BezDrawView()
testBezDrawView.backgroundColor = UIColor(white: 0.1, alpha: 1.0)
testBezDrawView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(testBezDrawView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain bez draw view to all 4 sides with 20-points "padding"
testBezDrawView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
testBezDrawView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
testBezDrawView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
testBezDrawView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
// add some sample path shapes and colors
let colors: [UIColor] = [
.systemRed, .systemGreen, .systemBlue,
.cyan, .magenta, .yellow, .orange, .green,
]
let rects: [CGRect] = [
CGRect(x: 20, y: 20, width: 60, height: 60),
CGRect(x: 180, y: 20, width: 40, height: 60),
CGRect(x: 20, y: 120, width: 60, height: 100),
CGRect(x: 200, y: 140, width: 50, height: 90),
CGRect(x: 90, y: 220, width: 100, height: 60),
CGRect(x: 220, y: 260, width: 80, height: 160),
CGRect(x: 50, y: 360, width: 200, height: 100),
CGRect(x: 150, y: 480, width: 120, height: 80),
]
var somePaths: [MyPath] = []
var i: Int = 0
for (c, r) in zip(colors, rects) {
var b = UIBezierPath()
switch i % 4 {
case 1: // oval
b = UIBezierPath(ovalIn: r)
case 2: // triangle shape
b = UIBezierPath()
b.move(to: CGPoint(x: r.minX, y: r.maxY))
b.addLine(to: CGPoint(x: r.midX, y: r.minY))
b.addLine(to: CGPoint(x: r.maxX, y: r.maxY))
b.close()
case 3: // diamond
b = UIBezierPath()
b.move(to: CGPoint(x: r.minX, y: r.midY))
b.addLine(to: CGPoint(x: r.midX, y: r.minY))
b.addLine(to: CGPoint(x: r.maxX, y: r.midY))
b.addLine(to: CGPoint(x: r.midX, y: r.maxY))
b.close()
default: // rect
b = UIBezierPath(rect: r)
}
b.lineWidth = 4
b.lineCapStyle = .round
b.lineJoinStyle = .round
b.setLineDash([5, 10], count: 2, phase: 0)
let p = MyPath(color: c, path: b)
somePaths.append(p)
i += 1
}
testBezDrawView.myPaths = somePaths
}
}
Here's a video of it in use (too big to convert to gif and embed here):
https://imgur.com/a/BmaHkKM
Edit in response to comment...
That needs very few changes to make it work with shape layers instead of draw(_:).
We can use the same Translation Transform to "move" the path, then update the .path property of that path's associated layer:
let tr = CGAffineTransform(translationX: point.x - startPoint.x, y: point.y - startPoint.y)
active.path.apply(tr)
activeLayer.path = activeParh.path.cgPath
I strongly, strongly recommend that you try to convert that sample code to shape layers on your own - it would be a good learning exercise.
But, if you run into trouble, here's a modified version...
First, we're going to use the. array index of the MyPath object to match with the sublayer index, so we need to make our struct Equatable:
struct MyPath: Equatable {
var color: UIColor = .white
var path: UIBezierPath = UIBezierPath()
}
Then some minor changes to BezDrawView -- which we'll name BezLayerView:
class BezLayerView: UIView {
var myPaths: [MyPath] = [] {
didSet {
// remove any existing layers
if let subs = layer.sublayers {
subs.forEach { lay in
lay.removeFromSuperlayer()
}
}
// create layers for paths
myPaths.forEach { p in
let lay = CAShapeLayer()
lay.lineWidth = 4
lay.lineCap = .round
lay.lineJoin = .round
lay.lineDashPattern = [5, 10]
lay.fillColor = UIColor.clear.cgColor
lay.strokeColor = p.color.cgColor
lay.path = p.path.cgPath
layer.addSublayer(lay)
}
}
}
// used to track path to move
var activeLayer: CAShapeLayer?
var activePath: MyPath?
var startPoint: CGPoint = .zero
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let t = touches.first, let subs = layer.sublayers else { return }
let point = t.location(in: self)
// find a path that contains the touch point
if let touchedPath = myPaths.first(where: {$0.path.contains(point)}) {
// find the layer associated with that path
if let idx = myPaths.firstIndex(of: touchedPath) {
if let lay = subs[idx] as? CAShapeLayer {
self.activePath = touchedPath
self.activeLayer = lay
self.startPoint = point
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let lay = activeLayer, let ap = activePath, let t = touches.first else { return }
let point = t.location(in: self)
// move the path by the distance the touch moved
let tr = CGAffineTransform(translationX: point.x - startPoint.x, y: point.y - startPoint.y)
ap.path.apply(tr)
lay.path = ap.path.cgPath
startPoint = point
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// done dragging
activeLayer = nil
activePath = nil
}
}
and an almost identical version of the controller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
let testBezLayerView = BezLayerView()
testBezLayerView.backgroundColor = UIColor(white: 0.1, alpha: 1.0)
testBezLayerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(testBezLayerView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain bez draw view to all 4 sides with 20-points "padding"
testBezLayerView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
testBezLayerView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
testBezLayerView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
testBezLayerView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
// add some sample path shapes and colors
let colors: [UIColor] = [
.systemRed, .systemGreen, .systemBlue,
.cyan, .magenta, .yellow, .orange, .green,
]
let rects: [CGRect] = [
CGRect(x: 20, y: 20, width: 60, height: 60),
CGRect(x: 180, y: 20, width: 40, height: 60),
CGRect(x: 20, y: 120, width: 60, height: 100),
CGRect(x: 200, y: 140, width: 50, height: 90),
CGRect(x: 90, y: 220, width: 100, height: 60),
CGRect(x: 220, y: 260, width: 80, height: 160),
CGRect(x: 50, y: 360, width: 200, height: 100),
CGRect(x: 150, y: 480, width: 120, height: 80),
]
var somePaths: [MyPath] = []
var i: Int = 0
for (c, r) in zip(colors, rects) {
var b = UIBezierPath()
switch i % 4 {
case 1: // oval
b = UIBezierPath(ovalIn: r)
case 2: // triangle shape
b = UIBezierPath()
b.move(to: CGPoint(x: r.minX, y: r.maxY))
b.addLine(to: CGPoint(x: r.midX, y: r.minY))
b.addLine(to: CGPoint(x: r.maxX, y: r.maxY))
b.close()
case 3: // diamond
b = UIBezierPath()
b.move(to: CGPoint(x: r.minX, y: r.midY))
b.addLine(to: CGPoint(x: r.midX, y: r.minY))
b.addLine(to: CGPoint(x: r.maxX, y: r.midY))
b.addLine(to: CGPoint(x: r.midX, y: r.maxY))
b.close()
default: // rect
b = UIBezierPath(rect: r)
}
let p = MyPath(color: c, path: b)
somePaths.append(p)
i += 1
}
testBezLayerView.myPaths = somePaths
}
}
The output and functionality should be indistinguishable from the BezDrawView implementation.
I’m currently working on a matching game and when the user touches the nodes (Fruit match cards) I want them to display different images when a user clicks the nodes (Fruit match cards).
This is my current code:
import Foundation
import SpriteKit
class EasyScreen: SKScene {
override func didMove(to view: SKView) {
var background = SKSpriteNode(imageNamed: "Easy Screen Background")
let timerText = SKLabelNode(fontNamed: "Arial")
timerText.fontSize = 40
timerText.fontColor = SKColor.white
timerText.position = CGPoint(x: 20, y: 400)
timerText.zPosition = 1
var counter:Int = 120
timerText.run(
SKAction.repeatForever(
SKAction.sequence(
[
SKAction.run {
counter -= 1
timerText.text = " Time: \(counter)"
print("\(counter)")
if counter <= 0 {
let newScene = TryAgainScreen(fileNamed: "Try Again Screen")
newScene?.scaleMode = .aspectFill
self.view?.presentScene(newScene)
}
},
SKAction.wait(forDuration: 1)
]
)
)
)
background.position = CGPoint(x: 0, y: 0)
background.size.width = self.size.width
background.size.height = self.size.height
background.anchorPoint = CGPoint(x: 0.5,y: 0.5)
let matchCardOne = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardTwo = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardThree = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardFour = SKSpriteNode(imageNamed: "Fruit Match Card")
matchCardOne.name = "FruitMatchCard1"
matchCardTwo.name = "FruitMatchCard2"
matchCardThree.name = "FruitMatchCard3"
matchCardFour.name = "FruitMatchCard4"
matchCardOne.size = CGSize(width: 150, height: 300)
matchCardTwo.size = CGSize(width: 150, height: 300)
matchCardThree.size = CGSize(width: 150, height: 300)
matchCardFour.size = CGSize(width: 150, height: 300)
matchCardOne.zPosition = 1
matchCardTwo.zPosition = 1
matchCardThree.zPosition = 1
matchCardFour.zPosition = 1
matchCardOne.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardTwo.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardThree.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardFour.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardOne.position = CGPoint(x: -125, y: 60)
matchCardTwo.position = CGPoint(x: -125, y: -260)
matchCardThree.position = CGPoint(x: 70, y: 60)
matchCardFour.position = CGPoint(x: 70 , y: -260)
addChild(background)
addChild(matchCardOne)
addChild(matchCardTwo)
addChild(matchCardThree)
addChild(matchCardFour)
addChild(timerText)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view?.isMultipleTouchEnabled = false
let touch = touches.first
let positionInScene = touch!.location(in: self)
let touchedCardOneNode = self.atPoint(positionInScene)
if let name = touchedCardOneNode.name {
if name == "FruitMatchCard1" {
let newTexture = SKTexture(imageNamed: "Apple")
FruitMatchCard1.init(texture: newTexture)
}
}
let touchTwo = touches.first
let positionInSceneTwo = touch!.location(in: self)
let touchedCardTwoNode = self.atPoint(positionInScene)
if let name = touchedCardTwoNode.name {
if name == "FruitMatchCard2" {
FruitMatchCard2.init(imageNamed: "Banana")
}
}
let touchThree = touches.first
let positionInSceneThree = touch!.location(in: self)
let touchedCardThreeNode = self.atPoint(positionInScene)
if let name = touchedCardThreeNode.name {
if name == "FruitMatchCard3" {
FruitMatchCard3.init(imageNamed: "Apple")
}
}
let touchFour = touches.first
let positionInSceneFour = touch!.location(in: self)
let touchedCardFourNode = self.atPoint(positionInScene)
if let name = touchedCardFourNode.name {
if name == "FruitMatchCard4" {
FruitMatchCard4.init(imageNamed: "Banana")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
I’m trying to change the textures of the nodes in this part of the code. Inside the “if name == “FruitMatchCard” { } “ part of the code. However, when I launch the Xcode simulator the node’s textures aren't changing.
Any advice on how I can do this? Thanks!
Your fundamental problem is that when someone taps a card, you are creating a new card with the new texture and not doing anything with it. Take the following code you have:
FruitMatchCard2.init(imageNamed: "Banana")
This code creates a match card, but you don't do anything with the card.
To change the texture of the card, you must get the sprite node of the tapped card and change its texture. I see in your code you have the following variable:
let touchedCardTwoNode = self.atPoint(positionInScene)
This variable has the card that was touched. Set its texture to the texture you want to use.
touchedCardTwoNode.texture = SKTexture(imageNamed: "Banana")
My SpriteKit is a bit rusty so I can't guarantee that example will compile, but it gives you an idea of what you need to do to change the texture when tapping a card.
How I can create Custom tab bar with 4 items and one FAB in center BUT Fab button show only when i press on index 3, for index 0 .. 3 fab.isHidden = true. I am don't need animation that button if shift, but in fill be plus), only show / hide. For creating custom tab bat I am use this guide https://medium.com/better-programming/draw-a-custom-ios-tabbar-shape-27d298a7f4fa, I am need create tab bar like this , I am try maenter image description hereny other way but can't solved problem.
Custom tab bar which needed
I am try this way
import UIKit
#IBDesignable
class CustomizedTabBar: UITabBar {
private var shapeLayer: CALayer?
private func addShape() {
let shapeLayer = CAShapeLayer()
shapeLayer.path = createPath()
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.lineWidth = 1.0
if let oldShapeLayer = self.shapeLayer {
self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
} else {
self.layer.insertSublayer(shapeLayer, at: 0)
}
self.shapeLayer = shapeLayer
}
override func draw(_ rect: CGRect) {
self.addShape()
}
func createPath() -> CGPath {
let height: CGFloat = 37.0
let path = UIBezierPath()
let centerWidth = self.frame.width / 2
path.move(to: CGPoint(x: 0, y: 0)) // start top left
path.addLine(to: CGPoint(x: (centerWidth - height * 2), y: 0)) // the beginning of the trough
//// // first curve down
// path.addCurve(to: CGPoint(x: centerWidth, y: height), controlPoint1: CGPoint(x: (centerWidth - 25), y: 0), controlPoint2: CGPoint(x: centerWidth - 35, y: height))
//
//// // second curve up
// path.addCurve(to: CGPoint(x: (centerWidth + height * 1.0), y: 0),
// controlPoint1: CGPoint(x: centerWidth + 25, y: height), controlPoint2: CGPoint(x: (centerWidth + 30), y: 0))
// cirle inside tab bar
path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: height, startAngle: CGFloat(180).degreesToRadians, endAngle: CGFloat(0).degreesToRadians, clockwise: false)
// complete the rect
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
path.addLine(to: CGPoint(x: 0, y: self.frame.height))
path.close()
return path.cgPath
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let buttonRadius: CGFloat = 35
return abs(self.center.x - point.x) > buttonRadius || abs(point.y) > buttonRadius
}
func createPathCircle() -> CGPath {
let radius: CGFloat = 37.0
let path = UIBezierPath()
let centerWidth = self.frame.width / 2
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: (centerWidth - radius * 2), y: 0))
path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: radius, startAngle: CGFloat(180).degreesToRadians, endAngle: CGFloat(0).degreesToRadians, clockwise: false)
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
path.addLine(to: CGPoint(x: 0, y: self.frame.height))
path.close()
return path.cgPath
}
}
extension CGFloat {
var degreesToRadians: CGFloat { return self * .pi / 180 }
var radiansToDegrees: CGFloat { return self * 180 / .pi }
}
And second way
import UIKit
class MainTabBar: UITabBar {
var checkState: Bool = true
public var middleButton = UIButton()
override func awakeFromNib() {
super.awakeFromNib()
guard let tabItems = items else { return }
tabItems[1].titlePositionAdjustment = UIOffset(horizontal: -10, vertical: 0)
tabItems[2].titlePositionAdjustment = UIOffset(horizontal: 10, vertical: 0)
setupMiddleButton()
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.isHidden {
return super.hitTest(point, with: event)
}
let from = point
let to = middleButton.center
return sqrt((from.x - to.x) * (from.x - to.x) + (from.y - to.y) * (from.y - to.y)) <= 39 ? middleButton : super.hitTest(point, with: event)
}
override func layoutSubviews() {
super.layoutSubviews()
middleButton.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: 5)
}
func setupMiddleButton() {
middleButton.frame.size = CGSize(width: 70, height: 70)
middleButton.layer.cornerRadius = 35
middleButton.layer.masksToBounds = true
middleButton.layer.borderWidth = 8
middleButton.layer.borderColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
middleButton.backgroundColor = #colorLiteral(red: 0.2447069331, green: 0.850134835, blue: 0.1531122658, alpha: 1)
middleButton.setImage(UIImage(named: "plus.png"), for: .normal)
middleButton.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: 0)
middleButton.addTarget(self, action: #selector(test), for: .touchUpInside)
addSubview(middleButton)
}
#objc func test() {
print("my name is jeff")
}
}
and I am try this way from article
https://equaleyes.com/blog/2017/09/04/the-common-raised-center-button-problems-in-tabbar/
but it's not working for me, also I am read too many info from WWW and don't get answer(
I am was created this tab bar, ours need few steps.
Create ViewController and Embed in "TabBarController", then need create TWO class first for "UITabBar" this class contain shape and what you want with "UITabBar", second class for "UITabBarController" for switch between ViewControllers inside we can add animation.... It's need because, my TabBar have 4 tabs and only on LAST tabs I am have Central FAB button with animation, and I am should animate position of my 2 and 3 ui tab bar element when button is appear.
Class for "UITabBar"
import UIKit
#IBDesignable
class CustomizedTabBar: UITabBar {
// MARK:- Variables -
#objc public var centerButtonActionHandler: ()-> () = {}
#IBInspectable public var centerButton: UIButton?
#IBInspectable public var centerButtonColor: UIColor?
#IBInspectable public var centerButtonHeight: CGFloat = 50.0
#IBInspectable public var padding: CGFloat = 5.0
#IBInspectable public var buttonImage: UIImage?
#IBInspectable public var buttonTitle: String?
#IBInspectable public var tabbarColor: UIColor = UIColor.lightGray
#IBInspectable public var unselectedItemColor: UIColor = .init(red: 0.58, green: 0.61, blue: 0.66, alpha: 1.0)
#IBInspectable public var selectedItemColor: UIColor = UIColor.black
public var arc: Bool = true {
didSet {
self.setNeedsDisplay()
}
}
private var shapeLayer: CALayer?
private func addShape() {
let shapeLayer = CAShapeLayer()
shapeLayer.path = createPath()
shapeLayer.strokeColor = UIColor.white.cgColor
shapeLayer.fillColor = #colorLiteral(red: 0.96, green: 0.96, blue: 0.96, alpha: 1)
shapeLayer.lineWidth = 1.0
if let oldShapeLayer = self.shapeLayer {
self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
} else {
self.layer.insertSublayer(shapeLayer, at: 0)
}
self.shapeLayer = shapeLayer
self.tintColor = centerButtonColor
self.unselectedItemTintColor = unselectedItemColor
self.tintColor = selectedItemColor
self.setupMiddleButton()
}
override func draw(_ rect: CGRect) {
self.addShape()
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard !clipsToBounds && !isHidden && alpha > 0 else { return nil }
for member in subviews.reversed() {
let subPoint = member.convert(point, from: self)
guard let result = member.hitTest(subPoint, with: event) else { continue }
return result
}
return nil
}
func createPath() -> CGPath {
let padding: CGFloat = 5.0
let centerButtonHeight: CGFloat = 53.0
let f = CGFloat(centerButtonHeight / 2.0) + padding
let h = frame.height
let w = frame.width
let halfW = frame.width/2.0
let r = CGFloat(18)
let path = UIBezierPath()
path.move(to: .zero)
if (!arc) {
path.addLine(to: CGPoint(x: halfW-f-(r/2.0), y: 0))
path.addQuadCurve(to: CGPoint(x: halfW-f, y: (r/2.0)), controlPoint: CGPoint(x: halfW-f, y: 0))
path.addArc(withCenter: CGPoint(x: halfW, y: (r/2.0)), radius: f, startAngle: .pi, endAngle: 0, clockwise: false)
path.addQuadCurve(to: CGPoint(x: halfW+f+(r/2.0), y: 0), controlPoint: CGPoint(x: halfW+f, y: 0))
}
path.addLine(to: CGPoint(x: w, y: 0))
path.addLine(to: CGPoint(x: w, y: h))
path.addLine(to: CGPoint(x: 0.0, y: h))
path.close()
return path.cgPath
}
private func setupMiddleButton() {
centerButton = UIButton(frame: CGRect(x: (self.bounds.width / 2)-(centerButtonHeight/2), y: -16, width: centerButtonHeight, height: centerButtonHeight))
centerButton!.setNeedsDisplay()
centerButton!.layer.cornerRadius = centerButton!.frame.size.width / 2.0
centerButton!.setTitle(buttonTitle, for: .normal)
centerButton!.setImage(UIImage(named: "plus"), for: .normal)
centerButton!.backgroundColor = .init(red: 0.07, green: 0.83, blue: 0.05, alpha: 1.0)
centerButton!.tintColor = UIColor.white
self.centerButton!.isHidden = true
if (!self.arc) {
DispatchQueue.main.async {
UIView.transition(with: self.centerButton!, duration: 1,
options: .transitionCrossDissolve,
animations: {
self.centerButton!.isHidden = false
})
}
}
//add to the tabbar and add click event
self.addSubview(centerButton!)
centerButton!.addTarget(self, action: #selector(self.centerButtonAction), for: .touchUpInside)
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let buttonRadius: CGFloat = 35
return abs(self.center.x - point.x) > buttonRadius || abs(point.y) > buttonRadius
}
func createPathCircle() -> CGPath {
let radius: CGFloat = 37.0
let path = UIBezierPath()
let centerWidth = self.frame.width / 2
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: (centerWidth - radius * 2), y: 0))
path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: radius, startAngle: CGFloat(180).degreesToRadians, endAngle: CGFloat(0).degreesToRadians, clockwise: false)
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
path.addLine(to: CGPoint(x: 0, y: self.frame.height))
path.close()
return path.cgPath
}
// Menu Button Touch Action
#objc func centerButtonAction(sender: UIButton) {
self.centerButtonActionHandler()
}
}
extension CGFloat {
var degreesToRadians: CGFloat { return self * .pi / 180 }
var radiansToDegrees: CGFloat { return self * 180 / .pi }
}
And class for UITabBarController
import UIKit
class MyTabBarController: UITabBarController {
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let myTabBar = tabBar as! CustomizedTabBar
if (myTabBar.items?[3] == item) {
myTabBar.arc = false
} else {
myTabBar.arc = true
}
}
}
I am new to swift and sprite kit and I am developing my first game. I am trying to add a shop to my game, however, I can not get it to work the way that I want to. I have a shop of skins that the user can choose from and what I want is for the user to touch on a skin. If it is locked, then the user may purchase it with in game currency and it automatically shows as their current skin during gameplay. If it isn't locked, then the user may press on it to make it their current skin during gameplay. This is my current code:
import SpriteKit
class SkinsScene: SKScene {
var shopTextContainer = SKShapeNode()
var backtoMenuButton = SKSpriteNode()
var skinsText = SKLabelNode()
var coinBox = SKSpriteNode()
var coins = SKLabelNode()
/// Moveable node in the scrollView
var startY: CGFloat = 0.0
var lastY: CGFloat = 0.0
var moveableArea = SKNode()
//SKINS
let skinsArray = [
["skin0": "Character", "price": "Default", "locked":false],
["skin1": "blueCharacter", "price": "50", "locked": true],
["skin2": "basketballSkin", "price": "150", "locked": true]
]
static var currentSkin = "skin0"
override func didMoveToView(view: SKView) {
/* Setup your scene here */
scene?.backgroundColor = UIColor(red: 31/255, green: 30/255, blue: 30/255, alpha: 1.0)
addTitle()
/// add moveable node
moveableArea.position = CGPointMake(0, 0)
self.addChild(moveableArea)
addSkins()
let bottom = SKLabelNode(fontNamed: "Avenir-Black")
bottom.text = "Bottom"
bottom.fontSize = CGRectGetMaxY(self.frame)/20
bottom.position = CGPoint(x:CGRectGetMidX(self.frame), y:0-CGRectGetMaxY(self.frame)*0.5)
moveableArea.addChild(bottom)
}
func addTitle() {
shopTextContainer = SKShapeNode(rectOfSize: CGSize(width: self.frame.size.width, height: 130))
shopTextContainer.position = CGPoint(x: self.frame.width/2, y: self.frame.height - 65)
shopTextContainer.fillColor = UIColor(red: 133/255, green: 0, blue: 241/255, alpha: 1.0)
shopTextContainer.strokeColor = UIColor.clearColor()
shopTextContainer.zPosition = 20
self.addChild(shopTextContainer)
backtoMenuButton = SKSpriteNode(imageNamed: "backButton")
backtoMenuButton.position = CGPoint(x: -shopTextContainer.frame.width/2 + 55, y: 0)
backtoMenuButton.zPosition = 25
shopTextContainer.addChild(backtoMenuButton)
skinsText.fontName = "DayPosterBlack"
skinsText.fontSize = 50.0
skinsText.text = "Skins"
skinsText.position = CGPoint(x: 0, y: -23)
skinsText.zPosition = 25
shopTextContainer.addChild(skinsText)
coinBox = SKSpriteNode(imageNamed: "coinBox")
coinBox.position = CGPoint(x: 0, y: -50)
coinBox.zPosition = 25
shopTextContainer.addChild(coinBox)
coins.fontName = "DayPosterBlack"
coins.fontColor = UIColor.whiteColor()
coins.position = CGPoint(x: 0, y: -6)
coins.zPosition = 1
coins.text = "0"
coins.fontSize = 16.0
coins.zPosition = 25
coinBox.addChild(coins)
}
func addSkins() {
for i in 0...skinsArray.count-1 {
let shape = SKShapeNode(rectOfSize: CGSize(width: 283, height: 73), cornerRadius: 40.05)
shape.position = CGPoint(x: self.frame.width/2, y: self.frame.height - 105*CGFloat(i) - 215)
shape.strokeColor = UIColor(red: 133/255, green: 0, blue: 241/255, alpha: 1.0)
shape.fillColor = UIColor(red: 1/255, green: 1/255, blue: 1/255, alpha: 0.78)
shape.name = String(format: "skin%d", i)
moveableArea.addChild(shape)
let price = SKLabelNode()
price.text = skinsArray[i]["price"] as? String
price.fontSize = 15.0
price.fontName = "DayPosterBlack"
price.fontColor = UIColor.whiteColor()
price.position = CGPoint(x: 0, y: -30)
shape.addChild(price)
let skin = SKSpriteNode(imageNamed: skinsArray[i][shape.name!] as! String)
skin.zPosition = 1
skin.position = CGPoint(x: 0, y: 4)
shape.addChild(skin)
if skinsArray[i]["locked"] == true {
let locked = SKSpriteNode(imageNamed: "lock")
locked.position = CGPoint(x: -shape.frame.size.width/2 + 50, y: 0)
shape.addChild(locked)
skin.alpha = 0.45
}
else if skinsArray[i]["locked"] == false {
let locked = SKSpriteNode(imageNamed: "unlock")
locked.position = CGPoint(x: -shape.frame.size.width/2 + 50, y: 0)
shape.addChild(locked)
shape.fillColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.4)
skin.alpha = 1.0
}
if shape.name == SkinsScene.currentSkin {
shape.strokeColor = UIColor(red: 65/255, green: 117/255, blue: 5/255, alpha: 1.0)
shape.lineWidth = 3.0
}
}
}
/// Touches began,
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
// store the starting position of the touch
for touch in touches {
let location = touch.locationInNode(self)
let goBack = touch.locationInNode(shopTextContainer)
startY = location.y
lastY = location.y
if backtoMenuButton.containsPoint(goBack){
backtoMenuButton.alpha = 0.7
}
else {
backtoMenuButton.alpha = 1.0
}
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
let goBack = touch.locationInNode(shopTextContainer)
if backtoMenuButton.containsPoint(goBack){
if GameScene.soundOn == true {
self.scene?.runAction(buttonTouched)
}
let scene = ShopScene(size: view!.bounds.size)
scene.scaleMode = .ResizeFill
self.view?.presentScene(scene,transition: SKTransition.fadeWithDuration(0.8))
}
else {
backtoMenuButton.alpha = 1.0
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
let currentY = location.y
// Set Top and Bottom scroll distances, measured in screenlengths
let topLimit:CGFloat = 0.0
let bottomLimit:CGFloat = 0.6
// Set scrolling speed - Higher number is faster speed
let scrollSpeed:CGFloat = 1.0
// calculate distance moved since last touch registered and add it to current position
let newY = moveableArea.position.y + ((currentY - lastY)*scrollSpeed)
// perform checks to see if new position will be over the limits, otherwise set as new position
if newY < self.size.height*(-topLimit) {
moveableArea.position = CGPointMake(moveableArea.position.x, self.size.height*(-topLimit))
}
else if newY > self.size.height*bottomLimit {
moveableArea.position = CGPointMake(moveableArea.position.x, self.size.height*bottomLimit)
}
else {
moveableArea.position = CGPointMake(moveableArea.position.x, newY)
}
// Set new last location for next time
lastY = currentY
}
}
You can see that I am making a rectangle with the skin from the array of dictionaries declared before the didMoveToView function. I had some code that attempted to detect the touch of the rectangle, but it isn't working, so I deleted it. I also tried to make a static variable called currentSkin to hold the skin and then pass it to my game scene, but that didn't work for me either. Any help would be appreciated. Thanks!
I don't know if I've understand well your question.
But if you want pass your currentSkin to GameScene, you can use a observe, a delegate or static var.
If you set static var current skin, you must write that variable.
in touch:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
// store the starting position of the touch
for touch in touches {
let location = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(location)
if let name = touchedNode.name {
if string.rangeOfString("skin") != nil {
SkinsScene.currentSkin = name
println(name)
}
}
}
}
Now you can call in GameScene:
let mySkin = SkinsScene.currentSkin
if your GameScene alredy exist, you must use delegate or observe.
Let me know you I understood well your question
I am new is swift programming. I want to spawn an enemies into a random positions using a Class node. I tried to search a code for a random spawning of an enemies but it seems it is irrelevant for my code.
Here is the code I searched for the random spawning.
import SpriteKit
class GameScene: SKScene {
let player = SKSpriteNode(imageNamed:"spacemonkey_fly02")
override func didMoveToView(view: SKView) {
player.position = CGPoint(x:frame.size.width * 0.1, y: frame.size.height * 0.5)
addChild(player)
backgroundColor = SKColor.blackColor()
runAction(SKAction.repeatActionForever(
SKAction.sequence([
SKAction.runBlock(spawnEnemy),
SKAction.waitForDuration(1.0)])))
}
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(#min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
func spawnEnemy() {
let enemy = SKSpriteNode(imageNamed: "boss_ship")
enemy.name = "enemy"
enemy.position = CGPoint(x: frame.size.width, y: frame.size.height * random(min: 0, max: 1))
addChild(enemy)
}
}
Here is the Class I made that I want to spawn randomly in my gamescene
import SpriteKit
class Meteor: SKSpriteNode, GameSprite {
var textureAtlas:SKTextureAtlas = SKTextureAtlas(named:"meteor.atlas")
var meteorAnimation = SKAction()
func spawn(parentNode: SKNode, position: CGPoint, size: CGSize = CGSize(width: 30, height: 30)) {
parentNode.addChild(self)
meteorRotation()
self.size = size
self.position = position
self.texture = textureAtlas.textureNamed("meteor-1.png")
self.physicsBody = SKPhysicsBody(texture: textureAtlas.textureNamed("meteor-1.png"), size: size)
self.physicsBody = SKPhysicsBody(circleOfRadius: size.width / 2)
self.physicsBody?.affectedByGravity = true
self.runAction(meteorAnimation)
}
func meteorRotation() {
let meteorCycle = SKAction.rotateByAngle(4, duration: 2);
meteorAnimation = SKAction.repeatActionForever(meteorCycle)
}
func onTap() {
//self.physicsBody?.affectedByGravity = false
self.physicsBody?.dynamic = false
self.physicsBody?.categoryBitMask = 0
let crashAnimation = SKAction.group([
SKAction.fadeAlphaTo(0, duration: 0.2),
SKAction.scaleTo(1.5, duration: 0.2),
SKAction.moveBy(CGVector(dx: 0, dy: 25), duration: 0.2)
])
let resetAfterCrashed = SKAction.runBlock {
self.position.y = 5000
self.alpha = 1
self.xScale = 1
self.yScale = 1
}
let crashSequence = SKAction.sequence([
crashAnimation,
resetAfterCrashed
])
self.runAction(crashSequence)
}
}
Is it possible to use the code I searched for a class node?
I tried to modify the random spawning code you found to use your meteor class. I took out any lines that didn't involve the meteors, so you'll need to add your world, earth, and field code as you sent me. Give this a shot and let me know if it works out:
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.blackColor()
runAction(SKAction.repeatActionForever(
SKAction.sequence([
SKAction.runBlock(spawnEnemy),
SKAction.waitForDuration(1.0)])))
}
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(#min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
func spawnEnemy() {
let newMeteor = Meteor()
let meteorPosition = CGPoint(x: frame.size.width, y: frame.size.height * random(min: 0, max: 1))
newMeteor.spawn(world, meteorPosition)
}
}