Leírás

3D grafikával kapcsolatos bejegyzések és cikkek. A Quadron Virtual Particle nevű game engine fejlesztése.

Stuff

Anything related to game development.

Contact: darthasylum at gmail dot com

Nemlineáris cikkek

Mi ezeknek a célja?

Leginkább az, hogy magyarul is elérhető legyen programozási anyag, olvasmányos formában.

A cikkek kódja GitHub-ról is elérhető. (*)-al jelölöm azt a cikket ami nemrégiben frissült.

A színek a nehézséget próbálják jelezni, de az, hogy egy cikk piros nem azt jelenti, hogy csak a profiknak szól!

 

 

 

 

 

 

 

 

Hörcsög

The Asylum

3D grafikával kapcsolatos bejegyzések és cikkek. A Quadron Virtual Particle nevű game engine fejlesztése.

Friss topikok

Címkék

HTML

Fókusz menedzsment

2013.05.21. 22:45 | darthasylum | Szólj hozzá!

A feladat a következő: adott egy modelviewer, amiben lehet forgatni a kamerát az egér lenyomva tartásával. A modelviewernek viszont van GUI-ja is (az engine-el rajzolva), ami szintén az egérrel működik. Hogyan lehet biztosítani a kölcsönös kizárást (az egeret csak egy valami birtokolhassa egy adott időben)? Kérdezek jobbat: hogyan lehet adott esetben megkerülni a kölcsönös kizárást?

Az elvárások a vázolt esetben az alábbiak:

  • a GUI elsőbbséget élvez
  • a kamera klikkelésre kap fókuszt, a GUI viszont egérmozgatásra (hover)
  • ha a kamera birtokolja a fókuszt, akkor a GUI nem reagál semmire
  • ha a GUI birtokolja a fókuszt, akkor a kamera nem reagál

Az eddigi megoldás ilyen hackelés szerű volt: a kamera értesítette a GUI-t, hogy megszerezte a fókuszt, a GUI ilyenkor letiltotta magát. A GUI értesítette a főprogramot, hogy megkapta a fókuszt, a főprogram letiltotta a kamerát (meg mást is csinált, de feleslegesen).

Egy másik feladatban kicsit más a helyzet:

  • a GUI és a kamera viszonya ugyanaz
  • de van egy harmadik elem, egy nagyító
  • a nagyító kizárja a GUI-t
  • a GUI kizárja a nagyítót
  • a nagyító a kamerával együtt tud működni

Több feltételt is teljesíteni kell tehát, másrészt könnyen jöhet olyan eset, hogy sok objektum birtokolhatja a fókuszt és akkor a 0. megoldás követhetetlen.


1. megoldás: event ignore

A legtöbb ilyen checkbox1_clicked() esemény szignálokkal van megoldva. A probléma az, hogy a szignálok tüzelését nem lehet megszakítani. Ha meglehetne, azzal egy objektum kisajátíthatja a fókuszt. Ez egy (nagyon) rossz megoldás, egyrészt mert az összes helyen át kell írni a slotokat, hogy a szignált ignorálni lehessen (akár visszatérő értékkel, vagy 0. paraméterként átadva), másrészt ez egy óriási korlátozás az engine használója felé (bár megkerülhető).


2. megoldás: fókusz objektum

Egy singleton, amit egy lock-hoz hasonlóan meg lehet fogni. Alapvetően jó ötlet, de a fókusz megosztása nehézkes. Miben tároljam? Multimap? Dictionary? Hashtable? Akármelyiket is választom "lassú" lesz. Nyilván kell tartani azt is, hogy épp kik birtokolják a fókuszt (ez megint overhead).


3. megoldás: osztott nyomkövetés

Végülis ezt csináltam meg mert elég egyszerű megcsinálni, és rugalmas. Csináltam egy Focusable nevű osztályt, aminek a lényege annyi, hogy a nyilvánvaló Acquire() és Unacquire() metóduson kivül van neki egy focuschanged<bool> szignálja és egy onfocuschanged(bool) slotja. A member változók pedig: hasfocus, canfocus, refcount.

Minden olyan osztály ami tud fókuszt szerezni, az ebből kell származzon. A kölcsönös kizárás biztosítása a programozóra van bízva, mégpedig úgy, hogy az egyik objektum szignálját ráköti egy másik slotjára (amennyiben az egyik kizárja a másikat).

Na de hogyan is működik és miért működik?

Focusable::Focusable()
{
    hasfocus = false;
    canfocus = true;
    refcount = 0;
}

void Focusable::AcquireFocus()
{
    if( !hasfocus )
        focuschanged(hasfocus = true);
}

void Focusable::UnacquireFocus()
{
    if( hasfocus )
        focuschanged(hasfocus = false);
}

void Focusable::onfocuschanged(bool otherhasfocus)
{
    if( otherhasfocus )
        ++refcount;
    else if( refcount > 0 )
        --refcount;

    canfocus = (refcount == 0);
}

Bár az Acquire()-be be lehetne rakni a canfocus-t is feltételnek, nem raktam be. Ezek után a fenti nagyítós példára a következőket kell csinálni:

form->focuschanged.connect(&camera, ...);
form->focuschanged.connect(magnifier, ...);
magnifier->focuschanged.connect(form, ...);
camera.focuschanged.connect(form, ...);

A ... mindenhol &Focusable::onfocuschanged (nem fér ki)...

Annyi megkötés van (de ez elég nyilvánvaló), hogy a mousedown eseményre először a GUI-t kell rákötni (hogy ha tudja, akkor ő kaphassa meg először a fókuszt).

viewer1.jpg
Ez nem a nagyítós példa

Ez a megoldás tűnik a legjobbnak, és semmiben sem korlátoz (ha nem akarod használni, nem használod). Memóriaügyileg elhanyagolható, teljesítményben pedig a szignál implementációjától függ (ami viszont már egy létező objektum, így plusz teljesítményromlás nincs).

A bejegyzés trackback címe:

https://darthasylum.blog.hu/api/trackback/id/tr35311941

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása