Do czujników środowiskowych zaliczamy węzły Collision,
ProximitySensor
i VisibilitySensor.
Collision {
eventIn
MFNode addChildren
eventIn
MFNode removeChildren
exposedField MFNode
children []
exposedField SFBool
collide TRUE
field
SFVec3f bboxCenter 0 0 0
field
SFVec3f bboxSize -1
-1 -1
field
SFNode proxy
NULL
eventOut
SFTime collideTime
}
Collision jest węzłem grupującym określającym właściwości kolizji dla obiektów zdefiniowanych w polu children. Jeśli w pliku VRML nie ma zdefiniowanego węzła Collision przeglądarka domyślnie nie pozwala „przejść przez ściany" wszystkich obiektów. Wyjątkiem są tu obiekty zdefinowane w węzłach IndexedLineSet, PoinSet i Text.
Pole collide odpowiada za włączenie lub wyłączenie wykrywania kolizji z obiektami zdefiniowanymi w węźle Collision. W polu proxy z kolei definiujemy obiekt, który jest niewidoczny, a który tak naprawdę stanowi powierzchnię, o którą się zatrzymamy chcąc dotrzeć do danego obiektu. Jeżeli w polu children nie znajdzie się definicja żadnego obiektu, pole collide będzie miało wartość TRUE, a w polu proxy (typu SFNode) zdefiniujemy jakiś obiekt to nic co prawda nie zostanie wyświetlone na ekranie, ale w ten właśnie sposób możemy definiować kolizje z niewidzialnymi obiektami. Przy zderzeniu z obiektami utworzonymi w polu proxy węzeł również wysyła odpowiednie zdarzenia.
W momencie kolizji, węzeł Collision
wysyła zdarzenie collideTime (typu
SFTime),
które możemy powiązać z innymi zdarzeniami wywołując jakiś efekt, który
ma kolizji towarzyszyć. Jednak aby zdarzenie collideTime
mogło zostać wysłane pole collide
musi mieć wartość TRUE.
A oto przykład zastosowania węzła
Collision (ze
względu na długość kodu źródłowego postanowiłem go tutaj nie przytaczać).
ProximitySensor {
exposedField SFVec3f
center 0 0 0
exposedField SFVec3f
size 0 0 0
exposedField SFBool
enabled TRUE
eventOut
SFBool isActive
eventOut
SFVec3f position_changed
eventOut
SFRotation orientation_changed
eventOut
SFTime enterTime
eventOut
SFTime exitTime
}
Węzeł ProximitySensor jest węzłem czujnikowym, dzięki któremu możemy "uwrażliwić" dany obszar na wkroczenie użytkownika. W chwili gdy użytkownik przekroczy granice zdefiniowanego obszaru zostaje wysłana grupa bardzo przydatnych zdarzeń.
Aktywny obszar, który utworzymy w węźle ProximitySensor jest określony niewidocznym sześcianem, którego środek określamy w polu center (typu SFVec3f), a rozmiar w polu size. Pole enabled służy do włączania lub wyłączania działalności tego węzła. Przy definiowaniu aktywnego obszaru należy pamiętać, że na pole center mają wpływ przesunięcia (translation) węzłów stojących wyżej w hierarchii. Inaczej mówiąc jeżeli węzeł ProximitySensor będzie jednym z "dzieci" węzła Transform to środek aktywnego obszaru znajdzie się nie w punkcie (0, 0, 0) głównego układu współrzędnych, ale w punkcie (0, 0, 0) lokalnego układu współrzędnych utworzonego przez węzeł Transform.
Aby precyzyjnie określić rozmiar aktywnego obszaru możemy najpierw utworzyć półprzeźroczysty sześcian za pomocą węzła Box i dostosować jego rozmiary do pożądanej wielkości. Należy jednak pamiętać aby węzeł Box utworzyć w tym samym układzie współrzędnych co węzeł ProximitySensor.
Kiedy użytkownik wkroczy w zdefiniowaną przestrzeń wysyłane jest zdarzenie isActive o wartości TRUE oraz zdarzenie enterTime, natomiast kiedy użytkownik opuści daną przestrzeń wysyłane jest zdarzenie isActive o wartości FALSE oraz zdarzenie exitTime.
Zdarzenia position_changed i orientation_changed "śledzą" ruch uczestnika świata i wysyłają zdarzenia, które mogą być odczytywane przez inne węzły. Jeżeli węzeł ProximitySensor ponownie przywołamy w innym miejscu (mechanizm DEF/USE) to wtedy aktywna staje się przestrzeń, którą stanowi suma "pudełek" ją wyznaczających.
#VRML V2.0 utf8
Transform {
translation
0 0 0
children
[
Collision {
collide FALSE
children [
DEF Prox ProximitySensor {size 2 2 2}
Shape { appearance Appearance { material
Material { transparency 0.6 }
}
geometry Box {}
}
]
}
]
}
DEF Obiekt Transform {
translation
-10 0 -20
children
[
Shape { appearance Appearance { material
Material { diffuseColor 0 1 0 }
}
geometry Sphere {}
}
]
}
DEF Czas TimeSensor {cycleInterval 5}
DEF Ruch PositionInterpolator
{
key [ 0, 0.25, 0.5,
0.75 , 1 ]
keyValue [ -5 0 -20,
0 5 -20, 5 0 -20, 0 -5 -20, -5 0 -20]
}
ROUTE Prox.enterTime TO Czas.set_startTime
ROUTE Czas.fraction_changed
TO Ruch.set_fraction
ROUTE Ruch.value_changed
TO Obiekt.set_translation
ROUTE Prox.isActive TO Czas.set_loop
Powyższy przykład pokazuje możliwość zastosowania węzła ProximitySensor do wystartowania animacji.
VisibilitySensor {
exposedField SFVec3f
center 0 0 0
exposedField SFBool
enabled TRUE
exposedField SFVec3f
size 0 0 0
eventOut
SFTime enterTime
eventOut
SFTime exitTime
eventOut
SFBool isActive
}
Ten węzeł z kolei "uwrażliwia" określony obszar na spojrzenie użytkownika. Węzeł VisibilitySensor budową bardzo przypomina węzeł ProximitySensor. W obu węzłach identycznie działają zdarzenia oraz pola center, size i enabled.
Skoro nie ma się specjalnie nad czym rozwodzić, przejdźmy do przykładu.
#VRML V2.0 utf8
Transform {
translation 5 0 0
children [
DEF Obiekt Shape
{
appearance Appearance {
material DEF Kolor Material { diffuseColor 0 1 1 }
}
geometry Cylinder { }
}
]
}
Transform {
translation -5 0 0
children [ USE Obiekt
]
}
DEF Obszar VisibilitySensor
{
center 0 0
0
size
5 5 5
}
DEF Czas TimeSensor {}
DEF Czas2 TimeSensor {}
DEF Animacja ColorInterpolator
{
key [0, 1]
keyValue [1 0 1, 0 1 0 ]
}
DEF Animacja2 ColorInterpolator
{
key [0, 1]
keyValue [0 1 0, 1 0 1 ]
}
ROUTE Obszar.enterTime TO
Czas.set_startTime
ROUTE Czas.fraction_changed
TO Animacja.set_fraction
ROUTE Animacja.value_changed
TO Kolor.set_diffuseColor
ROUTE Obszar.exitTime TO
Czas2.set_startTime
ROUTE Czas2.fraction_changed
TO Animacja2.set_fraction
ROUTE Animacja2.value_changed
TO Kolor.set_diffuseColor
W powyższym przykładzie do zmiany koloru byliśmy zmuszeni użyć dwóch węzłów TimeSensor i dwóch węzłów ColorInterpolator. Jedna grupa z tych węzłów odpowiada za zmianę koloru walca z zielonego na różowy a druga grupa odpowiada za działanie odwrotne. Jest to rozwiązanie mało eleganckie, ale ponieważ jeszcze nie potrafimy pisać skryptów i używać węzła Script - jest to rozwiązanie jedyne.
To wszystko w tej części.
Mam nadzieję, że wszytsko było zrozumiałe, i że nie będziecie mieli kłopotów
z utworzeniem własnych, bardziej złożonych światów. Jeżeli jednak jakieś
problemy się pojawią - proszę pisać.