在 xcode 中使用精灵表

Using sprite sheets in xcode

我正在尝试使用 sprite sheet 为我的游戏制作动画。我将如何从 sprite sheet 中切出每个 sprite 并在 xcode 中使用该 sprite?我目前正在使用 obj -c。我在某处读到我需要使用框架 cocoa2d 才能做到这一点?

在 sprite kit 中,您可以使用 SKTexture(rect: inTexture:) 初始值设定项来剪切部分纹理。这是一个帮助程序 class,它管理一个均匀间隔的精灵 sheet 并且可以在给定的行和列处剪切纹理。它像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))

这是完整的代码

//
//  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)
    }

}

边距属性 是图像边缘和精灵之间的间隙。间距是每个精灵之间的间隙。 fameSize 是每个精灵的大小。这张图片解释了它:

由于原始问题被标记为 Objective-C,因此这里是该 SpriteSheet class 的该语言的粗略版本。希望这对您有所帮助:

#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];
}

我将其添加到一个单独的答案中,因为它包含我发现的两个重要内容:

第一个:

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)

Swift 中的这段代码及其 Objective-C 对应的代码都有一个错误。 self.frameSize.width 用于行和列计算。高度需要用于列。

第二个: SKTexture 的坐标系将其 (0,0) 原点放在图像的左下角,制作精灵时必须考虑到这一点,否则需要更改代码。

例如,您可以像这样获取精灵从左上角到右下角的所有帧:

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。我对SpriteKit没有太多经验,所以如果我的回答有任何错误,请指正。

我修复了从上到下、从左到右获取精灵的问题。

还添加了总帧数,因此如果您没有包含所有帧的精灵,您可以设置总帧数,使数组只包含精灵,不包含空白精灵。

//
//  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)
    }
}

要调用,只需添加这样的动画:

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
}()

并称之为

    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))