koodi vinkki, joissa kaksoisbufferointi on mahdollisimman yksinkertaisesti esitetty, siis vaikka pallo kulkee laidasta laitaan(ei mielellään Aplettia).
Olisin kiitollinen, jos joku (esim. Sami) jaksaisi koodata tällaisen ja olisi kirjoittanut vielä niitä kommentteja käskyihin ynnä muuhun. Niitä aiempia koodivinkkejä on vähän vaikeampi hahmottaa, koska lauseiden kommentit perustuu lähinnä sisältöön, eikä Java käskyjen toimintaan...toki on hyvä, että sisältö kerrotaan huolella, mutta ois kiva, jos myös näiden käskyjen toimintaa selitettäisiin vähän.
Toinen koodivinkki olisi hyvä säikeiden toiminnasta. Ja mieluusti tästäkin pelkistetty ja yksinkertainen kommenttien kera, niin että niitä olisi helpompi lukea.
Kiitos etukäteen, jos joku vaan jaksaa koodata.
Kaksoispuskurointi on melko yksinkertaista toteuttaa. Käytännössä se menee niin, että ylikirjoitat update-metodin ja et suoritakaan piirtämistä suoraan piirtopinnalle, vaan piirrät ensin puskuriin ja lopuksi piirrät drawImagella puskurissa olevan kuvan näytölle. Appletin tai minkä tahansa muun piirtopinnan kaksoispuskurointi käy käytännössä täsmälleen samalla tavalla.
Koodina kaksoispuskurointi näyttää suunnilleen tältä:
import java.awt.*; public class Buffer extends Frame { int x, y; public Buffer() { // Ei mitään sen kummempaa, kuin että asetetaan ikkunan koko ja ikkuna näkyväksi this.setSize(500, 500); this.setVisible(true); // Silmukka jossa liikutetaan palloa vasemmalta oikealle ja joka kierros piirretään pallo uudestaan y = 250; for (x = 0; x < 500; x += 5) { repaint(); } System.exit(0); } public void paint (Graphics g) { int width = this.getWidth(); int height = this.getHeight(); // Pitäisi varmaan osata selittää järkevästi, mitä seuraavilla kahdella rivillä tehdään, mutta kun ei vaan osaa... // Ensiksi kuitenkin luodaan uusi kuva puskuriksi (bufferImage) Image bufferImage = createImage(width, height); // Ja sitten luodaan uusi piirtopinta (buffer), joka piirtää bufferImageen Graphics buffer = bufferImage.getGraphics(); //Kaikki piirtäminen tapahtuu g:n sijaan bufferiin buffer.setColor(Color.GREEN); buffer.fillOval(x-25, y-25, 50, 50); // Lopuksi vain piirretään puskurikuva g:hen. g.drawImage(bufferImage, 0, 0, this); } // Ylikirjoitetaan update-metodi siten, että se ei tyhjääkään piirtopintaa ennen uuden kuvan piirtämistä public void update(Graphics g) { paint(g); } public static void main(String[] args) { Buffer baa = new Buffer(); } }
Koodi ja kommentit on tehty keskellä yötä, joten siinä saattaa olla jotain hullua, mutta ainakin se kääntyi ja näytti toimivan ihan oikein :)
Kiitoksia Sami erittäin selkeästä esimerksitä:)
esimerkki toimii täydellisesti, mutta viive vaan on unohtunut.
Laitoin sinne väliin omanlaisen viiveen:
for (int viive = 0; viive < 10000000; ++viive){}
mutta ei toi näytä olevan kauheen tehokas javassa, vai onko. Onko muita tapoja laittaa viivettä?
Thread.sleep(long time); (+ try-catch)
Enkä varsinaisesti unohtanut sitä viivettä, sillä ainakin itselläni kuvan piirtäminen puskurista näytölle vie sen verran pitkän ajan, että se riittää viiveeksi.
Vielä ois yks juttu:
Tiedätkö mistä johtuu se, että pallo liikkuu pätkittäin vasemmalta oikealle, eikä sulavasti. Kuinka sen voisi korjata?
Laita pienempi horisontaalinen muutos kerralla, ja säilyttääksesi saman muutosnopeuden, pienennä nukkumisaikaa.
Paulus M:
for (x = 0; x < 500; x += 5) { repaint(); }
-->
for (x = 0; x < 500; x += 1) { repaint(); }
ei se silti vielä lopeta kokonaan tökkimistä. Luin yhdestä kirjasta jossa sanottiin, että javassa pitäisi käyttää säikeitä animaatioita tehdessä, varsinkin jos käytetään takaisin kutsuttavia funktioita?
Tökkiminen varmaan johtuu siitä, että repaint ei tarkoita sitä, että siinä kohtaa piirretään kuva uudestaan. Se tarkoittaa ainoastaan, että käyttöliittymä tulisi piirtää uudestaan kun siihen seuraavan kerran tulee tilaisuus. Tästä syystä x todennäköisesti kasvaa ruutujen välillä paljon enemmän kuin yhden askeleen. Nykiminen riippuu sitten siitä kuinka monta kertaa tuo silmukka ehtii pyöri ennen kuin tulee käyttöliittymää päivittävän threadin vuoro.
Nykimisen saanee pois, jos piirtosimukassa suhteuttaa liikkeen aikaan tai lisää pienen viiveen (Thread.sleep(20)) tuohon for silmukkaan.
lainaus:
Tökkiminen varmaan johtuu siitä, että repaint ei tarkoita sitä, että siinä kohtaa piirretään kuva uudestaan. Se tarkoittaa ainoastaan, että käyttöliittymä tulisi piirtää uudestaan kun siihen seuraavan kerran tulee tilaisuus.
Miten tämän repaint() funktion saisi sitten piirtämään kuvan uudestaan?
En sitten tiedä, mikä on nykymisen ja tökkimisen ero, mutta kyllä laiton tuohon väliin ton Thead.sleep:in, koska ilman sitä ikkuna ei ole kun sadasosan sekunnin päällä.
lainaus:
Miten tämän repaint() funktion saisi sitten piirtämään kuvan uudestaan?
Periaatteessa piirtäminen suoraan onnistuisi kutsumalla
paint(getGraphics())
Mutta tätä ei kyllä yleensä suositella.
lainaus:
En sitten tiedä, mikä on nykymisen ja tökkimisen ero, mutta kyllä laiton tuohon väliin ton Thead.sleep:in, koska ilman sitä ikkuna ei ole kun sadasosan sekunnin päällä.
Tulinpahan nyt oikeasti testanneeksi sitä koodia, ja näköjään virtuaalikone ajaa täydellisen garbage collectionin joka kuvan jälkeen (Tämän näkee käynnistämällä ohjelma "java -verbose:gc Buffer"). Se pieni Nykiminen johtuu luultavasti tuosta. Tämä puolestaan johtuu siitä, että tuossa jatkuvasti varataan uudestaan tuo muistipuskuri. Siirrä bufferedImagen luonti pois paint-metodista sisäiseksi muuttujaksi niin pitäisi helpottaa.
Tota, mitkäköhän se mahtaisi käytännössä tarkoittaa. Jos jaksat kertoo, niin mitä mun pitäis kirjoittaa lisä koodia ja minkä tilalle? En oikeen saanut itse toimimaan, kun sen verran vähän ole javaa käyttänyt.
Käytät varmaan tuota Samin koodia? Tee siis näin:
Ota nämä pois:
// Ensiksi kuitenkin luodaan uusi kuva puskuriksi (bufferImage) Image bufferImage = createImage(width, height); // Ja sitten luodaan uusi piirtopinta (buffer), joka piirtää bufferImageen Graphics buffer = bufferImage.getGraphics();
Ja laita ne luokan alkuun:
public class Buffer extends Frame { int x, y; Image bufferImage; // Tähän ... Graphics buffer; // ... ja tähän ... public Buffer() { // ... ja tänne (kun en ole varma, saako näitä laittaa aiemmin): bufferImage = createImage(width, height); // Muista laittaa koko näihin. buffer = bufferImage.getGraphics();
Kaikki muu koodi pysyy paikallaan. Varmaan tuonne paint-metodin alkuun tarvitaan jonkinlainen tyhjennys bufferille? En tiedä, en osaa Javaa. Joku buffer.clear() varmaan. Johonkin resize-metodiin pitää laittaa bufferin uudelleenluonti, että koko on oikea.
Okka...kiitti
Aihe on jo aika vanha, joten et voi enää vastata siihen.