Java2D a dokonalé vykresľovanie
Tento článok bude pojednávať o štýloch vykresľovanie a ako z Javy vyžmýkať to najlepšie Budem predpokladať, že už niečo máte v hlave, teda nebudem základné veci rozoberať dopodrobna.
Tipy na lepší výkon
Akceleruje
Ako prvú vec čo by som urobil pre zvýšenie výkonu našej aplikácie pri vykresľovaní, je zapnutie opengl. Defaultne je toto vypnuté, ale dokáže to zvýšiť fps o stovky, aj keď fps väčšinou u hier obmedzujeme z istých dôvodov, ale o tomto tento článok nehovorí. Takže ako na to? Stačí pri spúšťaní aplikácie dopísať niekam tento riadok:
System.setProperty("sun.java2d.opengl","True");
Poznámka
Toto funguje na Linux hlavne, pri Windows je potreba rovnakým spôsobom vypnúť Direct3D a DirectDraw. (D3D a noddraw)
Antialiasing
Ďalšia vec čo stojí za zmienku je antialiasing. Kto nevie čo je antialiasing tak si to vygooglí, v kocke to text, obrazce a iné veci pekne vyhladí a nie je to potom také kostrbaté. Opäť stačí na to jeden riadok, ktorý využíva metódu triedy Graphics2D / Graphics.
instanceTridyGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Ukážka (hore s antialiasing a dole bez):
antialiasing
Ďalšie postrehy
Ako ďalšie vec čo by som uviedol je metóda, ktorá informuje hardware o tom, že je potrebné prekreslenie, teda lepšie synchronizuje komunikáciu s HW.
Toolkit.getDefaultToolkit().sync();
Túto riadku by som odporučil dávať na koniec metódy, ktorá vykresľuje.
Poznámka
Dôležité hlavne balíkov linux kde sa obrazovka celého systému nepřekresluje nejako veľmi často, pri Windows nie je potrebné používať.
Na vykresľovanie radšej používajte metódu paintComponent, má väčšiu prioritu ako paint a tým pádom je aj rýchlejší.
Poznámka
Ešte rýchlejší je použiť aktívne renderovanie, čo znamená že si urobíme vlastnú metódu na kreslenie, kde si cez metódu komponenty zavoláme o inštanciu Graphics a kreslíme čo potrebujeme.
Pri aktívnom renderovanie treba použiť metódu setIgnoreRepaint, ktorá zapríčiní že sa nebudú volať metódy paint a paintComponent, ktoré by mohli zapríčiniť nejakú nekalosť pri vykresľovaní.
Ak by Vás zaujímalo viac takých "vychytávok", tak odporúčam túto stránku: http://docs.oracle.com/...d/flags.html
Ako vykresľovať
Existuje veľa postupov, možností ako vykresľovať, najlepšie budú najskôr tie čo sú už overené a napísané. Ja začnem od tých podľa môjho názoru najpomalších. Neberte to ako 100% pravdivé informácie, sú to len moje postrehy.
Normálne vykresľovanie
Toto snáď bude každému jasné, proste máme v triede prepísanou metódu paint, ktorú voláme pomocou prekreslenie (), a v nej nejaké tie príkazy na vykreslenie. Na hry to síce nie je najvhodnejšie, ale na jednoduché vzorové programy / hry to stačí.
Page flipping
Moc často sa s tým nestretnete, k dispozícii je to väčšinou len u FSEM (Full-Screen Exclusive Mode) a napríklad bufferstrategy už to má v sebe, takže nie je potrebné o tom niečo vedieť.
Double Buffering
Toto je jeden vôbec z najrozšírenejších spôsobov. Double Buffering by som rozdelil do pár menších podkapitol, pretože spôsobov ako to urobiť je veľa, takže uvediem aspoň pár z nich.
Ako to funguje?
Najskôr nebude na škodu si najskôr vysvetliť ako to vôbec funguje. Princíp je vlastne jednoduchý, najskôr si všetko vyrenderuje (vykreslíme) na nejaký BufferedImage a potom ho ešte len vykreslíme, tým predídeme rôznym blikaním a podivnosť pri vykresľovanie.
Vytvoreniu ideálneho BufferedImage
Tu Vám poskytnem metódu, ktorá ukazuje ako získať ideálne obrázok na renderovanie pre každý počítač.
private BufferedImage toCompatibleImage(BufferedImage image) { GraphicsConfiguration cfg = GraphicsEnvironment. getLocalGraphicsEnvironment().getDefaultScreenDevice(). getDefaultConfiguration(); //pokud již je obrázek ideální tak ho prostě vrátí zpět if (image.getColorModel().equals(cfg.getColorModel())) return image; //pokud není tak ho vytvoříme BufferedImage new_image = cfg.createCompatibleImage( image.getWidth(), image.getHeight(), image.getTransparency()); //a vrátíme ho return new_image; }
Túto metódu by som volal niekde v konstruktoru GUIcka pri inicializované BufferedImage.
Jednoduchý Double Buffering
Toto je úplne jednoduché, v nejakej slučke ktorá nám obstaráva chod hry / aplikácie, budeme volať 2 metódy, render, ktorú si vytvoríme a tam budeme vykresľovať celú hru do nejakého BufferedImage a prekreslenie ktorá nám zavolá paint alebo paintComponent ktorá bude už len vykresľovať ten náš narenderovaný obrázok.
Použitie VolatileImage
Používanie VolatileImage je trošku zložitejšie (ukladá sa do VRAM v grafickej karte) a je zakomponovaný v ďalšom príklade, ale aspoň si ukážeme ako na to.
Najskôr si musíme deklarovať dve dôležité premenné, ktoré budeme hojne používať pri validácii obrázku.
private static GraphicsEnvironment env=GraphicsEnvironment.getLocalGraphicsEnvironment(); private static GraphicsConfiguration conf=env.getDefaultScreenDevice().getDefaultConfiguration();
Ďalej deklarácia obrázka pre render.
private VolatileImage renderImage;
Inicializovať budeme v konštruktory GUIcka takto.
renderImage=createRenderImage(WIDTH,HEIGHT,Transparency.OPAQUE);
//WIDTH a HEIGHT si stanovíte sami ;)
Teraz si budeme musieť vytvoriť tú metódu createRenderImage.
private VolatileImage createRenderImage(int width, int height, int transparency){ VolatileImage image=null; image=conf.createCompatibleVolatileImage(width, height, transparency); int valid=image.validate(conf); if(valid==VolatileImage.IMAGE_INCOMPATIBLE){ image=this.createRenderImage(width, height, transparency); return image; } return image; }
Teraz sa pozrieme ako napísať metódu pre render.
private void render(){ Graphics2D g2=null; do{ int valid=renderImage.validate(conf); if(valid==VolatileImage.IMAGE_INCOMPATIBLE){ renderImage=createRenderImage(WIDTH,HEIGHT,Transparency.OPAQUE); } try{ g2=renderImage.createGraphics(); g2.clearRect(0,0,WIDTH,HEIGHT); //nějaké to vykreslování } }finally{ g2.dispose(); } }while(renderImage.contentsLost()); //dělá dokud se to zprávně neuloží do obrázku }
No a nakoniec si v metóde pre vykreslenie pridáme toto a máme hotovo.
if(renderImage.validate(conf)!=VolatileImage.IMAGE_OK){ renderImage=createRenderImage(WIDTH,HEIGHT,Transparency.OPAQUE); render(); } if(renderImage!=null){ instanceTridyGraphics2D.drawImage(renderImage, 0, 0, getWidth(), getHeight(), null); }
Použitie BufferStrategy
Ako posledné si ukážeme BufferStrategy, podľa môjho názoru aj veľa iných je to najjednoduchšie a zároveň aj najefektívnejšie cesta pre vykresľovanie.
Najskôr musíme BufferStrategy vytvoriť. To možno buď v Frame, JFrame alebo Canvas (možno možno v Dialog / JDialog ale neskúšal som), ak to budete chcieť dostať do JPanel tak jedine cez parameter v konštruktory to poslať.
createBufferStrategy(2); //dvojka znamená že budou 2 buffery, můžeme použít klidně i 3
Po vytvorení sa dá k BS dostať cez metódu getBufferStrategy () a uložiť si do nejakej premennej aby sme ju nemuseli furt volať.
Keď máme BufferStrategy tak nepotrebujeme volať žiadny prekreslenie, alebo iné blbosti. Stačí nám jedna metóda kde sa urobí všetko, vyrenderuje a vykreslí. Takáto metóda by mohla vyzerať nejako takto. (Volať sa samozřejmně bude v nejakej slučke)
private void updateGui() { Graphics2D g2 = (Graphics2D)bs.getDrawGraphics(); g2.setColor(Color.WHITE); g2.fillRect(0, 0, getWidth(), getHeight()); //vykreslení něčeho g2.dispose(); bs.show(); //zobrazení Toolkit.getDefaultToolkit().sync(); }
Koniec
Toto je odo mňa všetko. Dúfam, že pre Vás bude článok užitočný, spísal som snáď všetko čo viem ohľadom tejto témy. Pokiaľ si časom ešte na niečo spomeniem urobím edit Akékoľvek pripomienky, otázky píšte do komentov