Tiling a sprite texture in SpriteKit
I'm writing my next game, Icarus - Escape from Crete, using Xamarin iOS together with SpriteKit. The combination works great and SpriteKit turns out to be a well thought out API. My only gripe so far is that I haven't found an existing way for tiling a sprite texture, that is fill the entire sprite using a smaller texture.
The grass below is just a small image, repeated to fill the entire green meadow.
![Icarus in a green meadow](/img/tiling-a-sprite-texture-in-spritekit/1Icarus-in-a-beautiful-meadow.png)
The grass is created from this simple tile
![Small grass texture](/img/tiling-a-sprite-texture-in-spritekit/0grass-texture.png)
using an extension method on SKSpriteNode
/// <summary>
/// Creates a tiled texture and applies it to the <see cref="SKSpriteNode"/>.
/// </summary>
/// <param name="spriteNode">The <see cref="SKSpriteNode"/> to which the texture is applied.</param>
/// <param name="texture">The texture used as tiles.</param>
/// <param name="coverageSize">The size of the finished tiled texture.</param>
public static void TileSprite(this SKSpriteNode spriteNode, UIImage texture, CGSize coverageSize) {
var textureSize = new CGRect(CGPoint.Empty, texture.Size);
UIGraphics.BeginImageContext(coverageSize);
var context = UIGraphics.GetCurrentContext();
context.DrawTiledImage(textureSize, texture.CGImage);
var tiledBackground = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
spriteNode.Texture = SKTexture.FromImage(tiledBackground.CGImage);
spriteNode.Size = coverageSize;
}
Using CoreGraphics, an image of the proper size is created and the texture is tiled. Next the image is rendered and a SKTexture
is created from the result.
Here's the example from Icarus:
var grass = new SKSpriteNode();
grass.TileSprite(UIImage.FromFile("grass"), new CGSize(Device.Width, GrassHeight));
This method has two disadvantages though.
- It's slow. Create the needed nodes during game loading, not during gameplay.
- The resulting image is flipped. Remedy this by either using a flipped image as the tile, or flip the Y-axis on the
SKSpriteNode
after the texture is applied:grass.YScale = -1f;
I use the former.
Even with the disadvantages, the method was useful in Icarus. Maybe it's useful for you too 😃