Animasi 2D mau bagaimana bentuknya pada dasarnya sama saja yaitu menampilkan gambar-gambar secara bergiliran. Untuk membuat animasi pada game 2D pada umumnya, sedikitnya ada tiga class yang terlibat. Ketiga class tersebut adalah:
Class yang merepresentasikan citra/image yang akan dianimasikan. Biasanya disebut sprite. Dalam hal ini adalah TSprite.
Container untuk menampung sprite objects. Container ini juga berperan sebagai sprite manager. Pada contoh code diberi nama TSpriteContainer.
Class yang melakukan animasi. Bisa disebut sprite engine. Pada contoh code disebut TSpriteEngine.
Berikut adalah contoh code yang menampilkan teknik untuk melakukan animasi sederhana. Kelihatannya ribet yah. Mungkin iya karena code berikut adalah implementasi sederhana dari sebuah sprite engine untuk game 2D. Walaupun sederhana, mestinya sih sudah cukup untuk menjelaskan konsep animasi dan memperlihatkan bagaimana animasi berjalan tanpa flicker (bug paling menyebalkan dalam animasi). Contoh berikut terdiri atas dua unit, yaitu Sprite.pas dan Main.pas. Unit Sprite berisi empat buah class, yaitu:
TUtils yang berisi method untuk yang bersifat utilisasi dan dipakai bersama-sama oleh semua class.
TSprite yang merupakan class yang berisi informasi image yang akan digambar.
TSpriteContainer yang berfungsi sebagai sprite container dan sprite manager.
TSpriteEngine yang berfungsi melakukan animasi. Berisi sebuah method, yaitu OpenMouth.
Gambar-gambar yang akan di-raster di-load dan ditampung pada TSpriteContainer. Misalkan keempat file gambar tersebut bernama pac-1.bmp, pac-2.bmp, pac-3.bmp, pac-4.bmp, dan bg.bmp. Semuanya disimpan dalam directory “img\” yang path-nya relative terhadap .exe file.
File Sprite.pas:
unit Sprite;
interface
uses Windows, Graphics, Classes;
const BACKGROUND_PATH = ‘img\\bg.bmp’;
type
TUtils = class public class procedure ReleaseItems(AList: TList); end;
TSprite = class
private FName: string; FImage: TGraphic; FTop: Integer; FLeft: Integer; FWidth: Integer; FHeight: Integer; public
constructor Create(AImgPath: string); destructor Destroy; override;
property Name: string read FName write FName; property Image: TGraphic read FImage; property Top: Integer read FTop write FTop; property Left: Integer read FLeft write FLeft; property Width: Integer read FWidth write FWidth; property Height: Integer read FHeight write FHeigth; end;
TSpriteContainer = class
private FSprites: TList; public constructor Create; destructor Destroy; override; function Add(ASprite: TSprite): Integer; function Remove(ASprite: TSprite): Integer; function GetSprite(ASpriteName: string): TSprite; procedure Clear;
property Sprites: TList read FSprites; end;
TSpriteEngine = class
private FContainer: TSpriteContainer; FScreen: TCanvas; FBackground: TGraphic; public constructor Create(AContainer: TSpriteContainer); destructor Destroy; override; procedure OpenMouth(ADuration: Integer);
property Container: TSpriteContainer read FContainer; property Screen: TCanvas read FScreen write FScreen; end;
implementation
{TUtils}
class procedure TUtils.ReleaseItems(AList: TList); begin while AList.Count > 0 do
begin Dispose(AList.First); AList.Delete(0); end; AList.Capacity := 0; end;
{TSprite}
constructor TSprite.Create(AImgPath: string);
begin FImage := TBitmap.Create; FImage.LoadFromFile(AImgPath);
Fwidth := FImage.Width; FHeigth := FImage.Height; FTop := 0; FLeft := 0; end;
destructor TSprite.Destroy; begin FImage.Free; inherited;
end;
{TSpriteContainer}
constructor TSpriteContainer.Create; begin FSprites := TList.Create; end;
destructor TSpriteContainer.Destroy;
begin FSprites.Free; inherited; end;
function TSpriteContainer.Add(ASprite: TSprite): Integer; begin Result := FSprites.Add(ASprite);
end;
function TSpriteContainer.Remove(ASprite: TSprite): Integer; begin Result := Fsprites.Remove(ASprite); end;
function TSpriteContainer.GetSprite(ASpriteName: string): TSprite;
var i: Integer; sprite: TSprite; begin Result := nil; for i := 0 to FSprites.Count-1 do
begin sprite := FSprites.Items[i]; if sprite.Name = ASpriteName then begin Result := sprite; Exit; end; end;
end;
procedure TSpriteContainer.Clear; begin TUtils.ReleaseItems(FSprites); end;
{TSpriteEngine}
constructor TSpriteEngine.Create(AContainer: TSpriteCOntainer);
begin FContainer := AContainer; FBackground := TBitmap.Create; FBackground.LoadFromFile(BACKGROUND_PATH); end;
destructor TSpriteEngine.Destroy; begin FBackground.Free; inherited; end;
procedure TSpriteEngine.OpenMouth(APosition: TPoint; ADuration: integer); var startTick, deltaTick: Cardinal frameDuration: Cardinal; spriteIndex: Integer; buffer: TBitmap;
begin startTick := GetTickCount; spriteIndex := 0; desltaTick := 0; frameDuration := ADuration div FContainer.Sprites.Count;
buffer := TBitmap.Create; buffer.Width := FBackground.Width; buffer.Height := FBackground.Height;
while (deltaTick <= ADuration) and
(spriteIndex < color="#ff0066">-1) do begin deltaTick := GetTickCount - startTick; spriteIndex := deltaTick div frameDuration;
{ Teknik double buffer, yaitu gambar diolah dulu dimemory sebelum ditampilkan. Teknik ini digunakan untuk menghindari flicker }
buffer.Canvas.Draw(0, 0, FBackground); buffer.Canvas.Draw(APosition.X, APosition.Y, TSprite(FContainer.Sprites.Items[spriteIndex]).Image);
{ Screening } FScreen.Draw(0, 0, buffer); end;
buffer.Free; end;
end. |
Syntax Highlighted with http://delphi-id.org/syntax |
|
Unit Main.pas terdiri dari sebuah form yang berisi sebuah control bertipe TPainBox yang berfungsi sebagai screen, dan sebuah tombol bertipe TButton. Penggalan Main.pas adalah sebagai berikut:
unit Main;
interface
uses Windows, …, ExtCtrls, Sprite;
const IMAGES_NUMBER = 4; PLAYING_DURATION = 200; LOOP_DELAY = 100; X = 200; Y = 100;
type TformMain = class(TForm) PaintBox: TPaintBox; Button1: TButton; procedure Button1Click(Sender: TObject); private … procedure LoadAllImages(AContainer: TSpriteContainer); …
end;
implementation
{ TformMain }
constructor TformMain.Create(AOwner: TComponent); begin inherited; FContainer := TSpriteContainer.Create; { Load semua image yang akan di-raster, Kemas menjadi object bertipe TSprite, kemudian simpan di dalam container menjadi data yang siap raster } LoadAllImages(FContainer);
FSpriteEngine := TSpriteEngine.Create(FContainer); FSpriteEngine.Screen := PaintBox.Canvas;
end;
…
procedure LoadAllImages(AContainer: TSpriteCOntainer); var i: Integer; sprite: TSprite; spriteName: string; begin for i := 0 to IMAGES_NUMBER-1 do
begin spriteName := ‘pac-’ + IntToStr(i+1) + ‘.bmp’;
sprite := TSprite.Create(‘img\\’ + spriteName); sprite.Name := spriteName; AContainer.Add(sprite); end;
end;
procedure TFormMain.Button1Click(Sender: TObject); begin while True do begin FSpriteEngine.OpenMouth(Point(X, Y), PLAYING_DURATION); Sleep(LOOP_DELAY); end;
end;
end. |
Syntax Highlighted with http://delphi-id.org/syntax |
|
Gambar-gambar yang akan dianimasikan adalah:

Yang perlu diingat, code di atas hanyalah prototipe dari sprite engine sebuah dimensi dua yang memperlihatkan bagaimana memanipulasi sprite sehingga bisa menjadi gambar yang bergerak (animasi). Semoga bermanfaat dan silakan dimodifikasi sesuai dengan kebutuhan.
Tags: animasi, game, Object Pascals, pacman, programming, sprite
Posted in Object Pascals | No Comments »
Aku punya info lagi nich yaitu: fungsi untuk mengambil MAC Address dari ethernet card. Fungsinya beri nama GetMacAddress(AEthNumber: Byte). Kembalian dari fungsi ini adalah string dari MAC address ethernet card ber-index AEthNumber. Kalau PC hanya mempunyai sebuah ethernet card, AEthNumber bisa diisi dengan 0 (nol). Fungsi GetMacAddress memanfaatkan unit NB30 bawaan delphi yang berisi beberapa record serta fungsi untuk mengambil informasi network device melalui NetBios.
uses NB30;
function GetMacAddress(AEthNumber: Byte): string; var ncb: PNCB; errorCode: Char; adapter: PAdapterStatus; begin
New(ncb); { Inisiasi isi memory pada pointer ncb dengan char 0 }; FillChar(ncb^, SizeOf(TNCB), 0); ncb^.ncb_command := Char(NCBRESET); ncb^.ncb_lana_num := Char(AEthNumber); errorCode := NetBios(ncb);
{ Reset kembali isi memory pointer nbc dengan char 0 }; FillChar(ncb^, SizeOf(TNCB), 0); ncb^.ncb_command := Char(NCBASTAT); ncb^.ncb_lana_num := Char(AEthNumber); { Harus berisi 16 karakter }
StrPCopy(ncb^.ncb_callname, ‘* ‘);
New(adapter); FillChar(adapter^, SizeOf(TAdapterStatus), 0); ncb^.ncb_buffer := Pointer(adapter); ncb^.ncb_length := SizeOf(TAdapterStatus);
errorCode := NetBios(ncb); if (errorCode = Char(NRC_GOODRET)) or (errorCode = Char(NRC_INCOMP)) then
Result := IntToHex(Ord(adapter^.adapter_address[0]), 2) + ‘-’ + IntToHex(Ord(adapter^.adapter_address[1]), 2) + ‘-’ + IntToHex(Ord(adapter^.adapter_address[2]), 2) + ‘-’ + IntToHex(Ord(adapter^.adapter_address[3]), 2) + ‘-’ + IntToHex(Ord(adapter^.adapter_address[4]), 2) + ‘-’ + IntToHex(Ord(adapter^.adapter_address[5]), 2) else
Result := ‘00-00-00-00-00-00′;
Dispose(ncb); Dispose(adapter); end; |
Syntax Highlighted with http://delphi-id.org/syntax |
|
Begitu saja, semoga bermanfaat. Kali aja ada yang mo ikutan nambahin.