How to create progress bar in sprite kit?

后端 未结 11 940
失恋的感觉
失恋的感觉 2020-12-28 17:56

I want to create my own progress bar in Sprite Kit.
I figured I will need to images - one fully empty progress bar and filled progress bar.
I have those images, I pu

11条回答
  •  一生所求
    2020-12-28 18:25

    Swift 4

    ( my answer 3 -> old SpriteBar project fully translated to swift)

    To make a progress bar based to SKTextureAtlas you can use the Objective C project called SpriteBar maded by Henry Everett.

    I've forked and fully translated this project, this is the source:

    class SpriteBar: SKSpriteNode {
        var textureReference = ""
        var atlas: SKTextureAtlas!
        var availableTextureAddresses = Array()
        var timer = Timer()
        var timerInterval = TimeInterval()
        var currentTime = TimeInterval()
        var timerTarget: AnyObject!
        var timerSelector: Selector!
    
        init() {
            let defaultAtlas = SKTextureAtlas(named: "sb_default")
            let firstTxt = defaultAtlas.textureNames[0].replacingOccurrences(of: "@2x", with: "")
            let texture = defaultAtlas.textureNamed(firstTxt)
            super.init(texture: texture, color: .clear, size: texture.size())
            self.atlas = defaultAtlas
            commonInit()
        }
        convenience init(textureAtlas: SKTextureAtlas?) {
            self.init()
            self.atlas = textureAtlas
            commonInit()
        }
        func commonInit() {
            self.textureReference = "progress"
            resetProgress()
        }
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func closestAvailableToPercent(_ percent:Int)->Int {
            var closest = 0
            for thisPerc in self.availableTextureAddresses {
                if labs(Int(thisPerc) - percent) < labs(closest - percent) {
                    closest = Int(thisPerc)
                }
            }
            return closest
        }
        func percentFromTextureName(_ string:String) -> Int? {
            let clippedString = string.replacingOccurrences(of: "@2x", with: "")
            let pattern = "(?<=\(textureReference)_)([0-9]+)(?=.png)"
            let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive)
            let matches = regex?.matches(in: clippedString, options: [], range: NSRange(location: 0, length: clippedString.count))
            // If the matches don't equal 1, you have done something wrong.
            if matches?.count != 1 {
                NSException(name: NSExceptionName(rawValue: String("SpriteBar: Incorrect texture naming.")), reason: "Textures should follow naming convention: \(textureReference)_#.png. Failed texture name: \(string)", userInfo: nil).raise()
            }
            for match: NSTextCheckingResult? in matches ?? [NSTextCheckingResult?]() {
                let matchRange = match?.range(at: 1)
                let range = Range(matchRange!, in: clippedString)!
                return Int(clippedString[range.lowerBound..= fabsf(1.0) {
                if timerTarget != nil && timerTarget.responds(to: timerSelector) {
                    typealias MyTimerFunc = @convention(c) (AnyObject, Selector) -> Void
                    let imp: IMP = timerTarget.method(for: timerSelector)
                    let newImplementation = unsafeBitCast(imp, to: MyTimerFunc.self)
                    newImplementation(self.timerTarget, self.timerSelector)
                }
                timer.invalidate()
            }
        }
        func setProgressWithValue(_ progress:CGFloat, ofTotal maxValue:CGFloat) {
            self.setProgress(progress/maxValue)
        }
    
        func numberOfFrames(inAnimation animationName: String) -> Int {
            // Get the number of frames in the animation.
            let allAnimationNames = atlas.textureNames
            let nameFilter = NSPredicate(format: "SELF CONTAINS[cd] %@", animationName)
            return ((allAnimationNames as NSArray).filtered(using: nameFilter)).count
        }
        func startBarProgress(withTimer seconds: TimeInterval, target: Any?, selector: Selector) {
            resetProgress()
            timerTarget = target as AnyObject
            timerSelector = selector
            // Split the progress time between animation frames
            timerInterval = seconds / TimeInterval((numberOfFrames(inAnimation: textureReference) - 1))
            timer = Timer.scheduledTimer(timeInterval: timerInterval, target: self, selector: #selector(self.timerTick(_:)), userInfo: seconds, repeats: true)
        }
        @objc func timerTick(_ timer: Timer) {
            // Increment timer interval counter
            currentTime += timerInterval
            // Make sure we don't exceed the total time
            if currentTime <= timer.userInfo as! Double {
                setProgressWithValue(CGFloat(currentTime), ofTotal: timer.userInfo as! CGFloat)
            }
        }
        func invalidateTimer() {
            timer.invalidate()
        }
    }
    

    Usage:

    let progressBarAtlas = SKTextureAtlas.init(named: "sb_default")
    self.energyProgressBar = SpriteBar(textureAtlas: progressBarAtlas)
    self.addChild(self.energyProgressBar)
    self.energyProgressBar.size = CGSize(width:350, height:150)
    self.energyProgressBar.position = CGPoint(x:self.frame.width/2, y:self.frame.height/2)
    
    let wait = SKAction.wait(forDuration: 2.0)
    let action1 = SKAction.run {
        self.energyProgressBar.setProgress(0.7)
    }
    let action2 = SKAction.run {
        self.energyProgressBar.setProgress(0.0)
    }
    let action3 = SKAction.run {
        self.energyProgressBar.setProgress(1.0)
    }
    let action4 = SKAction.run {
        self.energyProgressBar.setProgress(0.5)
    }
    let action5 = SKAction.run {
        self.energyProgressBar.setProgress(0.1)
    }
    let action6 = SKAction.run {
        self.energyProgressBar.startBarProgress(withTimer: 10, target: self, selector: #selector(self.timeOver))
    }
    let sequence = SKAction.sequence([wait,action1,wait,action2,wait,action3,wait,action4,wait,action5,wait,action6])
    self.run(sequence)
    

    To have more details you can find my GitHUB repo here

提交回复
热议问题