Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add simple animated GIF to iOS Spritekit Game?

I am a beginner programmer and am creating a game using iOS sprite-kit. I have a simple animated GIF (30 frames) saved as a .gif file. Is there a simple way (few lines of code maybe similar to adding a regular .png through UIImage) of displaying this GIF in my game? I have done some research on displaying an animated GIF in Xcode and most involve importing extensive classes, most of which is stuff I don't think I need (I barely know enough to sift through it).

like image 461
user3797886 Avatar asked Jul 19 '14 23:07

user3797886


2 Answers

The way I think of it gifs are just like animating a sprite. So what I would do is add the gif as textures in a SKSpriteNode in a for loop and then tell it to run on the device with SKAction.repeatActionForever(). To be honest I'm fairly new to this as well. I'm just trying to give my best answer. This is written in Swift, but I don't think it'll be to hard to translate to Objective-C.

var gifTextures: [SKTexture] = [];

    for i in 1...30 {
        gifTextures.append(SKTexture(imageNamed: "gif\(i)"));
    }

    gifNode.runAction(SKAction.repeatActionForever(SKAction.animateWithTextures(gifTextures, timePerFrame: 0.125)));
like image 151
Michael Choi Avatar answered Nov 20 '22 23:11

Michael Choi


Michael Choi's answer will get you half way there. The rest is getting the individual frames out of the gif file. Here's how I do it (in Swift):

func load(imagePath: String) -> ([SKTexture], TimeInterval?) {
    guard let imageSource = CGImageSourceCreateWithURL(URL(fileURLWithPath: imagePath) as CFURL, nil) else {
        return ([], nil)
    }

    let count = CGImageSourceGetCount(imageSource)
    var images: [CGImage] = []

    for i in 0..<count {
        guard let img = CGImageSourceCreateImageAtIndex(imageSource, i, nil) else { continue }
        images.append(img)
    }

    let frameTime = count > 1 ? imageSource.delayFor(imageAt: 0) : nil

    return (images.map { SKTexture(cgImage: $0) }, frameTime)
}

extension CGImageSource { // this was originally from another SO post for which I've lost the link. Apologies.

    func delayFor(imageAt index: Int) -> TimeInterval {
        var delay = 0.1

        // Get dictionaries
        let cfProperties = CGImageSourceCopyPropertiesAtIndex(self, index, nil)
        let gifPropertiesPointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 0)
        if CFDictionaryGetValueIfPresent(cfProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque(), gifPropertiesPointer) == false {
            return delay
        }

        let gifProperties: CFDictionary = unsafeBitCast(gifPropertiesPointer.pointee, to: CFDictionary.self)

        // Get delay time
        var delayObject: AnyObject = unsafeBitCast(
            CFDictionaryGetValue(gifProperties,
                                 Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
            to: AnyObject.self)
        if delayObject.doubleValue == 0 {
            delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
                                                             Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
        }

        delay = delayObject as? TimeInterval ?? 0.1

        if delay < 0.1 {
            delay = 0.1 // Make sure they're not too fast
        }

        return delay
    }

}

Note that I assume that each frame of the gif is the same length, which is not always the case.

You could also pretty easily construct an SKTextureAtlas with these images.

like image 2
Joshua Basch Avatar answered Nov 20 '22 22:11

Joshua Basch