Sprity v DelphiX (C++ Builder) - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:



C/C++

Sprity v DelphiX (C++ Builder)

15. června 2001, 00.00 | Systém komponent DelphiX je určen pro vývojové prostředí Delphi. Jelikož pochází Delphi a C++ Builder ze stejné dílny, jsou oba vývojové nástroje částečně kompatibilní. V tomto článku se podíváme jak pomocí DelphiX v C++ Builderu obsluhovat 'sprite'..

Systém komponent nazvaný DelphiX je odladěn a určen pro vývojové prostředí Delphi. A jelikož pochází Delphi a C++ Builder ze stejné dílny, jsou oba vývojové nástroje částečně kompatibilní. DelphiX nainstalujeme bez větších problému do C++ Builderu. Ale nemáme vyhráno, protože veškeré helpy jsou zde určeny právě pro "Delfaře".

Autor DelphiX velmi kvalitně vyřešil systém pro práci se sprity. Tento systém (engine) spravuje komponenta DXSpriteEngine. Tu umístíme na plochu formuláře a do vlastnosti DXDraw registrujeme okno DirectX. A tím končí veškerá pohoda a práce s formuláři. Na řadu přichází C++, odvozování tříd a jiné velmi příjemné věci (pro ortodoxního céčkaře :)). To by ovšem samo o sobě nevadilo, pokud máme kam nahlédnout a inspirovat se, jak se to či ono dělá. S DelphiX je dodáno spoustu příkladů a mezi nimi krásné dva, určené speciálně pro sprity. Jenomže některé programové konstrukce jsou oproti pascalu hodně odlišné.

A to je příčina vzniku tohoto článku.
Podívejme se nejprve jaké užitečné třídy nám pro práci se sprity DelphiX nabízí:

  • TBackgroundSprite
  • TImageSprite
  • TSprite
  • TSpriteEngine

TSpriteEngine představuje systém, který spravuje všechny sprity pro používaný DXDraw. Jak už jsem zmínil, aby bylo s nimi možno pracovat, je nutné mít TSpriteEngine umístěný ve formuláři. Engine se stará o pozice spritů v daném souřadnicovém systému (nebo prostoru), stará se o jejich pohyb a kolize. Tedy o to, jestli se některé sprity nekryjí.

Základní třídou je TSprite, od které jsou odvozené pro nás velmi důležité TBackgroundSprite a TImageSprite. TBackgroundSprite slouží na vytváření podkladů. S TBackgroundSprite můžeme mapovat na sebe i více podkladů.
TImageSprite je klasika, tuto třídu využijeme nejčastěji.

Obě třídy jsou abstraktní. To znamená, že není vhodné tyto třídy používat přímo, ale je potřeba nejprve z nich definovat odvozené třídy.


TBackgroundSprite

Jak už jsem napsal, na vytváření podkladů slouží TBackgroundSprite. Udělejme si velmi jednoduchý pohyblivý podklad, průlet vesmírem (stejný podklad najdete v příkladu /Samples/Shoot).
Pro tento účel potřebujeme odvodit novou třídu od TBackgroundSprite.

class TScrollBackground : public TBackgroundSprite {
public:
   __fastcall TScrollBackground(TSprite *BSprite);
  
   double FSpeed;
   virtual void __fastcall DoMove(int MoveCount);
};

Tato třída obsahuje pouze konstruktor, proměnou FSpeed a přetíženou funkci DoMove.

Dříve než začneme tvořit podklad potřebujeme k tomu data - sprite, ze kterého sestavíme vesmírný podklad. Příslušný obrázek najdete u přiloženého příkladu. Ten je uložen v souboru sprity.dxg. Obrázek má velikost 64*64 a je rozdělen na několik částí (Pattern), každá část o velikosti 16*16 pixelů. A transparentní barvu nastavenou na černou.
Soubor sprity.dxg načteme přímo do komponenty DXImageList. O komponentě DXImageList si můžete přečíst v sekci Delphi.

Vytvořme si nejprve jeden pohyblivý podklad. Z připraveného obrázku resp. z jeho částí, namapujeme podklad.

__fastcall TScrollBackground::TScrollBackground(TSprite *BSprite):TBackgroundSprite(BSprite) {
   SetMapSize(200, 100);
   Image = Sprity->DXImageList1->Items->Find("vesmir");
   Y = 10; Z = 1;
   FSpeed = 1 ;
   Tile = true;
   for (int i = 0; i < MapHeight; i++) {
   for (int j = 0; j < MapWidth; j++) {
     Chips[j][i] = Image->PatternCount-random(Image->PatternCount/8);
     if (rand() % 100 < 96)
     Chips[j][i] = -1;
   }
   }
}

Podklad namapujeme tak, že funkcí SetMapSize určíme velikost obrázku, který se bude na pozadí opakovat. Náš má velikost 200*100. Tím určíme také velikost pole Chips[][], které jsme zdědili od naší mateřské třídy TBackgroundSprite. Potom načteme do Image náš připravený obrázek "vesmir". V další fázi stanovíme rychlost pohybu FSpeed o jeden pixel.

Obrázek, který se má na pozadí opakovat musíme seskládat z námi připraveného obrázku "vesmir". Jak již jsem říkal, skládá se z částí (pattern), které v obrázku představují hvězdičky. A mi musíme jednotlivé hvězdičky umístit na plochu o velikosti 200*100. K tomu nám poslouží pole souřadnic Chips. Na každou souřadnici můžeme dosadit určitou část z obrázku "vesmír" (jednotlivé hvězdičky). Nechceme-li zobrazit na souřadnici nic, zapíšeme na souřadnici hodnotu -1.

Opakujeme cyklus, v každé smyčce dosadíme náhodně nějakou hvězdičku. Když potom náhodně zvolené číslo ze 100 je menší jak 96, zapíšeme na souřadnici -1 (prázdný prostor). Zůstane nám tak na ploše ideální počet hvězd.

Efektu pohyblivého vesmíru dosáhneme tak, že vytvoříme dva objekty TScrollBackground, každý s jinou rychlostí a jinou souřadnicí Z.
Podklad = new TScrollBackground(DXSpriteEngine1->Engine);
Schválně si zkuste porovnat rozdíly mezi konstruktorem v C++/Delphi.

with TBackgroundSprite.Create(DXSpriteEngine.Engine) do
begin
   SetMapSize(1, 1);
   Image := ImageList.Items.Find('background');
   Z := -2;
   Tile := True;
end;

Konstruktor v Delphi je v tomto případě definován přímo ve funkci, kde je volán (něco jako funkce ve funkci). Pro céčkaře je to věc naprosto nevídaná. S trochou nadsázky by se dalo říct "Pascalisti jsou ale podivíni." :).

Výsledek si stáhněte ZDE.


TImageSprite

Sprity slouží k tomu, abychom z obrázku o nějaké velikosti zobrazili jenom jeho viditelnou část. Protože každá bitmapa má rozměry X*Y, bylo by zobrazení celé bitmapy nesmyslné. Například v nějaké počítačové hře. Pohybující obdelník v jehož středu je nakreslený hlavní hrdina by asi nikoho neoslnil. V DelphiX máme k dispozici objekt TImageSprite, kde načneme obdelníkový obrázek do vlastnosti Image. Tato vlastnost je objekt, který má vlastnost TransparenColor (průhledná barva). Při zobrazení bitmapy bez průhledné barvy dosáhneme výsledného efektu. Pokud poskládáme více takových obrázků vedle sebe dostaneme animaci.
Zobrazit obrázek samozřejmě dokážeme i bez TImageSprite. To je pravda, ale TImageSprite nabízí mnohem víc, sledování kolizí, pohyb a animování.
Spritová animace:

Postup při používání TImageSprite je poněkud odlišný jako u TBackgroundSprite.
Vyzkoušejme si to na jednoduchém příkladě. Ke skrólujícímu vesmíru přidejme tlačítko "KONEC". To se má rozsvítit, když přes něj přejede kurzor myši. Stisknutím tlačítka se program ukončí.

Vycházíme z předešlého programu, navíc musíme odvodit třídu TImageSprite do třídy s názvem TMainSprite podle následujícího kódu:

class TMainSprite : public TImageSprite {
public:
   __fastcall TMainSprite(TSprite *BSprite);

   protected: void __fastcall DoMove(int MoveCount);
   void __fastcall DoCollision(TSprite* Sprite, bool &Done);
};

Třída je velmi jednoduchá, kromě virtuální funkce DoMove přetížíme ještě DoCollision.

Událost DoCollision se vyskytne v případě, že se sprit kryje z nějakým jiným. Pro rozpoznání kolize si určíme indikátor v podobě proměné kolize. Ta nabude hodnoty true, když dojde ke kolizi a nebo false, když ke kolizi nedojde. Ve funkci DoMove si všimněte volání funkce Collision(), toto volání zajistí, aby bylo testováno zda se TMainSprite nepřekrývá s nějaký spritem. Pokud ano, nastává již zmíněná událost DoCollision.

Kolize spritů je závislá na velikost objektu. Kolizní hranice je určena vlastnostmi Width a Height. Nejideálnější je tuto velikost stanovit shodně s velikostí obrázku našeho spritu.

Konec->X = DXDraw1->Width/2 - Konec->Width/2;
Konec->Y = DXDraw1->Height/2 - Konec->Height/2;

V našem příkladu jsem nápis konec definoval jako TImageSprite, aby bylo vidět, že i neodvozenou třídu jde v některých případech také vhodně použít. Testování kolize jsem přenechal odvozené třídě. Jako objekt TMainSprite jsem definoval kurzorovou šipku.

Rada nad zlato:
Hodně často se stává, že používaný obrázek nemá čtvercový okraj (trojúhelník). Když se dostanete se spritem , u kterého je testována kolize, do kolizní hranice (Width a Height), dojde ke kolizi aniž by se sprity překryli. V takovém případě je vhodné nastavit vlastnost PixelCheck na true.

Celý příklad si můžete opět stáhnout zde.

Animace

Do třetice se podívejme na jednoduchou animaci. Vytvoříme spritovou animaci. Tuto animaci načteme do TMainSprite a řekneme programu kolik má animace snímků a jak je jeden snímek veliký:
Meteor->AnimCount = Meteor->Image->PatternCount;
Potom musíme zajistit animování jednotlivých snímků. O to se postará přetížená funkce DoMove.
TImageSprite::DoMove(MoveCount);
Kdyby jsme ji nezavolali uvnitř naší přetížené funkce DoMove, animace by se ani nepohnula.
Opakování animace zajistíme přepínačem AnimLopped = true a rychlost animování stanovíme vlastností AnimSpeed:
Meteor->AnimSpeed = (double) 15/1000;
Tu správnou rychlost si musíte najít sami. Optimální by měla být rychlost 15/1000, což dělá asi 15 snímků za sekundu.

Celý příklad si můžete stáhnout zde.

Pro tentokrát je to vše. Konečně jsme vnesli trochu pohybu a kolizí do naších programů a her. No a to je fajn.

Tématické zařazení:

 » Rubriky  » C/C++  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: