Runar Ovesen Hjerpbakk

Science-based software development

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


The grass is created from this simple tile Small grass texture


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.

  1. It's slow. Create the needed nodes during game loading, not during gameplay.
  2. 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 šŸ˜ƒ