Ahhh... bas je lepo kada te puste ranije s posla

Prosto uzivam... bazencic, pivce, muzika, lap-top... samo da ga ne ispustim u vodu

Kao sto sam obecao red je da napravimo nasu prvu malu igru. Ovog puta cemo sve sto smo naucili da stavimo u jedan program i videcemo kako je moguce kreirati i brisati objekte na sceni u runtime.
Pocecemo tako sto cemo pobacati GLScene, GLSceneViewer, GLMaterialLibrary i GLCadencer na formu i kameru i svetlo na scenu. Sad dodajmo GLDummyCube na scenu 0, 0, -4 (Scene Objects->Add object->DummyCube) i nazovimo je Tabla. Ovaj objekat je prilicno koristan. Na sceni se vidi kao providna kocka cije su ivice iscrtane isprekidanom linijom, a kada se program pokrene onda se ne vidi. Neko se moze zapitati sta ce nam objekat koji se ne vidi... secate li se kada sam pokazao kako je moguce hijerarhijski postavljati objekte. E, DummyCube se koristi uglavnom za to. Ako postavimo nekoliko lopti, kocki ili bilo kojih objekata da njihove pozicije zavise od DummyCube, pomeranjem i rotiranjem DummyCube svi ti objekti ce se takodje kretati. U kodu cete videti malo bolje o cemu pricam.
Sada nam je cilj da napravimo plocicu koja ce se okretati. Treba da ima jednu stranu obojenu jednom dok su sve ostale obojene drugom bojom. Problem je sto GLCube to bas i ne moze da uradi... ali zato 2 GLCube mogu

Dodajmo jednu kocku i postavimo je u DummyCube. Posto pozicija nase kocke sad zavisi od DummyCube znaci da ce se i nasa kocka naci na 0, 0, -4.
GLCube nam dozvoljava da izaberemo koje stranice kocke ce iscrtavati (Parts property) i mi cemo iskljuciti Back i tada strana koja je okrenuta od nas nije iscrtana. Sada cemo dodati jos jednu kocu u DummyCube i njoj cemo u Parts postaviti da iscrtava SAMO Back stranu tako da ce ona popuniti deo koji prva kocka nije iscrtala. I sada mozemo jednu stranu da obojimo u jednu, a ostale u drugu boju

Samo postavimo materijale kocki na razlicite boje (recimo jedna strana na crveno, a ostale na plavo). Problem je sto sad ne mozemo da vidimo koje je boje deo okrenut od nas... zato cemo malo da zarotiramo nase kocke. Umesto da rotiramo jednu i drugu sve sto cemo uraditi jeste postavljanje ugla za DummyCube. Postavimo TurnAngle na 140 i sta vidimo... obe kocke su zarotirane i sada vidimo i zadnju stranu. Ovaj nacin cemo koristiti u igri da postavljamo i rotiramo polja.
Pa... hajde da pocnemo... obrisimo iz scene sve osim kamere, svetla i DummyCube (koju smo nazvali Tabla). Vratimo TurnAngle Table na 0. I spremni smo da pocnemo

Da bismo uopste mogli da igramo prvo moramo postaviti polja. Napravicemo nam jednu proceduru koja ce obrisati sva postojeca polja na Tabeli i postaviti nova. Proceduru cemo dodati recimo u public deo nase forme
Code:
type
TForm1 = class(TForm)
GLScene1: TGLScene;
GLSceneViewer1: TGLSceneViewer;
GLMaterialLibrary1: TGLMaterialLibrary;
GLCadencer1: TGLCadencer;
GLCamera1: TGLCamera;
GLLightSource1: TGLLightSource;
Tabla: TGLDummyCube;
private
{ Private declarations }
public
{ Public declarations }
procedure NovaIgra;
end;
Prva stvar koju cemo uraditi je brosanje postojecih polja. Posto ce sva polja biti u DummyCube koju smo nazvali Tabla mozemo ih obrisati sve pozivom samo jedne funkcije
Code:
Tabla.DeleteChildren;
Ovo ce obrisati sve objekte koji se nalaze u Tabla objektu. Da nismo koristili DummyCube morali bismo da cuvamo polja u nekom nizu pa da ih brisemo jedno po jedno dok ovako GLScene radi sve umesto nas.
Sledece sto treba da uradimo je da dodamo nova polja kao sto smo vec probali u designtime. Dodacemo u
var deo procedure dve promenljive
Code:
var
Dummy: TGLDummyCube;
Cube: TGLCube;
One ce cuvati objkte koje budemo kreirali. Kad smo pravili polje prvo smo postavili DummyCube... to cemo uraditi i sad
Code:
Dummy := TGLDummyCube.CreateAsChild(Tabla);
Kada ovako kreiramo objekat on ce se nalaziti u objektu koji navedemo prilikom poziva CreateAsChild... znaci nas Dummy ce se nalaziti u objektu Tabla. Ako zelimo da kreiramo objekat koji nije vezan za neki drugi onda umesto CreateAsChild pozivamo samo Create.
Kad smo napravili Dummy treba da dodamo kocku koja ce iscrtavati sve sem Back strane. Da polje ne bi izgledalo previse "kockasto" malo cemo da spljostimo nasu "kocku". To bi izgledalo ovako
Code:
Cube := TGLCube.CreateAsChild(Dummy);
Cube.CubeDepth := 0.2;
Cube.Parts := Cube.Parts - [cpBack];
Ovaj deo svih polja ce biti iste boje pa bi bilo zgodno napraviti material u GLMaterialLibrary koji ce drzati boju za njih. Dodajmo jedan material proizvoljne boje i nazovimo ga "Pozadina". U designtime znamo kako se postavlja material objektima, a evo kako to ide u runtime
Code:
Cube.Material.MaterialLibrary := GLMaterialLibrary1;
Cube.Material.LibMaterialName := 'Pozadina';
Ostaje nam jos samo da dodamo i obojenu stranu. Posto cemo praviti 12 polja to je po 6 parova koji ce imati razlicitu boju pa hajde da dodamo jos 6 materijala razlicite boje u GLMaterialLibrary i nazovimo ih 0, 1, 2, 3, 4 i 5. I obojena strana je skoro gotova
Code:
Cube := TGLCube.CreateAsChild(Dummy);
Cube.CubeDepth := 0.2;
Cube.Parts := [cpBack];
Cube.Material.MaterialLibrary := GLMaterialLibrary1;
Cube.Material.LibMaterialName := '0';
To bi za sad bio deo koji inicijalizuje igru. Cela funkcija bi za sada izgledala ovako
Code:
procedure TForm1.NovaIgra;
var
X, Y: Integer;
Mat: array[0..11] of String;
Dummy: TGLDummyCube;
Cube: TGLCube;
// Izabira material za polje
function IzaberiMaterial: String;
var
I: Integer;
begin
I := Random(12 - (X + (Y * 4)));
Result := Mat[I];
Mat[I] := Mat[11 - (X + (Y * 4))];
end;
begin
// Brisemo sve objekte u Tabla
Tabla.DeleteChildren;
// Postavljamo boje za IzaberiMaterijal
for X := 0 to 5 do
begin
Mat[X] := IntToStr(X);
Mat[X + 6] := IntToStr(X);
end;
// Postavljamo plocice na tablu
for Y := 0 to 2 do
for X := 0 to 3 do
begin
// Dodajemo jedan DummyCube koji ce drzati
// pozadinu (kocku bez jedne strane) i
// zadnju stranu koja je obojena
Dummy := TGLDummyCube.CreateAsChild(Tabla);
Dummy.Position.SetPoint(-3 + X * 2, -1.5 + Y * 1.5, 0);
// Dodajemo pozadinu
Cube := TGLCube.CreateAsChild(Dummy);
Cube.CubeDepth := 0.2;
Cube.Parts := Cube.Parts - [cpBack];
Cube.Material.MaterialLibrary := GLMaterialLibrary1;
Cube.Material.LibMaterialName := 'Pozadina';
// Dodajemo obojenu stranu
Cube := TGLCube.CreateAsChild(Dummy);
Cube.CubeDepth := 0.2;
Cube.Parts := [cpBack];
Cube.Material.MaterialLibrary := GLMaterialLibrary1;
Cube.Material.LibMaterialName := IzaberiMaterijal;
end;
end;
Funkcija IzaberiMaterijal koristi Random da bi odredila boju polja... mogla je da se uradi i na drugi nacin. Ostaje nam da u Form.OnCreate podesimo da se ova procedura pozove i videcemo polja sa zadnje strane ako pokrenemo program
Sada... mogli bi da pocnemo da okrecemo plocice. Posto ce nam kasnije biti potrebno da znamo kad smo okrenuli dva polja dodacemo dve promenljive koje ce cuvati polja koja se okrecu i jos jednu promenljivu koja ce nam reci da li se polja okrecu u datom trenutku
Code:
var
Okretanje: Boolean;
Izabrana1, Izabrana2: TGLDummyCube;
Polje cemo da izaberemo u OnMouseDown eventu GLSceneViewera
Code:
procedure TForm1.GLSceneViewer1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
Obj: TGLCube;
begin
// Ako se nesto desava druge ploce ne mogu da se izaberu
if Okretanje then Exit;
Obj := TGLCube(GLSceneViewer1.Buffer.GetPickedObject(X, Y));
// Uzimamo u obzir samo kocke
if Obj <> nil then
begin
// Ako nismo izabrali ni jednu onda nam je ovo prva
// i pocinjemo sa okretanjem
if Izabrana1 = nil then
begin
// Parent od kocke koja je pozadina je DummyCube koja
// sadrzi i pozadinu i boju pa kada rotiramo
// DummyCube rotira se i pozadina i boja
Izabrana1 := TGLDummyCube(Obj.Parent);
Okretanje := True;
end
else
// Ako je prva vec izabrana pazimo da je nismo izabrali
// ponovo i ako nismo onda je izabrana i druga i
// pocinjemo sa okretanjem
if Izabrana1 <> Obj.Parent then
begin
Izabrana2 := TGLDummyCube(Obj.Parent);
Okretanje := True;
end;
end;
end;
Ovde nema niceg komplikovanog... kada kliknemo na neku kocku proveravamo da li je vec izabrana i ako nije uzimamo vrednost iz Parent propertya sto nam daje objekat u kojem se nasa kocka nalazi tj. DummyCube koji smo kreirali. Tako cemo u Izabrana1 ili Izabrana2 imati DummyCube koji treba da okrenemo da bi se polje videlo. Polje cemo okretati u OnProgress eventu GLCadencera
Code:
procedure TForm1.GLCadencer1Progress(Sender: TObject; const deltaTime,
newTime: Double);
begin
// Okrecemo izabrano polje
if Okretanje then
begin
// Vec smo izabrali drugo polje
if Izabrana2 <> nil then
begin
// Okrecemo ga
Izabrana2.TurnAngle := Izabrana2.TurnAngle + 500 * deltaTime;
// Okrenuo se na zeljenu stranu (ili mozda malo vise)
if Izabrana2.TurnAngle < 0 then
begin
// Resetujemo rotacije i postavljamo tacnu vrednost
// koju zelimo
Izabrana2.ResetRotations;
Izabrana2.Turn(180);
// Okretanje je zavrseno i pocinje pauza da igrac
// vidi sta je uradio
Okretanje := False;
// Ovde cemo proveriti da li su polja ista
end;
end
else
begin
// Okrecemo prvo polje
Izabrana1.TurnAngle := Izabrana1.TurnAngle + 500 * deltaTime;
if Izabrana1.TurnAngle < 0 then
begin
Izabrana1.ResetRotations;
Izabrana1.Turn(180);
Okretanje := False;
end;
end;
end;
end;
I ovde nema niceg komplikovanog

mozete primetiti da sam vrednost sa kojom uvecavam ugao mnozio sa deltaTime. To je dobro zato sto ce i na racunaru koji ima 20 FPS i na onom koji ima 200 FPS rotacija da bude iste brzine. Pokrenimo program i probajmo da okrecemo polja.
Nastavak u sledecem postu
[Ovu poruku je menjao Srki_82 dana 31.05.2005. u 19:32 GMT+1]