如何在运行时每 0.2 秒在屏幕上的不同位置重新创建屏幕上的相同节点?

How do I recreate the same nodes on screen during runtime every 0.2 seconds at different locations on screen?

好的,所以我在 swift 制作这个游戏,有这 4 个硬币,每个硬币的单独价值分别为 10、20、50、100。我是否知道如何分配这些硬币这些节点的值,然后在运行时重新创建它们?我的意思是将具有相同值的完全相同的节点添加到屏幕上,可能带有动画,位于屏幕上的不同位置。此外,我希望以不同的时间间隔在屏幕上重新复制 4 个硬币,例如:硬币 10 的价值较低,因此与硬币 50(例如:每 1 秒)相比,它的重新创建频率较低(例如:每 0.2 秒) . 我什至不知道如何开始这个,所以请帮忙? 我在下面包含了我的 GameScene.swift 代码,尽可能具体。

//
//  GameScene.swift
//  Coin Grabber
//
//  Created by Viren Sareen on 13/07/2015.
//  Copyright (c) 2015 Viren Sareen. All rights reserved.
//

import SpriteKit

class GameScene: SKScene, SKPhysicsContactDelegate {

var coin10 = SKSpriteNode(imageNamed: "10S.png")
var coin100 = SKSpriteNode(imageNamed: "100S.png")
var coin50 = SKSpriteNode(imageNamed: "50S.png")
var coin20 = SKSpriteNode(imageNamed: "20S.png")
var wall1 = SKSpriteNode(imageNamed: "Wall1.png")
var wall2 = SKSpriteNode(imageNamed: "Wall2.png")
var bar = SKSpriteNode(imageNamed: "Bar.png")
var touchedcoin: SKSpriteNode?

var scorelabel = SKLabelNode()
var score = 0

var touchPoint: CGPoint = CGPoint()
var touching: Bool = false

enum ColliderType:UInt32 {
    case coin = 1
    case wall = 2
    case bars = 3

}

override func didMoveToView(view: SKView) {
    /* Setup your scene here */

    //Adding coin10
    coin10.position = CGPointMake(self.size.width / 2, self.size.height / 5)
    coin10.physicsBody = SKPhysicsBody(circleOfRadius: coin10.size.width/1.5)
    coin10.physicsBody!.affectedByGravity = false
    coin10.physicsBody!.categoryBitMask = ColliderType.coin.rawValue
    coin10.physicsBody!.contactTestBitMask = ColliderType.wall.rawValue
    coin10.physicsBody!.collisionBitMask = ColliderType.wall.rawValue
    coin10.physicsBody!.dynamic = true
    self.addChild(coin10)

    //Adding coin100
    coin100.position = CGPointMake(self.size.width / 1.7, self.size.height / 5.1)
    coin100.physicsBody = SKPhysicsBody(circleOfRadius: coin100.size.width/1.3)
    coin100.physicsBody!.affectedByGravity = false
    coin100.physicsBody!.categoryBitMask = ColliderType.coin.rawValue
    coin100.physicsBody!.contactTestBitMask = ColliderType.wall.rawValue
    coin100.physicsBody!.collisionBitMask = ColliderType.wall.rawValue
    coin100.physicsBody!.dynamic = true
    self.addChild(coin100)

    //Adding coin50
    coin50.position = CGPointMake(self.size.width / 2.2, self.size.height / 4.9)
    coin50.physicsBody = SKPhysicsBody(circleOfRadius: coin50.size.width/1.5)
    coin50.physicsBody!.affectedByGravity = false
    coin50.physicsBody!.categoryBitMask = ColliderType.coin.rawValue
    coin50.physicsBody!.contactTestBitMask = ColliderType.wall.rawValue
    coin50.physicsBody!.collisionBitMask = ColliderType.wall.rawValue
    coin50.physicsBody!.dynamic = true
    self.addChild(coin50)

    //Adding coin20
    coin20.position = CGPointMake(self.size.width / 2.4, self.size.height / 5)
    coin20.physicsBody = SKPhysicsBody(circleOfRadius: coin20.size.width/1.5)
    coin20.physicsBody!.affectedByGravity = false
    coin20.physicsBody!.categoryBitMask = ColliderType.coin.rawValue
    coin20.physicsBody!.contactTestBitMask = ColliderType.wall.rawValue
    coin20.physicsBody!.collisionBitMask = ColliderType.wall.rawValue
    coin50.physicsBody!.dynamic = true
    self.addChild(coin20)

    //Adding wall1
    wall1.position = CGPointMake(self.size.width / 1.32, self.size.height / 1.04)
    wall1.physicsBody = SKPhysicsBody(rectangleOfSize: wall1.size)
    wall1.physicsBody!.affectedByGravity = false
    wall1.physicsBody!.categoryBitMask = ColliderType.wall.rawValue
    wall1.physicsBody!.contactTestBitMask = ColliderType.coin.rawValue
    wall1.physicsBody!.collisionBitMask = ColliderType.coin.rawValue
    wall1.physicsBody!.dynamic = false
    self.addChild(wall1)

    //Adding wall2
    wall2.position = CGPointMake(self.size.width / 4.8, self.size.height / 1.04)
    wall2.physicsBody = SKPhysicsBody(rectangleOfSize: wall2.size)
    wall2.physicsBody!.affectedByGravity = false
    wall2.physicsBody!.categoryBitMask = ColliderType.wall.rawValue
    wall2.physicsBody!.contactTestBitMask = ColliderType.coin.rawValue
    wall2.physicsBody!.collisionBitMask = ColliderType.coin.rawValue
    wall2.physicsBody!.dynamic = false
    self.addChild(wall2)

    //Adding bar
    bar.position = CGPointMake(self.size.width / 2, self.size.height)
    bar.physicsBody = SKPhysicsBody(circleOfRadius: bar.size.height/2)
    bar.physicsBody!.affectedByGravity = false
    bar.physicsBody!.categoryBitMask = ColliderType.bars.rawValue
    bar.physicsBody!.contactTestBitMask = ColliderType.coin.rawValue
    bar.physicsBody!.collisionBitMask = ColliderType.coin.rawValue
    bar.physicsBody!.dynamic = false
    self.addChild(bar)

    //Adding physics world properties
    self.physicsWorld.contactDelegate = self
    var scenebody = SKPhysicsBody(edgeLoopFromRect: self.frame)
    scenebody.friction = 0
    self.physicsBody = scenebody
    self.physicsWorld.gravity = CGVectorMake(0, 0)
    physicsWorld.contactDelegate = self

    //Scoreboard
    scorelabel = SKLabelNode(text: "0")
    scorelabel.position.y = (self.size.height/2)
    scorelabel.position.x = (self.size.height/2.3)
    addChild(scorelabel)

}

func didBeginContact(contact: SKPhysicsContact) {

}

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    /* Called when a touch begins */

    for touch in (touches as! Set<UITouch>) {
        let location10 = touch.locationInNode(self)
        let location100 = touch.locationInNode(self)
        let location20 = touch.locationInNode(self)
        let location50 = touch.locationInNode(self)

        if coin10.containsPoint(location10){
            touchPoint = location10
            touching = true
            touchedcoin = coin10
        }
        else if coin100.containsPoint(location100){
            touchPoint = location100
            touching = true
            touchedcoin = coin100
        }
        else if coin20.containsPoint(location20){
            touchPoint = location20
            touching = true
            touchedcoin = coin20
        }
        else if coin50.containsPoint(location50){
            touchPoint = location50
            touching = true
            touchedcoin = coin50
        }
    }
}

override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {

    for touch in (touches as! Set<UITouch>) {
        let location10 = touch.locationInNode(self)
        let location100 = touch.locationInNode(self)
        let location50 = touch.locationInNode(self)
        let location20 = touch.locationInNode(self)

        if coin10.containsPoint(location10){
            touchPoint = location10
        }
        else if coin100.containsPoint(location100){
            touchPoint = location100
        }
        else if coin50.containsPoint(location50){
            touchPoint = location50
        }
        else if coin20.containsPoint(location20){
            touchPoint = location20
        }
    }
}

override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
    touching = false
}

override func update(currentTime: CFTimeInterval) {
    if touching {

        let dt: CGFloat = 1.1/101.0
        let distance = CGVector(dx: touchPoint.x-touchedcoin!.position.x, dy: touchPoint.y-touchedcoin!.position.y)
        let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
        touchedcoin!.physicsBody!.velocity = velocity
        }
    }
 }

这是您可以使用的硬币示例 class:

import Foundation
import SpriteKit

enum ColliderType:UInt32 {
    case coin = 1
    case wall = 2
    case bars = 3

}

class Coin {

    var value: Int
    var coinNode: SKSpriteNode

    init(coinValue: Int, scene: SKScene) {
        value = coinValue
        let node = SKSpriteNode(imageNamed: String(coinValue) + "S")
        node.position = CGPointMake(node.size.width / 2, node.size.height / 5)
        node.physicsBody = SKPhysicsBody(circleOfRadius: node.size.width/1.5)
        node.physicsBody!.affectedByGravity = false
        node.physicsBody!.categoryBitMask = ColliderType.coin.rawValue
        node.physicsBody!.contactTestBitMask = ColliderType.wall.rawValue
        node.physicsBody!.collisionBitMask = ColliderType.wall.rawValue
        node.physicsBody!.dynamic = true
        scene.addChild(node)
        self.coinNode = node
    }
}

这里还有一个子class硬币的选项:

import Foundation
import SpriteKit

enum ColliderType:UInt32 {
    case coin = 1
    case wall = 2
    case bars = 3

}

class Coin: SKSpriteNode {

    var value: Int

    init(coinValue: Int) {
        value = coinValue
        let texture = SKTexture(imageNamed: String(coinValue) + "S")
        super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
        self.position = CGPointMake(self.size.width / 2, self.size.height / 5)
        self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.width/1.5)
        self.physicsBody!.affectedByGravity = false
        self.physicsBody!.categoryBitMask = ColliderType.coin.rawValue
        self.physicsBody!.contactTestBitMask = ColliderType.wall.rawValue
        self.physicsBody!.collisionBitMask = ColliderType.wall.rawValue
        self.physicsBody!.dynamic = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

然后,编写一个计时器随机生成更多硬币实例,并为硬币选择一个值。 就计时器而言,在场景初始化或移动到视图时执行类似的操作。:

let timer = SKAction.waitForDuration(10, withRange: 2) 
let spawnNode = SKAction.runBlock {
    var coin = Coin(10, scene: self)
   //set coin position: coin.coinNode.position = whatever
}

let sequence = SKAction.sequence([timer, spawnNode])
self.runAction(SKAction.repeatActionForever(sequence))

我个人会选择 Kendal 提出的其他选项,它是 SKSpriteNode 的子类。

Coin.swift

import Foundation
import SpriteKit

enum ColliderType:UInt32 {
    case coin = 1
    case wall = 2
    case bars = 3

}


class Coin: SKSpriteNode {

    var value: Int

    init(coinValue: Int) {

         self.value = coinValue

         let texture = SKTexture(imageNamed: String(coinValue) + "S")

        super.init(texture: texture, color: nil, size: texture.size())


        self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.width/2.0)
        self.physicsBody!.affectedByGravity = true
        self.physicsBody!.categoryBitMask = ColliderType.coin.rawValue
        self.physicsBody!.contactTestBitMask = ColliderType.wall.rawValue
        self.physicsBody!.collisionBitMask = ColliderType.wall.rawValue
        self.physicsBody!.dynamic = true
        self.name = "coin"

    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

GameScene.swift:

    import SpriteKit


class GameScene: SKScene,SKPhysicsContactDelegate
{

    let debugLabel = SKLabelNode(fontNamed: "Geneva")

    var coin10counter = 0
    var coin20counter = 0
    var coin50counter = 0
    var coin100counter = 0

    let gameDuration = 15

    var timeLeft = 15

    let startButton = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 80, height:30))
    let stopButton = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 80, height:30))

    let gameTimerLabel = SKLabelNode(fontNamed: "Geneva")



    override func didMoveToView(view: SKView)
    {

        //Setting up physics - default for dy is -9.81 but because of easier debugging I set it to -0.5
        self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)

        self.physicsWorld.gravity = CGVector(dx: 0.0, dy:-0.5)


        //Debug labels

        debugLabel.fontColor = SKColor.whiteColor()
        debugLabel.fontSize =  15
        debugLabel.text = "coin10: \(coin10counter) coin20: \(coin20counter) coin50: \(coin50counter) coin100: \(coin100counter)"
        debugLabel.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMaxY(self.frame)-200)
        self.addChild(debugLabel)


        gameTimerLabel.fontColor = SKColor.whiteColor()
        gameTimerLabel.fontSize =  20
        gameTimerLabel.text = "Time left : \(gameDuration)"
        gameTimerLabel.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMaxY(self.frame)-40)

        self.addChild(gameTimerLabel)

        self.backgroundColor = SKColor.blackColor()


        //Buttons

        startButton.position = CGPoint(x: CGRectGetMidX(self.frame)-80, y: CGRectGetMaxY(self.frame)-100)
        startButton.name = "start"
        stopButton.position = CGPoint(x: CGRectGetMidX(self.frame)+80, y: CGRectGetMaxY(self.frame)-100)
        stopButton.name = "stop"

        self.addChild(startButton)
        self.addChild(stopButton)


    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {


        let touch: AnyObject? = touches.anyObject()

        let location = touch?.locationInNode(self)

        println(location)

        let touchedNode = self.nodeAtPoint(location!)

        println(touchedNode.name)

        if(touchedNode.name == "start"){

            self.generateCoins()

        }else if(touchedNode.name == "stop"){

            self.stopGeneratingCoins()
        }

    }


    func getRandomCoin() ->Coin{


        let randomNumber = Double(arc4random() % 1000) / 10.0;

        switch(randomNumber) {

       //You can modify this to play with chances

        case 60..<90:

             coin20counter++
            return Coin(coinValue: 20)
        case 90..<97:
             coin50counter++
            return Coin(coinValue: 50)

        case 97..<100: // smallest chance

             coin100counter++
            return Coin(coinValue: 100)
        default:
            //biggest chance
             coin10counter++
            return Coin(coinValue: 10)
        }


    }

    func stopGeneratingCoins(){


        removeActionForKey("spawning")

        removeActionForKey("countdown")

        coin10counter = 0

        coin20counter  = 0

        coin50counter = 0

        coin100counter = 0

        self.enumerateChildNodesWithName("coin", usingBlock: {
            (node: SKNode!, stop: UnsafeMutablePointer <ObjCBool>) -> Void in

            node.removeFromParent()

        })

        timeLeft = gameDuration

        debugLabel.text = "coin10: \(coin10counter) coin20: \(coin20counter) coin50: \(coin50counter) coin100: \(coin100counter)"

        gameTimerLabel.text = "Time left : \(gameDuration)"
    }


    func countdown(){


        let updateTimeleftLabel = SKAction.runBlock({

            self.gameTimerLabel.text = "Time left : \(self.timeLeft--)"
        })

        let waitAndUpdate = SKAction.sequence([updateTimeleftLabel ,SKAction.waitForDuration(1)] )



        let countdown = SKAction.repeatAction(waitAndUpdate, count: self.gameDuration)

        let sequence = SKAction.sequence([countdown, SKAction.runBlock({

            self.stopGeneratingCoins()

        })])

        self.runAction(sequence, withKey:"countdown")

    }


    func generateCoins(){

        if(self.actionForKey("spawning") != nil){return}

        countdown()

        let timer = SKAction.waitForDuration(0.5, withRange: 0.3)

        let spawnNode = SKAction.runBlock {


            var coin = self.getRandomCoin()


            let spawnLocation = CGPoint(x:Int(arc4random() % UInt32(self.frame.size.width - coin.size.width/2) ),
                                        y:Int(arc4random() %  UInt32(self.frame.size.height - coin.size.width/2)))

            coin.position = spawnLocation





            self.debugLabel.text =
            "coin10 : \(self.coin10counter) coin20: \(self.coin20counter) coin50 : \(self.coin50counter) coin100 : \(self.coin100counter)"

            self.addChild(coin)

            println(spawnLocation)

        }

        let sequence = SKAction.sequence([timer, spawnNode])
        self.runAction(SKAction.repeatActionForever(sequence) , withKey: "spawning")


    }



}

因此,此代码主要基于 Kendal 的代码,但有一些不同之处:

  • 我在将硬币添加到场景之前设置硬币的位置,而不是将场景作为参数传递到硬币的 init 方法中。
  • 我 运行 带有 Key 参数的操作允许我通过给定的键停止某些操作(例如停止产生硬币)

我还实现了在随机位置生成硬币以及随机生成某些硬币的机会 - 价值较低的硬币将比价值较高的硬币更频繁地产生。

编辑:

我添加了一个计时器和一些调试标签来显示游戏结束前还剩多少时间,并允许您跟踪生成的硬币数量。这是结果:

如您所见,"game" 在用户单击绿色按钮时启动,在单击红色按钮时停止。如果未被用户打断,游戏将在 gameDuration 变量确定的时间后结束。

您还可以看到根据价值确定的硬币数量是如何随机化的……十秒后大约有:

9个coin10节点,7个coin20节点,3个coin50和1个coin100节点,我想这就是你想要的数字。您可以调整 getRandomCoin: 方法以获得不同的结果。

还有一个名为 stopGeneratingCoins 的新方法可以将所有内容重置为默认值。在该方法中,所有 运行ning 操作都被删除,计数器和类似变量被设置为默认值,并且所有硬币都使用 - enumerateChildNodesWithName:usingBlock: 从其父项中删除使用此方法时,重要的是知道应该定义 coin.name 。因此,我在 Coin 的 init 方法中设置了硬币的名称,这使我可以通过名称搜索它(并将其从场景中删除)。就是这样:-)

希望这对您有所帮助。