Wstęp

edytuj

W tej lekcji głównymi tematami będą:
-ustawienie światła
-ustawienie kamery
-poruszanie kamerą (nasz gracz)
-napisanie nieba
-napisanie ziemi
Niedużo, ale też i nie mało. W tej lekcji poznasz także podstawy języka Blitz3D.

Światło

edytuj

Pierwszą rzeczą będzie światło. Ale przed dodaniem światła musimy poinformować kompilator o wielkości okna i w jakim buforze będziemy rysować. Czyli powinno to wyglądać tak:

;graphics
Graphics3D 640,480,32,1
SetBuffer BackBuffer()

Yes, yes, yes! Od razu widać, jaki łatwy jest Blitz3D! Tak mało linijek kodu, a będzie tryb pełnoekranowy! :) Dla porównania, uczę się C++ od paru miesięcy (ok. 3?), a jeszcze nie umiem ustawić tego trybu. Czemu? Ponieważ C++ jest bardzo trudny do nauczenia. Jeśli to czytasz, i chcesz się nauczyć Blitz3D, to Ci powiem, że to właściwy wybór! Ten język jest niezwykle łatwy i dla początkującego po prostu świetny! Niewielu nawet wie, że w tym języku, napisano Maluch Racer'a! Ale zajmijmy się kodem :) ;graphics to komentarz. Komentarze umieszczamy po znaku ;. Graphics3D mówi kompilatorowi, że ma do czynienia z grafikami/grą (jak kto woli) trójwymiarową. Natomiast liczby 640,480,32,1 to po kolei: szerokość okna, wysokość okna, głębia kolorów, no i tryb pełnoekranowy. Gdybyś nie chciał tego trybu, możesz napisać tylko i jedynie 640,480. A jak wiesz oznaczałoby to: okno o szerokości 640 pikseli i o wysokości 480 pikseli. Tutaj zamieszczam krótką listę tych wymiarów (kto wie, może się przyda):

320x200
520x340
640x480
800x600
1024x768
1280x1024

Co prawda kompilator przyjmie np. 500, 500, ale najlepsze do użycia są dwa wymiary: 640,480 800,600 Trochę się rozpisałem, a jeszcze nie mamy najważniejszego w tej chwili elementu. Światła. Za światło będą odpowiedzialne kolejne trzy linijki tekstu:

;light
Global light = CreateLight()
RotateEntity light,90,0,0

Wiesz, co to komentarz, więc wytłumaczę na razie te dwa słówka: Global light. Global to w potocznym tłumaczeniu globalny. Czyli nasze światło, będzie obecne przez całe działanie gry. light to nazwa światła. Równie dobrze mogłaby brzmieć swiatlo albo nfjnjbfj :) To po prostu nazwa. CreateLight() (czyli StwórzŚwiatło()) oznacza stworzenie światła :) A ostatnia linijka? Sprawia, że obraca nasze światło. Chyba jest przydatne, skoro wtedy światło "rozejdzie się" po całym naszym świecie. Po RotateEntity piszemy nazwę światła (w naszym przypadku light). A następnie piszemy ile obracamy. 90,0,0 to po kolei x,y,z. X oznacza długość, Y wysokość, a Z oddalenie od kamery. Więc mamy nasze światło, ale po skompilowaniu (klawisz F5) program samoistnie się zakończy :( Na szczęście, jeszcze w tej lekcji nauczysz się co zrobić, aby program się tak nie kończył :) Ale wpierw kamera.

Kamera

edytuj

Nie martw się. Tym razem nie będę się rozpisywał (choć to zależy od punktu widzenia). Od razu podaję kod:

;camerapivot
camera_pivot=CreatePivot()
EntityRadius camera_pivot,1.4
EntityType camera_pivot,PLAYER

Przybyła jedna linijka tekstu :) No dobra, jeśli załapałeś ten fragmencik o świetle, to druga linijka nie powinna ci sprawić kłopotów. camera_pivot to nazwa, CreatePivot() stworzy pewną część kamery. Ta pewna część odpowiada za to... jak to ująć? Mam nadzieję, że zrozumiesz: Dzięki tej części kodu będziemy mogli poruszać kamerą, stworzyliśmy naszego gracza. Więc kolejna linijka. EntityRadius camera_pivot,1.4 ta część jest odpowiedzialna za kolizję. Znaczy, że gdy nasz gracz (no, prawie kamera) dotknie np. budynku po prostu się zatrzyma, czy coś w tym stylu (odbije się...). EntityType camera_pivot,PLAYER. A! To bardzo ważna część. EntityType to funkcja, która mówi do której kategorii (typu) kolizji należy dany obiekt. Potem umieszczamy nazwę (np. camera_pivot) i typ. Tutaj będzie to PLAYER. I w związku z tym, musimy coś napisać, po kodzie odpowiedzialnym za światło, ale przed kodem, odpowiedzialnym za naszego gracza. Krótko mówiąc, po środku tych dwóch części kodu :)

;Collision Group
Global PLAYER = 2

Ustaliliśmy tutaj globalne dla PLAYER. Bez tego nasz gracz nie mógłby się zderzyć i takie tam :) OK, teraz konkretna rzecz, kamera:

;camera
Global camera = CreateCamera(camera_pivot)
CameraRange camera,1,1000
CameraFogMode camera,1
CameraFogRange camera,60,200

Przy okazji, to wiesz, jak łatwo sprawdzić, czy dobrze wpisałeś funkcję? Jeśli np. funkcja CameraFogMode (i jeśli masz kompilator Blitz3D, nieważne czy demo czy full) podświetli Ci się na błękit, tzn. że dobrze wpisałeś funkcję. Jeśli jest inaczej, tzn. że źle. Wróćmy może do kodu. camera to rzecz jasna, nazwa. CreateCamera stworzy kamerę. A to co umieściliśmy w nawiasie [(camera_pivot)], to tak zwany, parent (rodzic). Gdybym miał wytłumaczyć to nieprofesjonalnymi słowami, brzmiałoby to tak:
camera_pivot, które umieściliśmy w nawiasie działa jako takie przyłączenie się do kamery. To połączenie kamery z graczem. Czyli dzięki temu też będziemy mogli poruszać kamerą (do poruszania kamerą, będzie służyć jeszcze pewna część kodu, którą poznamy niedługo).
Dalsze parametry odpowiadają, za między innymi, jak dużo ma być zamglona nasza kamera (CameraFogMode i CameraFogRange). Tak oto stworzyliśmy kamerę i światło. Jednak nasz świat gry jest nadal pusty. Lecz już za chwilę przeczytasz co zrobić, aby nie był taki pusty.

Po przeczytaniu tego podtematu i skompilowaniu gry wyświetli Ci się śliczne niebo. Ale powoli. Plik sky.bmp możesz pobrać z zasobów, które znajdują się w spisie treści. Więc podaję kod:

;sky
Global sky = CreateSphere(16,camera_pivot)
FlipMesh sky
ScaleEntity sky,100,100,100
PositionEntity sky,0,50,0
sky_tex = LoadTexture("sky.bmp")
EntityTexture sky,sky_tex

Przybyło parę nowych rzeczy. CreateSphere' tworzy kulę, nasze niebo. 16 oznacza z iloma segmentami będzie kula. camera_pivot to parent. Pisałem już o parentach, więc nie będę się powtarzał :). Bez FlipMesh niebo "ukrywałoby się", byłoby niewidoczne, gdybyś chciał poruszyć kamerą. A co dokładniej robi ta funkcja? Przerzuca siatkę nieba. Zauważ, że poprzednie zdanie, jest prawie dokładnym tłumaczeniem funkcji FlipMesh sky. Dowodzi to jednego. Że Blitz3D przypomina niby-opis gry. Czyli dokładne tłumaczenie, bez żadnych skomplikowanych funkcji. Dlatego jest to bardzo łatwy język. ScaleEntity poszerzy niebo o 100 w każdą stronę (x,y,z). PositionEntity ustala pozycję nieba. Najbardziej odpowiednią jest powyższa. 50 stopni po osi Y. Kolejne dwie linijki umieszczają teksturę. Najpierw podajemy nazwę tekstury (sky_tex), potem wpisujemy LoadTexture, co załaduje teksturę, a w nawiasie dajemy nazwę tekstury wraz z rozszerzeniem (.bmp). Jeśli chodzi o grafiki 2D (w Blitz3D można pisać też gry 2D, czego też nauczysz się w tym kursie) to najlepiej zapisywać takie grafiki w formacie .bmp. EntityTexture mówi do czego ma być przyłączona tekstura. Tutaj będzie przyłączona do nieba, czyli sky a potem sky_tex. Pamiętaj też, że kompilator jest bardzo czuły na wielkość liter. Jeżeli wpisałbyś, np. sky_tex, a gdzie indziej sKy_TeX dla kompilatora byłby to całkiem inne, dwie nazwy. Skompiluj grę; powinieneś zobaczyć nasze niebo. Albo i nie :)

Ziemia

edytuj
Global terrain = LoadTerrain("height2.jpg")
TerrainDetail terrain,2500,True
ScaleEntity terrain, 3,15,3
PositionEntity terrain,-1000,0,-530
ter_tex = LoadTexture("mossyground.bmp")
EntityTexture terrain,ter_tex
EntityRadius terrain,0.2
EntityType terrain,SCENERY

Załadowaliśmy plik "height2.jpg" do zmiennej terrain komendą LoadTerrain. Ten plik jest poprostu rysunkiem przedstawiajacym wszystkie wysokości , wgłebienia w terenie ,ktory umieszczamy w grze.Jezeli ktos chce zobaczyc jak wyglada ,to ujrzy biało-czarne linie ,gdzie : biały to wysokość ,czarny to zaglebienie a szary to stromość.Komenda TerrainDetail ustawia nam ,ze nasz teren bedzie skladal sie z 2500 Vertexów .ScaleEntity powoduje ,ze nasz teren został powiekszony o 3 razy wzdluz osi x , o 15 razy wzdluz osi y i o 3 razy wzdluz osi z. Tak wiec nasz teren bedzie ogromny.Nastepnie umiejscowiamy teren na odpowiednich wspolrzednych . Teraz za pomoca komendy LoadTexture ładuemy nasza texturę w pliku "mossyground.bmp" i przypisujemy ja do zmiennej ter_tex .Texturujemy nasz teren stworzona wczesniej textura za pomoca EntityTexture .EntityRadius sluzy do wygenerowania jak dlugi promien ze srodka obiektu ma wlasnie ten obiekt . Tutaj ustawilismy na 0.2. Bedzie to nam sluzylo pozniej do wykrywania kolizji .EntityType przydziela teren do grupy SCENERY ,co nam posluzy do wykrywania kolizji w przyszlym czasie.

Grupy kolizji

edytuj
Global SCENERY = 1
Global PLAYER = 2

Teraz tworzymy dwie zmienne globalne o różnych watościach tak ,żeby można było łatwo zdefiniować która grupa przedmiotów koliduje z którą. Do każdej z tych grup możemy przypisać dowolne obiekty,ale na razie pozostawmy eksperymenty na deser.

 
PositionEntity Camera_pivot 0,10,120

Ten fragment kodu umiejscawia nam pivot 10 stopni wyzej i 120 do przodu .Pamietacie ,ze wraz z Pivotem porusza sie takze kamera i niebo ,gdyz pivot jest ich parentem ,a one sa jego dziecmi, czyli robia wszystko to co pivot.

Pętla

edytuj
While Not KeyHit(1)
If KeyDown(200) MoveEntity camera_pivot,0,0,0.05
If KeyDown(208)MoveEntity camera_pivot,0,0,-0.05
If KeyDown(203) TurnEntity camera_pivot,0,2,0
If KeyDown(205)TurnEntity camera_pivot,0,-2,0

UpdateWorld
RenderWorld
Flip

Wend 
End

Teraz najważniejszy fragment kodu ,czyli petla . Słowo While powoduje powstanie petli ,a Not i KeyHit(1) to sa warunki i parametry dodatkowe. Keyhit(1) samo w sobie oznacza przycisniecie klawisza 1 .W spisie kodów klawiszy jest to ESC . Wyraz Not jest to operator logiczny,ktory ma za zadanie odwracac wartosci i wyrazenia .Czyli jezeli cos ma wartosc prawda to Not odwraca to na false lub jezeli cos ma 1 to Not odwraca na 0 .Teraz wyjasnie działanie tego kodu . Petla bedzie wykonywala tak dlugo zadania zawarte pod nia (o tym zaraz powiem),jezeli bedzie przyciskany klawisz ESC .Ale jakto ESC ? Keyhit(1) po nacisnieciu zwraca wartosc 1 czyli warunek zostaje spelniony i petla pracuje .Ale nam chodzi o to zeby po nacisnieciu ESC petla zakonczyla swoje dzialanie .No wlasnie ,zapomnielismyo operatorze Not ktory odwraca wartosc KeyHit z 0 (czyli nie- wcisniety klawisz) na 1 (czyli wcisniety ,ale na pozor) i w ten sposob petla mysli ze przycisk jest wcisniety i dziala dalej . Teraz omowie zadania petli . Wyrazenie If rozpoczyna petle warunkową . Dalej Keydown(200) oznacza wcisneicie przycisku :200 = strzalka do góry. Czyli jesli zostanie wcisniety klawisz 200 to MoveEntity(porusz przedmiot) camera_pivot o odpowiednei wspolrzedne,tutaj tak zebys poruszal sie do przodu .Dalsze warunki If oznaczaja to samo ale dla wcisniecia klawiszy : strzalka w lewo ,prawo i tył . Dajmy na to ze wciskasz strzalke w prawo ,to jezeli warunek pierwszy(strzalka w gore)nie zostaje spelniony to program przechodzi dalej ,az dochodzi do strzalki w prawo i wykonuje polecenia dla tego fragmentu kodu .UpdateWorld i RenderWorld sa funkcjami specjalnymi programu i aktualizuja wszystko i dbaja o plynnosc programu.Flip przelacza z tylnego buforu na przedni ,czyli to co bylo schowane przechodzi na widocznosc.Wend to zaznaczenie konca petli. End konczy prace programu.