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)
}
}
Related
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)
}
}
}
}
I'm constructing a Swift Playground with UIKit that contains an SKScene. After creating the code I'm planning to use within an Xcode project, I tried to adapt it into my Playground.
I encountered an issue while running the Playground - when a new SKSpriteNode is added to the Scene, the new and existing nodes jitter and lag for a split-second, then return to smooth 60fps. You can see this issue in effect here. No errors or warnings are spat, and the console is completely empty.
The scene in the project didn't have this issue, and I can't recreate it on another project - even when I directly copy-paste the code from the Playground to the project.
I searched the internet for answers to this issue and all I found was this StackOverflow question with no answers and the link to OP's supposed solution broken.
The code that is in effect for this SKScene is below.
class SimulatorController: UIViewController {
override func loadView() {
let view = SKView()
let scene = GameScene(size: view.bounds.size)
view.showsFPS = true
view.showsNodeCount = true
view.ignoresSiblingOrder = true
scene.scaleMode = .resizeFill
view.presentScene(scene)
self.view = view
}
}
class GameScene: SKScene {
override func didMove(to view: SKView) {
run(SKAction.repeatForever(SKAction.sequence([
SKAction.run(addNeutron),
SKAction.wait(forDuration: 1)
])
))
}
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
func addNeutron() {
let neutron = SKSpriteNode(imageNamed: "neutron.heic")
neutron.size = CGSize(width: 20, height: 20)
neutron.physicsBody = SKPhysicsBody(rectangleOf: neutron.size)
neutron.physicsBody?.isDynamic = true
let actualX = random(min: 0, max: size.width)
let actualY = random(min: 0, max: size.height)
neutron.position = CGPoint(x: actualX, y: actualY)
addChild(neutron)
let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
let actualX2 = random(min: 0, max: size.width)
let actualY2 = random(min: 0, max: size.height)
let actionMove = SKAction.move(to: CGPoint(x: actualX2, y: actualY2), duration: TimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
neutron.run(SKAction.sequence([actionMove, actionMoveDone]))
}
}
Thanks so much for your help!
I have a main player node and enemies coming down randomly, but parts of them are outside the frame, I want to make it so they (and the player) are not able to get out of frame, left and right borders. How can I achieve that?
Thank you for your time and effort :)
Here's my enemy line, not sure how this can help but here it is so I don't get reported for being 'too broad'
func launchEnemy() {
let randomX = arc4random_uniform( UInt32(screenWidth))
EnemyMissile.position = CGPoint( x: CGFloat(randomX) - (screenWidth / 2), y: screenHeight + 50)
let action = SKAction.move(by: CGVector(dx: 0, dy: -400 + speedScore), duration: 5.0)
EnemyMissile.run(SKAction.repeatForever(action))
increaseSpeed()
self.run(action, withKey:"LaunchEnemyAction")
}
Here's my EnemyClass
import Foundation
import SpriteKit
class EnemyClass: SKNode {
var EnemyNode:SKSpriteNode = SKSpriteNode()
var hitsToKill:Int = 2
var hitCount:Int = 0
var damagePoints: Int = 2
//var missileAnimation:SKAction?
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) had not been implemented")
}
override init () {
super.init()
}
func createEnemy ( _ theImage:String) {
EnemyNode = SKSpriteNode(imageNamed: theImage)
self.addChild(EnemyNode)
let body:SKPhysicsBody = SKPhysicsBody(circleOfRadius: EnemyNode.size.width / 2.25, center:CGPoint(x: 0,y: 0))
body.isDynamic = true
body.affectedByGravity = false
body.allowsRotation = false
body.categoryBitMask = BodyType.enemy.rawValue
body.contactTestBitMask = BodyType.bullet.rawValue | BodyType.player.rawValue
self.physicsBody = body
self.name = "EnemyClass"
}
func destroy() {
self.removeFromParent()
self.name = "removeNode"
}
func hit() -> Bool {
hitCount += 1
if ( hitCount == hitsToKill) {
destroy()
return true
} else {
damagePoints = 4
EnemyNode.removeAction(forKey: "animation")
return false
}
}
}
Basically you have 2 issues:
Your spawning points are messed up
You have no boundary in place
You have a few options for setting boundaries, one being a physicsBody, another being keepInBounds() method or such. Below I should a simple keep in bounds method:
class GameScene: SKScene {
func spawnEnemy() {
let offset = CGFloat(5) // pixels, so enemy will have some spacing between borders.
let enemy = SKSpriteNode(color: .blue, size: CGSize(width: 50, height: 100))
enemy.name = "enemy"
addChild(enemy)
var randomX = CGFloat(arc4random_uniform(UInt32(self.size.width))) // This is not a perfect rando algo.
// Because our random x was from 0 through screenwidth, but .position works
// on -halfWidth through +halfWidth
randomX -= (self.frame.size.width / 2)
// Because spawning on the border will mean that the enemy is HALF out of view:
/*
right border: |
enemy # frame.maxX: x
- offset: x|
- size.w/2 x |
*/
if randomX > self.frame.maxX - offset - (enemy.size.width / 2) {
randomX = self.frame.maxX - offset - (enemy.size.width / 2)
}
else if randomX < self.frame.minX + offset + (enemy.size.width / 2) {
randomX = self.frame.minX + offset + (enemy.size.width / 2)
}
enemy.position.x = randomX
}
func keepEnemyInBounds() {
// A better way to do this would be to have a dictionary of enemies:
for node in self.children {
guard node.name == "enemy" else { continue }
let enemy = node as! SKSpriteNode
if enemy.position.x > frame.maxX - (enemy.size.width / 2) {
enemy.position.x = frame.maxX - (enemy.size.width / 2)
}
else if enemy.position.x < frame.minX + (enemy.size.width / 2) {
enemy.position.x = frame.minX + (enemy.size.width / 2)
}
}
}
override func update(_ currentTime: TimeInterval) {
spawnEnemy()
}
override func didFinishUpdate() {
keepEnemyInBounds()
}
}
I was trying to make a game where the dragon moves around randomly and the hero has to avoid it. I can make the dragon appear at a random location and move once or twice but to make it continuously move from point to point and then move some more has given me trouble. I think it might be because I'm not waiting for the action to complete before generating all of the random numbers. I tried the following code including labels to prove to myself that the random numbers are generating, at least up to 20...
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private let greenDragonNode = GreenDragonSprite.newInstance()
private var lastUpdateTime : TimeInterval = 0
private var i = 0
override func sceneDidLoad() {
self.lastUpdateTime = 0
let xOrigin = CGFloat(arc4random()).truncatingRemainder(dividingBy: size.width)
let yOrigin = CGFloat(arc4random()).truncatingRemainder(dividingBy: size.height)
let dragonOrigin = CGPoint(x: xOrigin, y: yOrigin)
greenDragonNode.position = dragonOrigin
greenDragonNode.setScale(0.1)
addChild(greenDragonNode)
}
override func didMove(to view: SKView) {
for i in 1...20 {
let xDestination = CGFloat(arc4random()).truncatingRemainder(dividingBy: size.width)
let yDestination = CGFloat(arc4random()).truncatingRemainder(dividingBy: size.height)
let dragonDestination = CGPoint(x: xDestination, y: yDestination)
let xDestination2 = CGFloat(arc4random()).truncatingRemainder(dividingBy: size.width)
let yDestination2 = CGFloat(arc4random()).truncatingRemainder(dividingBy: size.height)
let dragonDestination2 = CGPoint(x: xDestination2, y: yDestination2)
let dragonNodeTravel = SKAction.move(to:dragonDestination, duration: 3.0)
let dragonNodeReturn = SKAction.move(to: dragonDestination2, duration: 3.0)
greenDragonNode.run(SKAction.sequence([dragonNodeTravel, dragonNodeReturn]))
// i += 1
let label = SKLabelNode(text: "\(i)")
label.position = dragonDestination2
addChild(label)
}
}
}
That is happening because only the last action from the for loop is executed. You have to make a queue of actions. So you should append them to an array, and run them in a sequence, like this:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private let greenDragonNode = SKSpriteNode(color: .green, size: CGSize(width: 20, height: 20))
func getRandomPoint(withinRect rect:CGRect)->CGPoint{
let x = CGFloat(arc4random()).truncatingRemainder(dividingBy: rect.size.width)
let y = CGFloat(arc4random()).truncatingRemainder(dividingBy: rect.size.height)
return CGPoint(x: x, y: y)
}
override func didMove(to view: SKView) {
greenDragonNode.position = self.getRandomPoint(withinRect: frame)
addChild(greenDragonNode)
var actions:[SKAction] = []
for i in 1...20 {
let destination = getRandomPoint(withinRect: frame)
let move = SKAction.move(to:destination, duration: 1.0)
actions.append(move)
let label = SKLabelNode(text: "\(i)")
label.position = destination
addChild(label)
}
let sequence = SKAction.sequence(actions)
greenDragonNode.run(sequence)
}
}
I'm experimenting a few things with Swift and SpriteKit.
I have 4 images of coins. (coin1,coin2, ...). I want to spawn a random coin at a random position on the screen and let it fade out. I want to repeat this action every 3 seconds. This is the code and it worked fine.
class GameScene: SKScene {
// creating a playable area
let gameArea: CGRect
override init(size: CGSize) {
let maxAspectRatio: CGFloat = 16.0/9.0
let playableWidth = size.height / maxAspectRatio
let gameAreaMargin = (size.width - playableWidth)/2
gameArea = CGRect(x: gameAreaMargin, y: 270, width: playableWidth, height: size.height - 270)
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max-min) + min
}
func spawnAndFadeCoins() -> SKSpriteNode{
let randNum = arc4random()%4 + 1
let coin = SKSpriteNode(imageNamed: "coin\(randNum)")
if randNum == 4 {
coin.zPosition = 10
}
else {
coin.zPosition = 5
}
coin.name = "coin\(randNum)"
let randomX = random(min: gameArea.minX + coin.size.width/2,
max: gameArea.maxX - coin.size.width/2)
let randomY = random(min: gameArea.minY + coin.size.height/2,
max: gameArea.maxY - coin.size.height/2)
coin.position = CGPoint(x: randomX, y: randomY)
let disappear = SKAction.fadeOut(withDuration: 1.0)
coin.run(SKAction.repeatForever(disappear))
self.addChild(coin)
return(coin)
}
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed:"bg")
background.size = self.size
background.position = CGPoint(x:self.frame.size.width/2, y: self.frame.size.height/2)
background.zPosition = 0
self.addChild(background)
var _ = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(GameScene.spawnAndFadeCoins), userInfo: nil, repeats: true)
}
}
However, when I add a label to each coin specifying its name, something weird happens: After every 3 seconds, 3 coins appear simultaneously on the screen. The label is placed on 1 of those 3 coins, and it doesn't even say the coin's name correctly. This is the new code:
class GameScene: SKScene {
// creating a playable area
let gameArea: CGRect
override init(size: CGSize) {
let maxAspectRatio: CGFloat = 16.0/9.0
let playableWidth = size.height / maxAspectRatio
let gameAreaMargin = (size.width - playableWidth)/2
gameArea = CGRect(x: gameAreaMargin, y: 270, width: playableWidth, height: size.height - 270)
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max-min) + min
}
func spawnAndFadeCoins() -> SKSpriteNode{
let randNum = arc4random()%4 + 1
let coin = SKSpriteNode(imageNamed: "coin\(randNum)")
if randNum == 4 {
coin.zPosition = 10
}
else {
coin.zPosition = 5
}
coin.name = "coin\(randNum)"
let randomX = random(min: gameArea.minX + coin.size.width/2,
max: gameArea.maxX - coin.size.width/2)
let randomY = random(min: gameArea.minY + coin.size.height/2,
max: gameArea.maxY - coin.size.height/2)
coin.position = CGPoint(x: randomX, y: randomY)
let disappear = SKAction.fadeOut(withDuration: 1.0)
coin.run(SKAction.repeatForever(disappear))
self.addChild(coin)
return(coin)
}
func spawnAndFadeLabels() -> SKLabelNode{
let label = SKLabelNode()
label.text = "\(spawnAndFadeCoins().name)"
label.zPosition = 15
label.color = SKColor.white()
label.fontSize = 60
label.position = spawnAndFadeCoins().position
let disappear2 = SKAction.fadeOut(withDuration: 1.0)
label.run(SKAction.repeatForever(disappear2))
self.addChild(label)
return(label)
}
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed:"bg")
background.size = self.size
background.position = CGPoint(x:self.frame.size.width/2, y: self.frame.size.height/2)
background.zPosition = 0
self.addChild(background)
var _ = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(GameScene.spawnAndFadeCoins), userInfo: nil, repeats: true)
var _ = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(GameScene.spawnAndFadeLabels), userInfo: nil, repeats: true)
}
}
Can you help me solve this mystery? Thanks in advance.
The problem is that you called spawnAndFadeCoins two more times in spawnAndFadeLabels:
func spawnAndFadeLabels() -> SKLabelNode{
let label = SKLabelNode()
label.text = "\(spawnAndFadeCoins().name)" <-- here!
label.zPosition = 15
label.color = SKColor.white()
label.fontSize = 60
label.position = spawnAndFadeCoins().position <-- and here!
let disappear2 = SKAction.fadeOut(withDuration: 1.0)
label.run(SKAction.repeatForever(disappear2))
self.addChild(label)
return(label)
}
I think one thing you can do to solve it is this:
Have only one timer:
runAction(SKAction.repeatForever(SKAction.sequence([SKAction.wait(forDuration: 3), SKAction.run(spawnAndFadeCoins)])))
I used SKActions here because it is not recommended to use Timer in spritekit.
Now only spawnAndFadeCoins will be called once every three seconds. Then, change spawnAndFadeLabels to:
func spawnAndFadeLabels(of node: SKSpriteNode) -> SKLabelNode{
let label = SKLabelNode()
label.text = "\(node.name)"
label.zPosition = 15
label.color = SKColor.white()
label.fontSize = 60
label.position = node.position
let disappear2 = SKAction.fadeOut(withDuration: 1.0)
label.run(SKAction.repeatForever(disappear2))
self.addChild(label)
return(label)
}
Change spawnAndFadeCoins to:
func spawnAndFadeCoins() -> SKSpriteNode{
let randNum = arc4random()%4 + 1
let coin = SKSpriteNode(imageNamed: "coin\(randNum)")
if randNum == 4 {
coin.zPosition = 10
}
else {
coin.zPosition = 5
}
coin.name = "coin\(randNum)"
let randomX = random(min: gameArea.minX + coin.size.width/2,
max: gameArea.maxX - coin.size.width/2)
let randomY = random(min: gameArea.minY + coin.size.height/2,
max: gameArea.maxY - coin.size.height/2)
coin.position = CGPoint(x: randomX, y: randomY)
let disappear = SKAction.fadeOut(withDuration: 1.0)
coin.run(SKAction.repeatForever(disappear))
self.addChild(coin)
spawnAndFadeLabels(of: coin) <-- This line is added!
return(coin)
}
I think this is want you want.