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!