Czujniki środowiskowe

Ostatnią grupą czujników są czujniki środowiskowe, czyli takie, które są uruchamiane przez użytkownika nieświadomie, przez przypadek, tzn. do ich uaktywnienia nie potrzebne jest  świadomie działanie użytkownika, np: kliknięcie myszą.

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ć.