7

我正在制作一个精灵套件游戏,显然拥有一个带有平铺纹理的大 SKSpriteNode 比拥有多个带有平铺纹理的 SKSpriteNode 更有效。我的问题是,当我尝试使用

SKTexture* tex = [SKTexture textureWithRect:CGRectMake(0,0,5,5) inTexture:[SKTexture textureWithImageNamed:@"tile.png"]];
SKSpriteNode* node = [SKSpriteNode spriteNodeWithTexture:tex size:CGSizeMake(100,100)];

图像已适当调整大小,但它被夹住而不是平铺。在 openGL 术语中,我得到了一个 GL_CLAMP_TO_EDGE,我想要一个 GL_REPEAT。无论如何我可以在单个 SKSpriteNode 中实现纹理的平铺效果,而无需创建非常大的图像。

这是我的问题的图像: 在此处输入图像描述

4

7 回答 7

10

Afaik,没有办法用平铺纹理创建精灵。但是你可以做的是在一个绘图过程中渲染很多精灵。

来自 Apple 的文档(Sprite Kit Best Practices -> 绘制您的内容):

如果一个节点的所有子节点都使用相同的混合模式和纹理图集,那么 Sprite Kit 通常可以在一次绘制过程中绘制这些精灵。另一方面,如果孩子被组织起来,以便为每个新精灵改变绘图模式,那么 Sprite Kit 可能会执行每个精灵一个绘图通道,这是非常低效的。

于 2013-11-13T21:22:48.850 回答
1
SKTexture* tex = [SKTexture textureWithRect:CGRectMake(0,0,5,5) inTexture:[SKTexture textureWithImageNamed:@"tile.png"]];

创建一个子纹理,并且textureWithRect:inTexture:rectangle 参数在单位坐标空间中,这意味着所有值都是 0-1

的行为CGRectMake(0,0,5,5)未定义,但似乎将纹理拉伸了 5 倍。


编辑:单位坐标空间

您可以将单位坐标视为指定总可能值的百分比。单位坐标空间中的每个坐标的取值范围为 0.0 到 1.0。例如,沿 x 轴,左边缘位于坐标 0.0,右边缘位于坐标 1.0。沿 y 轴,单位坐标值的方向随平台而变化

核心动画编程指南,图层使用两种坐标系

于 2014-01-26T19:02:35.243 回答
1

创建纹理时应用 ciaffinetile 过滤器。

于 2013-11-13T20:58:02.077 回答
1

我发现这个问题没有得到完美的回答。我是这样解决的。

你知道你可以通过 resizableImageWithCapInsets 在 UIButton 或 UIImageView 中做到这一点,所以,这是我的解决方案:

-(SKTexture *)textureWithImage:(UIImage *)image tiledForSize:(CGSize)size withCapInsets:(UIEdgeInsets)capInsets{
//get resizable image
image =  [image resizableImageWithCapInsets:capInsets];
//redraw image to target size
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
[image drawInRect:CGRectMake(0, 0, size.width-1, size.height-1)];
[[UIColor clearColor] setFill];
//get target image
UIImage *ResultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// get texture
SKTexture * texture = [SKTexture textureWithImage:ResultImage];
return texture;
}
于 2017-07-28T10:59:43.763 回答
0

我使用了薛永伟的答案,并在扩展中将其翻译为 Swift:

extension SKTexture {

  static func x_repeatableTexture(named name: String, toSize size: CGSize) -> SKTexture {
    let image = UIImage(named: name)
    UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
    image?.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    guard let resultImage = UIGraphicsGetImageFromCurrentImageContext() else {
      fatalError("Image could not be drawn.")
    }
    UIGraphicsEndImageContext()
    return SKTexture(image: resultImage)
  }

}

您可以通过以下方式使用它:

let texture = SKTexture.x_repeatableTexture(named: textureName, toSize: size)

该解决方案允许使用在资产编辑器中完成的切片,这非常好。

于 2019-01-11T11:06:30.690 回答
0

为了帮助我转换坐标系,我使用了以下函数 - 这样我就可以像上面使用 (5,5) 一样使用绝对坐标:

//Pass the absolute sizes of your sub-texture (subRect) and main-texture (mainRect)
CGRect unitCoordinateGivenSubRectInRect (CGRect subRect, CGRect mainRect)
{
if (mainRect.size.height == 0 || mainRect.size.width == 0) {
    return CGRectZero;
}

float x = subRect.origin.x / mainRect.size.width;
float y = subRect.origin.y / mainRect.size.height;

float sizeX = subRect.size.width / mainRect.size.width;
float sizeY = subRect.size.height / mainRect.size.height;

return CGRectMake(x, y, sizeX, sizeY);
}
于 2017-02-12T17:03:17.483 回答
0

更好更简单的方法当然是 via SKShader。像这样简单的着色器:

// SpriteTiled.fsh

//uniform vec2 u_offset;
//uniform vec2 u_tiling;

void main() {
    vec2 uv = v_tex_coord.xy + u_offset;
    vec2 phase = fract(uv / u_tiling);
    vec4 current_color = texture2D(u_texture, phase);

    gl_FragColor = current_color;
}

然后设置shader你的属性SKSpriteNode

shader = SKShader(
    named: "SpriteTiled", 
    uniforms: [
        // e.g., 0 has no effect and 1 will offset the texture by its whole extent
        SKUniform(name: "u_offset", vectorFloat2: vector_float2(0, 0)),
        // e.g., 0.5 will repeat the texture twice, 1 produces no effect, and 1 < "zooms in" on the texture
        SKUniform(name: "u_tiling", vectorFloat2: vector_float2(0.5, 0.5))
    ]
)

如果您的源纹理不是正方形,则只需将传递给的值的第一个分量乘以u_tiling纹理的纵横比即可。

于 2021-08-28T09:09:34.320 回答