Using sprite sheets in xcode

前端 未结 4 690
一向
一向 2020-12-14 13:19

I\'m trying to animate my game using a sprite sheet. How would I go about cutting out each sprite from the sprite sheet and using the sprite in xcode? I\'m currently using o

相关标签:
4条回答
  • 2020-12-14 13:57

    In sprite kit you can cut part of a texture out using SKTexture(rect: inTexture:) initializer. This is a helper class which manages an evenly spaced sprite sheet and can cut out a texture at a given row and column. It is used like So

    let sheet=SpriteSheet(texture: SKTexture(imageNamed: "spritesheet"), rows: 1, columns: 11, spacing: 1, margin: 1)
    let sprite=SKSpriteNode(texture: sheet.textureForColumn(0, row: 0))
    

    Here is the full code

    //
    //  SpriteSheet.swift
    //
    
    import SpriteKit
    
    class SpriteSheet {
        let texture: SKTexture
        let rows: Int
        let columns: Int
        var margin: CGFloat=0
        var spacing: CGFloat=0
        var frameSize: CGSize {
            return CGSize(width: (self.texture.size().width-(self.margin*2+self.spacing*CGFloat(self.columns-1)))/CGFloat(self.columns),
                height: (self.texture.size().height-(self.margin*2+self.spacing*CGFloat(self.rows-1)))/CGFloat(self.rows))
        }
    
        init(texture: SKTexture, rows: Int, columns: Int, spacing: CGFloat, margin: CGFloat) {
            self.texture=texture
            self.rows=rows
            self.columns=columns
            self.spacing=spacing
            self.margin=margin
    
        }
    
        convenience init(texture: SKTexture, rows: Int, columns: Int) {
            self.init(texture: texture, rows: rows, columns: columns, spacing: 0, margin: 0)
        }
    
        func textureForColumn(column: Int, row: Int)->SKTexture? {
            if !(0...self.rows ~= row && 0...self.columns ~= column) {
                //location is out of bounds
                return nil
            }
    
            var textureRect=CGRect(x: self.margin+CGFloat(column)*(self.frameSize.width+self.spacing)-self.spacing,
                                   y: self.margin+CGFloat(row)*(self.frameSize.height+self.spacing)-self.spacing,
                                   width: self.frameSize.width,
                                   height: self.frameSize.height)
    
            textureRect=CGRect(x: textureRect.origin.x/self.texture.size().width, y: textureRect.origin.y/self.texture.size().height,
                width: textureRect.size.width/self.texture.size().width, height: textureRect.size.height/self.texture.size().height)
            return SKTexture(rect: textureRect, inTexture: self.texture)
        }
    
    }
    

    The margin property is the gap between the edge of the image and the sprites. The spacing is the gap between each sprite. The fameSize is the size each sprite will be. This image explains it:

    Sprite Sheet Example

    0 讨论(0)
  • 2020-12-14 13:58

    Since the original question was tagged as Objective-C, here's a rough version of this SpriteSheet class in that language. Hope this is helpful:

    #import "SpriteSheet.h"
    
    @interface SpriteSheet ()
    @property (nonatomic, strong) SKTexture *texture;
    @property (nonatomic)         NSInteger rows;
    @property (nonatomic)         NSInteger cols;
    @property (nonatomic)         CGFloat   margin;
    @property (nonatomic)         CGFloat   spacing;
    @property (nonatomic, getter=frameSize)         CGSize    frameSize;
    @end
    
    
    @implementation SpriteSheet
    
    - (instancetype)initWithTextureName:(NSString *)name rows:(NSInteger)rows cols:(NSInteger)cols margin:(CGFloat)margin spacing:(CGFloat)spacing {
      SKTexture *texture = [SKTexture textureWithImageNamed:name];
      return [self initWithTexture:texture rows:rows cols:cols margin:margin spacing:spacing];
    }
    
    - (instancetype)initWithTexture:(SKTexture *)texture rows:(NSInteger)rows cols:(NSInteger)cols {
      return [self initWithTexture:texture rows:rows cols:cols margin:0 spacing:0];
    }
    
    - (instancetype)initWithTexture:(SKTexture *)texture rows:(NSInteger)rows cols:(NSInteger)cols margin:(CGFloat)margin spacing:(CGFloat)spacing {
      if (self == [super init]) {
        _texture = texture;
        _rows = rows;
        _cols = cols;
        _margin = margin;
        _spacing = spacing;
        _frameSize = [self frameSize];
    
      }
      return self;
    }
    
    - (CGSize)frameSize {
    
      CGSize newSize = CGSizeMake((self.texture.size.width - (self.margin * 2.0 + self.spacing * ((CGFloat)self.cols - 1.0))) / ((CGFloat)self.cols),
                        (self.texture.size.height - ((self.margin * 2.0) + (self.spacing * ((CGFloat)self.rows - 1.0))) / ((CGFloat)self.rows)));
    
      return newSize;
    }
    
    - (SKTexture *)textureForColumn:(NSInteger)column andRow:(NSInteger)row {
    
      if (column >= (self.cols) || row >= (self.rows)) {
        NSLog(@"Asking for row or col greater than spritesheet");
        return nil;
      }
    
      CGRect textureRect = CGRectMake(self.margin + (column * self.frameSize.width + self.spacing) - self.spacing,
                                      self.margin + (row * self.frameSize.width + self.spacing) - self.spacing, // note using width here
                                      self.frameSize.width,
                                      self.frameSize.height);
    
      textureRect = CGRectMake(textureRect.origin.x / self.texture.size.width, textureRect.origin.y / self.texture.size.height, textureRect.size.width / self.texture.size.width, textureRect.size.height/self.texture.size.height);
    
      return [SKTexture textureWithRect:textureRect inTexture:self.texture];
    }
    
    0 讨论(0)
  • 2020-12-14 13:59

    I am adding this into a separate answer, because it contains two important things, that I found:

    First :

    var textureRect=CGRect(x: self.margin+CGFloat(column)*(self.frameSize.width+self.spacing)-self.spacing,
                           y: self.margin+CGFloat(row)*(self.frameSize.width+self.spacing)-self.spacing,
                           width: self.frameSize.width,
                           height: self.frameSize.height)
    

    This code in Swift and its Objective-C counterpart both have a bug. The self.frameSize.width is used for both row and column calculations. The height needs to be used for column.

    Second : The coordinate system for SKTexture places its (0,0) origin at the bottom left corner of the image, this has to be considered when making your sprites or the code needs to be altered.

    For example You can get all the frames of the sprite from top left corner to bottom right corner of the sprite like this:

    var anim = [SKTexture]()
    for row in (0..<self.rows).reversed() {
      for column in 0..<self.columns {
        if let frame = textureForColumn(column: column, row: row) {
            anim.append(frame)
        }
      }
    }
    

    PS. I don't have the much experience with SpriteKit, so if there are any mistakes in my answer, please correct me.

    0 讨论(0)
  • 2020-12-14 14:02

    I did a fix for getting sprites from top to bottom, left to right.

    Also added a totalFrames so if you don't have a sprite with all frames, you can set the total frames so the array only contains the sprites, no blank sprites.

    //
    //  SpriteSheet.swift
    //
    
    import SpriteKit
    
    class SpriteSheet {
        let texture: SKTexture
        let rows: Int
        let columns: Int
        var margin: CGFloat=0
        var spacing: CGFloat=0
        var frameSize: CGSize {
            return CGSize(width: (self.texture.size().width-(self.margin*2+self.spacing*CGFloat(self.columns-1)))/CGFloat(self.columns),
                      height: (self.texture.size().height-(self.margin*2+self.spacing*CGFloat(self.rows-1)))/CGFloat(self.rows))
        }
    
        init(texture: SKTexture, rows: Int, columns: Int, spacing: CGFloat, margin: CGFloat) {
            self.texture=texture
            self.rows=rows
            self.columns=columns
            self.spacing=spacing
            self.margin=margin
    
        }
    
        convenience init(texture: SKTexture, rows: Int, columns: Int) {
            self.init(texture: texture, rows: rows, columns: columns, spacing: 0, margin: 0)
        }
    
        func textureForColumn(column: Int, row: Int)->SKTexture? {
            if !(0...self.rows ~= row && 0...self.columns ~= column) {
                return nil
            }
            var textureRect = CGRect(x: self.margin + CGFloat(column) * (self.frameSize.width+self.spacing)-self.spacing, y: self.margin + CGFloat(self.rows - row - 1) * (self.frameSize.height+self.spacing)-self.spacing,
                               width: self.frameSize.width,
                               height: self.frameSize.height)
    
            textureRect = CGRect(x: textureRect.origin.x/self.texture.size().width, y: textureRect.origin.y/self.texture.size().height,
                           width: textureRect.size.width/self.texture.size().width, height: textureRect.size.height/self.texture.size().height)
            return SKTexture(rect: textureRect, in: self.texture)
        }
    }
    

    To call, just add the animation like this:

    fileprivate let framesOpening: [SKTexture] = {
        let totalFrames = 28
        let rows = 6
        let columns = 5
        let sheet = SpriteSheet(texture: SKTexture(imageNamed: "GETREADY"), rows: rows, columns: columns, spacing: 0, margin: 0)
        var frames = [SKTexture]()
        var count = 0
    
        for row in (0..<rows) {
            for column in (0..<columns){
                if count < totalFrames {
                    if let texture = sheet.textureForColumn(column: column, row: row) {
                        frames.append(texture)
                    }
                }
                count+=1
            }
        }
        return frames
    }()
    

    and call it

        let sprite1 = SKSpriteNode(texture: framesOpening.first)
        let openingAnimation: SKAction = SKAction.animate(with: framesOpening, timePerFrame: 0.2, resize: false, restore: true)
        sprite1.position = CGPoint(x: frame.midX, y: frame.midY)
        sprite1.zPosition = 2000
        self.addChild(sprite1)
        sprite1.run(SKAction.repeatForever(openingAnimation))
    
    0 讨论(0)
提交回复
热议问题