A sprite sheet is basically just an image. But it’s an image that is organized in a special way, usually with a 32×32 or 64×64 pixel grid where each cell contains either a character animation frame – or a background tile. Back in the old Amiga days of 8 bit graphics and hand crafted sprites this was the only way to go.
Since the world of mobile javascript game development is more or less en-par with the old Amiga (in terms of cpu power and restrictions. Which is amazing since the Amiga had a 1hz motorola mc68000 CPU) an example of a spritesheet class might come in handy.
A simple spritesheet class
TW3SpriteSheet = class(TObject)
private
FImage: TW3Image;
FReady: Boolean;
FColumns: Integer;
FRows: Integer;
FTotal: Integer;
FWidth: Integer;
FHeight: Integer;
FOnReady: TNotifyEvent;
procedure HandleImageReady(sender:TObject);
procedure SetWidth(aValue:Integer);
procedure Setheight(aValue:Integer);
Procedure ReCalcInfo;
public
Property OnReady:TNotifyEvent read FOnReady write FOnReady;
Property SpriteWidth:Integer read FWidth write setWidth;
Property SpriteHeight:Integer read FHeight write setHeight;
Property Ready:Boolean read FReady;
Procedure LoadImage(aUrl:String);
Procedure Draw(Canvas:TW3Canvas;dx,dy:Integer;FrameIndex:Integer);
Constructor Create;virtual;
destructor Destroy;Override;
end;
constructor TW3SpriteSheet.Create;
begin
inherited Create;
FWidth := 32;
FHeight := 32;
FColumns := 0;
FRows := 0;
FTotal := 0;
FImage := TW3Image.Create(NIL);
FImage.OnLoad := HandleImageReady;
end;
destructor TW3SpriteSheet.Destroy;
begin
FImage.onLoad:=NIL;
FImage.free;
inherited;
end;
procedure TW3SpriteSheet.setWidth(aValue:Integer);
begin
FWidth:=w3_ensureRange(aValue,4,MAXINT);
ReCalcInfo;
end;
procedure TW3SpriteSheet.setheight(aValue:Integer);
begin
FHeight:=w3_ensureRange(aValue,4,MAXINT);
ReCalcInfo;
end;
procedure TW3SpriteSheet.ReCalcInfo;
begin
if FReady then
begin
FColumns:=FImage.Width div FWidth;
FRows:=FImage.Height div FHeight;
FTotal:=FColumns * FRows;
end else
begin
FColumns:=0;
FRows:=0;
FTotal:=0;
end;
end;
procedure TW3SpriteSheet.HandleImageReady(sender:TObject);
begin
FReady:=True;
ReCalcInfo;
if assigned(FOnReady) then
FOnReady(self);
end;
procedure TW3SpriteSheet.LoadImage(aUrl:String);
begin
FReady:=False;
ReCalcInfo;
FImage.LoadFromUrl(aUrl);
end;
procedure TW3SpriteSheet.Draw(Canvas:TW3Canvas; dx,dy:Integer; FrameIndex:Integer);
var
sx,sy: Integer;
begin
if FReady
and assigned(canvas)
and (FrameIndex>=0)
and (FrameIndex<FTotal) then
Begin
sx:=FrameIndex mod FColumns;
sy:=FrameIndex div FRows;
sx:=sx * FWidth;
sy:=sy * FHeight;
canvas.DrawImageEx2F(FImage.tagRef,sx,sy,FWidth,
FHeight,dx,dy,FWidth,FHeight);
end;
end;
Using the class
Using the class is simple. Initialize it in TApplication.ApplicationStarting method and load in a picture, like this:
FSheet := TW3SpriteSheet.Create;
FSheet.LoadImage('res/sprites.png');
When the actual sprite-sheet image is loaded, the “ready” property will be set to true. So in your PaintView method always check the ready flag before accessing it:
if FSheet.Ready then
begin
FSheet.Draw(Canvas,100,100,10); // draw tile #10
FSheet.Draw(Canvas,133,100,11); // draw tile #11
FSheet.Draw(Canvas,166,100,12); // draw tile #12
end;
Enjoy!

