Autor Wątek: Problem z IEventReceiver w Irrlicht  (Przeczytany 803 razy)

Offline Marandil

  • Użytkownik

# Wrzesień 30, 2007, 22:23:26
Witam, ostatnio podczas oprogramowywania pewnego szkieletu gry (ma to być elastyczny szkielet składający się z wielu modułów, połączonych ze sobą, łatwy do wykorzystania w wielu grach różnych typów), który – jak zastrzegam już na samym początku – w zależności od oczekiwanej wydajności / funkcjonalności może korzystać z np. różnych silników graficznych (Irrlicht, OGRE, własny silnik oparty na DX, SDL, itp...). Z wieloma problemami staram się poradzić sobie samemu, ale ostatnio odkryłem pewien problem, który praktycznie zablokował mi możliwości rozwijania jednego z nurtów modułu obsługi zdarzeń. Cały problem polega na tym, iż z nieznanego mi powodu klasa IrrEventReceiver, dziedzicząca po IEventReceiver nie zachowuje się tak, jak powinna. Aby łatwiej było mi tłumaczyć, przytoczę tutaj kod:

        #ifdef USE_IRRLICHT // jeśli zostało zdefiniowane, zostały dołączone nagłówki Irrlichta.
        std::queue<irr::SEvent> irrEvents; // kolejka zdarzeń typu Irrlichta dla wątku obsługującego.
        bool* irrEventsRes; // Używam wskaźnika dla dodatkowej wartości NULL, patrz dalej
        class IrrEventReceiver : public irr::IEventReceiver // Standardowa implementacja odbiorcy zdarzeń silnika Irrlicht
        {
            public:
                virtual bool OnEvent(irr::SEvent ev) // Metoda odbiorcy
                {
                    std::cout << "czas na log! OnEvent" << std::endl; // Log, upewniający mnie, iż otrzymano zdarzenie...
                    EnterCriticalSection(irrCS); // Krytyczna sekcja dla kolejki
                    irrEvents.push(ev); // Dodanie zdarzenia do kolejki
                    LeaveCriticalSection(irrCS); // Koniec CS
                    while(irrEventsRes==NULL) Sleep(10); // Oczekiwanie na rezultat
                    bool result = *irrEventsRes; // pobranie rezultatu z innego wątku
        delete irrEventsRes;
                    irrEventsRes = NULL; // Wyzerowanie wskaźnika.
                    std::cout << "czas na log! OnEvent - koniec" << std::endl; // Log, upewniający mnie, iż przesłano dalej zdarzenie.
                    return result;
                }
        };
        IrrEventReceiver *ier; // obiekt klasy IrrEventReceiver, przekazany dalej do silnika
        #endif

        // Osobny wątek dla obsługi zdarzeń:

        DWORD STD_DECL EventsService(void* nullRef)
        {
            Graph_Eng::DType dt = Graph_Eng::getDeviceType(); // Typ używanego urządzenia (silnika), może to być np. Irrlicht_DX8, czy OpenGL
// teraz interesuje nas tylko część dla silnika Irrlicht
            #ifdef USE_IRRLICHT
            if( ISIRRLICHT(dt) ) // tylko, jeśli używamy Irrlichta.
            {
                irrCS = new CRITICAL_SECTION(); // tworzymy CS dla kolejki...
                InitializeCriticalSection(irrCS); // ... i ją inicjalizujemy
                irr::IrrlichtDevice *irrdev = Graph_Eng::getIrrDevice(); // główny obiekt Irrlichta
                ier = new IrrEventReceiver(); // tworzymy ier...
                irrdev->setEventReceiver(ier); // ... i ustawiamy go jako EventReceivera dla Irrlichta.
                _OnIrrlichtDeviceClose = RegisterEvent("OnIrrlichtDeviceClose"); // rejestracja zdarzeń
                _OnKeyDown = RegisterEvent("OnKeyDown"); // (wewnętrzne, nie będę opisywał)
                _OnKeyUp = RegisterEvent("OnKeyUp");
                while(irrdev->run()) // pętla zaklęta ;P
                {
                    EnterCriticalSection(irrCS); // Wchodzimy w CS koleji
                    if(irrEvents.size() > 0) // tylko, jeśli coś w niej jest...
                    {
                        irr::SEvent e = irrEvents.front(); // Bierzemy z kolejki zdarzenie...
                        irrEvents.pop();
                        LeaveCriticalSection(irrCS); // Opuszczamy CS kolejki
                        //
                        std::cout << "czas na log! Przed switch" << std::endl;
                        std::cout << e.EventType << std::endl; // Logi upewniające mnie, iż obsługujemy zdarzenie w pętli
                        switch(e.EventType) // W zależności od typu wywołujemy różne metody zdarzeń.
                        {
                            case irr::EET_GUI_EVENT:
                                break;
                            case irr::EET_KEY_INPUT_EVENT:
                                if(!e.KeyInput.PressedDown)
                                    DoEvent(OnKeyDown(), new KeyEventArgs(e.KeyInput.Char, false, e.KeyInput.Control, e.KeyInput.Shift)); // Wewnętrzny sposób obsługi zdarzeń (nie opisuję)
                                else
                                    DoEvent(OnKeyUp(), new KeyEventArgs(e.KeyInput.Char, false, e.KeyInput.Control, e.KeyInput.Shift));
                                break;
                            case irr::EET_MOUSE_INPUT_EVENT:
                                break;
                            case irr::EET_LOG_TEXT_EVENT:
                                std::clog << e.LogEvent.Text << std::endl;
                                break;
                            case irr::EET_USER_EVENT:
                                break;
                            default:
                                break;
                        }
                        irrEventsRes = new bool(irrdev->getSceneManager()->getActiveCamera()->OnEvent(e)); // przekazanie zdarzenia dalej (do kamery)
                    }
                    else
                    {
                        LeaveCriticalSection(irrCS); // Wychodzimy z CS kolejki
                        Sleep(10); // Czekamy chwilę, na nowe zdarzenia...
                    }
                }
                DoEvent(OnIrrlichtDeviceClose()); // na koniec rzucamy zdarzeniem, iż skończyliśmy działanie
                delete ier; // i sprzątamy po sobie.
                DeleteCriticalSection(irrCS);
            }
            #endif
// Inne silniki itp...
// ...
        }

Problem polega na tym, iż tak naprawdę, OnEvent przez całe życie aplikacji zostaje wywołany tylko raz (otrzymuje jakąś mało ważną wiadomość typu EET_LOG_TEXT_EVENT). Nie mam zielonego pojęcia, czemu tak się dzieję, ale jeśli ktoś miałby jakiś pomysł, proszę o pomoc. Aktualnie musiałem się zająć innym miejscem w tym szkielecie, ale miałbym zamiar wrócić do EventModule najszybciej, jak się da, a bez działającego EventReceivera ani rusz!
Należy się z mojej strony jeszcze słowo wyjaśnienia na temat rozdzielenia IrrEventReceivera i EventsService... Otóż EventsService należy do zupełnie innego wątku, którego zadaniem jest jedynie pobieranie i wysyłanie do systemowej (mojej) kolejki zdarzeń metodą DoEvent(..).  Planowana jest także poprawa wydajności, ponieważ przesyłanie zdarzenia do kamery ma zostać przeniesione do OnEvent, gdzie nie będzie już wykorzystywane oczekiwanie na rezultat, dzięki czemu będzie można bezproblemowo żonglować zdarzeniami w kolejkach, bez zbędnego oczekiwania. Wtedy EventsService dla Irrlichtowych Device'ów sprowadzi się tylko do konwersji z jednego typu na inny.

PS.
Podczas pisania tego posta doszedłem do wniosku, iż z moimi problemami może być związana instrukcja break w switchu dla typów zdarzeń, ale odrzuciłem ten pomysł, kiedy przypomniałem sobie, iż bezproblemowo działało także zwracanie wartości przez wskaźnik, które by przecież zostało pominięte (OnEvent wpadło by w nieskończoną pętlę, a nic się takiego nie działo)
« Ostatnia zmiana: Wrzesień 30, 2007, 23:13:53 wysłana przez Marandil »

Offline Mr. Spam

  • Miłośnik przetworów mięsnych