Grafika 3D w Visual Basic'u (2)
W drugiej części dokończymy omawianie przykładowego kodu. Zanim do tego przejdę, chciałbym napisać trochę o rozwiązywaniu problemów z kontrolką TrueVision. Praca z tą kontrolką nie różni się od pracy z innymi kontrolkami. Po jej ściągnięciu i rozpakowaniu, trzeba ją zainstalować, najlepiej za pomocą dołączonego pliku
Register Engine.bat. Jeśli mamy zainstalowaną poprzednią wersję, dobrze jest ją odinstaować. Po instalacji uruchamiamy Windows ponownie. Teraz możemy uruchomić VB i stworzyć nowy projekt. Klikamy na Project | References... i zaznaczamy kontrolkę. Jeśli naciśniemy F2 powinniśmy zobaczyć klasy, stałe i typy danych kontrolki. Możemy włączyć okienko Immediate i wpisać
dim a as
Gdy po
as wciśniemy spację, na liście powinny być widoczne klasy kontrolki TrueVision. Gdy otwieramy kod źródłowy programu korzystającego z TrueVision, sprawdźmy, czy kontrolka jest w referencjach. Może się zdarzyć, że przy zaznaczonej kontrolce jest napis MISSING. Wtedy tą pozycję odznaczamy i szukamy kontrolki niżej na liście. Gdy ją znajdziemy, zaznaczmy ją i zamykamy okienko klikając na OK. Teraz kod powinien działać. Nie można też oczywiście zapomnieć o plikach tekstur, jeśli program ich używa. Pliki EXE powinny działać bez problemu, o ile kontrolka jest poprawnie zainstalowana. Ja nigdy nie miałem problemów z kontrolką TrueVision. Mam Win98SE i VB6. Możliwe, że pod starszymi Windowsami i wcześniejszymi wersjami VB wystąpią jakieś problemy. Trzeba wtedy poszukać pomocy na stronie http://www.truevision3dsdk.com. Dosyć często wychodzą nowe wersje kontrolki. Mają one poprawione błędy, dodane nowe funkcje i zwiększoną wydajność. Warto więc ściągnąć najnowszą wersję. Jak zapewne Czytelnicy zauważyli, kontrolka tworzy na dysku plik
debug8.txt
Zapisywane są tam logi z ostatniego użycia kontrolki. Plik ten może się przydać przy szukaniu przyczyny problemu.
Jeśli i to nic nie da, trzeba napisać do autorów kontrolki z prośbą o pomoc. Myślę, że napisanie kilku zdań po angielsku nie będzie trudne, zawsze zresztą można poprosić kogoś o pomoc.
Teraz przejdziemy do dalszego omawiania przykładowego kodu. Tutaj małe sprostowanie: to nie jest gra, jak napisał Naczelny, ale pagórkowata powierzchania, po której można chodzić. Procedura
ChangePos()
jest odpowiedzialna za przemieszczanie się kamery a także steruje trybem wyświetlania obrazu.
GCamera(1) = GCamera(0)
If Inp.IsKeyPressed(TV_KEY_UP) = True And (Inp.IsKeyPressed(TV_KEY_LEFTSHIFT) Or Inp.IsKeyPressed(TV_KEY_RIGHTSHIFT)) Then
GCamera(1).X = GCamera(0).X + Cos(GCamera(0).Ang) * TV.TimeElapsed * 0.3
GCamera(1).z = GCamera(0).z + Sin(GCamera(0).Ang) * TV.TimeElapsed * 0.3
End If
If Inp.IsKeyPressed(TV_KEY_UP) = True And (Inp.IsKeyPressed(TV_KEY_LEFTSHIFT) Or Inp.IsKeyPressed(TV_KEY_RIGHTSHIFT)) = False Then
GCamera(1).X = GCamera(0).X + Cos(GCamera(0).Ang) * TV.TimeElapsed * 0.1
GCamera(1).z = GCamera(0).z + Sin(GCamera(0).Ang) * TV.TimeElapsed * 0.1
End If
If Inp.IsKeyPressed(TV_KEY_DOWN) = True And (Inp.IsKeyPressed(TV_KEY_LEFTSHIFT) Or Inp.IsKeyPressed(TV_KEY_RIGHTSHIFT)) Then
GCamera(1).X = GCamera(0).X - Cos(GCamera(0).Ang) * TV.TimeElapsed * 0.21
GCamera(1).z = GCamera(0).z - Sin(GCamera(0).Ang) * TV.TimeElapsed * 0.21
End If
If Inp.IsKeyPressed(TV_KEY_DOWN) = True And (Inp.IsKeyPressed(TV_KEY_LEFTSHIFT) Or Inp.IsKeyPressed(TV_KEY_RIGHTSHIFT)) = False Then
GCamera(1).X = GCamera(0).X - Cos(GCamera(0).Ang) * TV.TimeElapsed * 0.07
GCamera(1).z = GCamera(0).z - Sin(GCamera(0).Ang) * TV.TimeElapsed * 0.07
End If
If Inp.IsKeyPressed(TV_KEY_LEFT) = True Then
GCamera(1).Ang = GCamera(0).Ang + TV.TimeElapsed * 0.005
End If
If Inp.IsKeyPressed(TV_KEY_RIGHT) = True Then
GCamera(1).Ang = GCamera(0).Ang - TV.TimeElapsed * 0.005
End If
Jak widać na początku kodu, zmienna
GCamera
jest tablicą dwóch obiektów
CameraGravity
. Zmiany są wprowadzane w jednym z tych obiektów, a potem przepisywane do drugiego. Na początku sprawdzany jest stan klawiszy i przycisków myszy. Według ich stanu zmieniana jest pozycja kamery. Jeśli naciśnięty jest Shift, zmiany są większe (większa pręskość). Metoda
TimeElapsed
obiektu
TV zwraca wartość zawierającą ilość czasu, który upłynął od wyświetlenia ostatniej ramki. Dzięki temu szybkość zmian pozycji kamery jest niezależna od wydajności procesora i karty graifcznej. Jeśli jest wyświetlany wolno, pozycja kamey jest zmianiana o większą odległość. Odwrotnie jest przy wydajnej karcie - ruch następuje co krótki okres czasu ale o mniejszy odcinek (lub kąt w przypadku obrotu). Do wyznaczania pozycji wykorzystywane są funkcje trygonometryczne. Jak ktoś chce, może się zagłębiać w sposób ich wykorzystania, ale w praktyce będziemy tylko zmieniać liczbę na końcu liniki, która decyduje o szybkości ruchu i ewentualnie stałe klawiszy.
Dalej mamy oprogramowane dadatkowe klawisze:
If Inp.IsKeyPressed(TV_KEY_S) = True Then
TV.ScreenShot "c:\Screenshot.bmp"
End If
If Inp.IsKeyPressed(TV_KEY_1) Then Scene.SetRenderMode TV_SOLID
If Inp.IsKeyPressed(TV_KEY_2) Then Scene.SetRenderMode TV_LINE
If Inp.IsKeyPressed(TV_KEY_3) Then Scene.SetRenderMode TV_POINT
Klawiszem S możemy wykonać screen. Ścieżkę do pliku można oczywiście zmienić. Klawiszami 1, 2 i 3 zmieniamy sposób wyświetlania obrazu. Odpowiednio są to: normalny, widoczne tylko krawędzie trójkątów, widoczne tylko wierzchołki trójkątów. Dalej jest obsługa klawiszy myszy. Działa analogicznie jak przy klawiaturze.
Inp.GetMouseState MX, MY, b1, b2
If b1 <> 0 Then
If (Inp.IsKeyPressed(TV_KEY_LEFTSHIFT) Or Inp.IsKeyPressed(TV_KEY_RIGHTSHIFT)) Then
GCamera(1).X = GCamera(0).X + Cos(GCamera(0).Ang) * TV.TimeElapsed * 0.3
GCamera(1).z = GCamera(0).z + Sin(GCamera(0).Ang) * TV.TimeElapsed * 0.3
Else
GCamera(1).X = GCamera(0).X + Cos(GCamera(0).Ang) * TV.TimeElapsed * 0.1
GCamera(1).z = GCamera(0).z + Sin(GCamera(0).Ang) * TV.TimeElapsed * 0.1
End If
End If
If b2 <> 0 Then
If (Inp.IsKeyPressed(TV_KEY_LEFTSHIFT) Or Inp.IsKeyPressed(TV_KEY_RIGHTSHIFT)) Then
GCamera(1).X = GCamera(0).X - Cos(GCamera(0).Ang) * TV.TimeElapsed * 0.21
GCamera(1).z = GCamera(0).z - Sin(GCamera(0).Ang) * TV.TimeElapsed * 0.21
Else
GCamera(1).X = GCamera(0).X - Cos(GCamera(0).Ang) * TV.TimeElapsed * 0.07
GCamera(1).z = GCamera(0).z - Sin(GCamera(0).Ang) * TV.TimeElapsed * 0.07
End If
End If
Następnie jest kod sprawdzający kąt nachylenia. Kąt ten musi się zawierać w pewnych wartościach, inaczej po przekroczeniu zby dużej wartości program zaczyna reagowć odwrotnie na ruchy myszy. Można się o tym przekonać, zaznaczając odpowiednią część kodu jako komentarz.
AngY = AngY - (MY / 100)
If AngY > ((22 / 7) * 0.1) Then
AngY = ((22 / 7) * 0.1)
End If
If AngY < -((22 / 7) * 0.8) Then
AngY = -((22 / 7) * 0.8)
End If
GCamera(1).Ang = GCamera(1).Ang - (MX / 100)
Ostatnia liczba w powyższej linijce decyduje o szybkości obrotu kamery przy przesunięciu myszy. Następnie ustawiana jest wysokość kamery. Pobierana jest wysokość powirzchi w danym miejscu i dodawana jest do niej wartość określająca wysokość nad terenem. Tutaj jest to 20, ale można pobawić się z innymi wartościami.
GCamera(1).y = Land.GetHeight(GCamera(1).X, GCamera(1).z) + 20
Teraz pozostaje już tylko przepisać wartości jednego obiektu
CameraGravity
do drugiego i w następnej linijce ostateczne ustawienie pozycji kamery.
Camera(0) = GCamera(1)
Scene.SetCamera GCamera(0).X, GCamera(0).y, GCamera(0).z, GCamera(0).X + Cos(GCamera(0).Ang), GCamera(0).y + Cos(AngY), GCamera(0).z + Sin(GCamera(0).Ang)
Procedura kończy się i wracamy do głównej pętli. Gdy mamy już omówiony przykład, należałoby do niego dodać coś nowego, np. ścianę. W tym celu posłużymy się metodą
AddWall
obiektu
Mesh. W kodzie, w sekcji General dopiszmy więc deklarację:
Public Mesh As New Mesh8
pod deklaracjami w Form_Load:
Set Mesh = Scene.CreateMeshBuilder
a przed linijką
TV.RenderToScreen
wpiszmy:
Scene.RenderAllMeshes
Teraz możemy tworzyć ściany za pomocą obiektu Mesh. Odpowiednią linijkę wpiszmy po instrukcji Land.Optimize:
Mesh.AddWall GetTex("Dolne"), 100, 100, 200, 200, 100, 0, 1, 1
Pierwszy parametr określa teksturę ściany. Musi być ona wcześniej załadowana za pomocą TexFactory.LoadTexture. Następne 2 parametry określają miejsce położenia ściany. Jeśli po uruchomieniu programu nie zmienimy pozycji kamery, parametry te będą decydować o odległości na przód i w lewo od nas. Innymi słowy, im większy będzie drugi parametr, tym początek ściany będzie dalej przed nami, im większy będzie trzeci parametr, tym ściana będzie bardziej w lewo. Podobnie jest w przypadku końca ściany. Szósty parametr decyduje o wysokości ściany. Następny parametr decyduje o położeniu ściany w pionie. Dzięki niemu można "unieść" ścianę. Ósmy i dziewiąty parametr wyznaczają ułożenie tekstury w pionie i poziomie na ścianie. Jeśli oba parametry są ustawione na 1, wówczas tekstura zostanie rozciągnięta na całą ścianę. Gdy parametry będą inne, tekstura zosatnie pomniejszona i na ścianie będzie obok siebie kilka jej kopii. Parametry określają, ile tych kopii w pionie i poziomie ma być. Omawiana metoda ma jeszcze 2 parametry, ale nie będziemy się nimi zajmować. Jeśli chcemy użyć jakiejś innej tekstury, ładujemy ją tak jak inne za pomocą TexFactory.LoadTexture i podajemu jako paramter funkcji GetTex, która przekaże teksturę metodzie Mesh.AddWall.
Aby zrobić poziomą płaszczyznę (podłogę, sufit itp.), posłużymy się metodzieAddFloor obiektu Mesh.
Mesh.AddFloor GetTex("Dolne"), 0, 0, 200, 200, 50, 1, 1
Parametry są podobne. Podajemy teksturę, współrzędne, wysokość i układ tekstury. Teksturę wziąłem z tych używanych dotychczas, ale Czytelnicy mogą oczywiście wybrać inną. Ściana i platforma utworzona w tym przykładzie nie tworzą żadnego konkretnego obiektu, są tylko przykładem. Można oczywiście budować bardziej skomplikowane budowle, to zależy tylko od wyobraźni i karty graficznej.
Kod źródłowy do tej lekcji wraz z teksturami i plikiem exe można sciągnać z http://gnthexfiles.republika.pl. Plik exe i źródła wykorzystują kontrolkę TrueVision w wersji 5.6. Kontrolkę również można ściągnąć z tej strony. W źródłach jest kilka poprawek, exe jest na nowo skompilowany z wykorzystaniem nowej wersji kontrolki. Poprzednia wersja exe nie działała u niektórych osób. Dlatego zalecam ściągnięcie kodów źródłowych ponownie. Nie powinno to zająć na modemie więcej niż 2 minuty. Jeśli ściągniemy nowe źródła, powinniśmy też ściągnąć nową wersję kontrolki, tym bardziej, że ma ona wiele nowych funkcji i usuniętych błędów. Ściągnięcie kontrolki razem ze źródłami nie potrfa na modemie więcej niż 5 minut. Jak wspomniałem, wszystko jest na http://gnthexfiles.republika.pl.
Grzegorz Niemirowski
grzegorz@grzegorz.net
www.grzegorz.net