Ongelmana on tilepohjaisen taustan skrollaaminen. Olen onnistunut tilen liikuttelussa yksi kerrallaan, mutta taustan liikuttelu sulavasti(pikselitasolla) tuottaa ongelmia.
Piirrän tilet ruudulle taulukosta näin:
for(y = 0; y < näkymäKorkeus; y++)for(x = 0; x < näkymäLeveys; x++){ //käydään koko taulukko läpi for(int i = 0; i < 100; i++){ //katsotaan mitä lukuja taulukossa on if(karttaTaulukko[x+kamerax][y+kameray]==i){ kuva=getImage(getCodeBase(), "tile"+i+".png"); //ladataan png-kuva puskuri.drawImage(kuva, x*tilex+murtoX, y*tiley+murtoY, tilex, tiley, this); //piirretään kuva paikalleen } } }
Taustan skrollaaminen onnistuuu lisäämällä tai vähentämällä murtoX/murtoY-muuttujaa ja kun muuttuja on yhtä suuri kuintilen leveys/korkeus se nollataan ja lisätään kamerax/kameray :tä. Kuitenkin liikuttaessa vasemmalle taustaan jää valkoinen kohta, sillä piirtoa ei aloiteta yhtä tileä kauempaa. Miten saan piirrettyä taustan yhtä tileä kauempaa vasemmalle mentäessä(oiekallehan tuo onnistuu lisäämällä kamerax:ää yhdellä)?
Käytän seuraavanlaista silmukkaa oikealle liikkumiseen:
for(murtoX=0; murtoX>-64; murtoX--){ murtoX=0; kamerax++; repaint(); odota(65); } public void odota(int aika) { try { //ohjelma odottaa hetken Thread.sleep(aika); }catch (InterruptedException e) {} }
Mistä johtuu, että kun liikun oikealle ruutu on valkoinen silmukan suorittamiseen asti?
Kiitokset jo etukäteen vastauksista!
Tuossa minun funktioni C++:lla SDL:ää ja omaa tile-engineäni käyttäen.
void PiirraKentta(TileSet *tileset, SDL_Surface *naytto, Alue *kentta, Pelaaja *pelaaja, System *sysdata) { int start_x=sysdata->res_w/2-pelaaja->x, start_y=sysdata->res_h/2-pelaaja->y, x=0, y=0; while(x < kentta->leveys) { while(y < kentta->korkeus) { tileset->DrawTile(kentta->HaeKohta(x, y), naytto, start_x+x*tileset->GetSize(), start_y+y*tileset->GetSize(), false); y++; } y=0; x++; } }
Tuo piirtää koko kartan joten ei aivan hyvä isoilla kartoilla. Minulla oli versio joka piirtää vain näkyvän osan mutta se oli semmonen tile kerrallaan mallinen.
Kyse oli varmaankin Javasta, vaikka eihän sitä tosiaan mainittu.
Tosiaan Javastahan oli kyse, mutta kiitos kuitenkin.
Taisimpa vähän hätiköidä tuon kysymyksen kanssa. Tietysti tuo onnistuu muuttamalla aloituskoordinaatteja -1:ksi(tässä tapauksessa x ja y).
for(y = -1; y < näkymäKorkeus; y++)for(x = -1; x < näkymäLeveys; x++){ //käydään koko taulukko läpi nyt -1:stä näkymäLeveys+1:een asti for(int i = 0; i < 100; i++){ //katsotaan mitä lukuja taulukossa on if(karttaTaulukko[x+kamerax][y+kameray]==i){ kuva=getImage(getCodeBase(), "tile"+i+".png"); //ladataan png-kuva puskuri.drawImage(kuva, x*tilex+murtoX, y*tiley+murtoY, tilex, tiley, this); //piirretään kuva paikalleen } } }
Mutta silti tuo toinen ongelma vaivaa. Eli miksi ruutu pysyy silmukan ajan valkoisena ja piirtyy vasta sitten kun silmukka on valmis?
for(murtoX=0; murtoX>-64; murtoX--){ murtoX=0; kamerax++; repaint(); odota(65); } public void odota(int aika) { try { //ohjelma odottaa hetken Thread.sleep(aika); }catch (InterruptedException e) {} }
Ensiksi kysyisin että oletko varma että silmukka päättyy joskus? Jos murtoX:n arvo on aina 0 se on aina arvoa -64 suurempi.
Ruudun valkoisuus voi johtua jostain muusta, mitä et tässä esitä.
On tietysti mahdollista, että virhe johtuu muusta kuin tuosta hidasteesta, mutta jos muutan koodia niin, että aina hiiren klikkauksella ukko liikkuu esim. oikealle yhden pikselin koodi toimii oikein. Vain silloin kun kutsun hidastetta ruutu pysyy valkoisena.
Tein tällaisen pikkuohjelman samasta ongelmasta:
import java.applet. *; import java.awt. *; import java.awt.event. *; public class Testi extends Applet implements KeyListener{ /** * */ private static final long serialVersionUID = 1L; String napit=""; //mitä namiskoja on paineltu int x=50, y=50; //laatikon koordinaatit //kaksoispuskuroinnille Dimension mitat; //appletin mitat Graphics puskuri; //toinen piirtopinta Image offScreen; //ja uusi kuva public void init(){ addKeyListener(this); //lisätään näppäimistön kuuntelija mitat=getSize(); //haetaan appletin koko offScreen=createImage(mitat.width, mitat.height); //luodaan samankokoinen uusi kuva puskuri=offScreen.getGraphics(); //vedetään sille vielä piirtopintakin } public void paint(Graphics pixeli){ puskuri.clearRect(0, 0, mitat.width, mitat.height); //pyyhitään puskuri puskuri.drawRect(x, y, 50, 50); //piirretään neliö puskuriin pixeli.drawImage(offScreen, 0, 0, this); //piirretään offScreen-kuva näkyvälle piirtopinnalle } public void update(Graphics pixeli){ //ylikirjoitetaan tämä ettei kutsuta update()-metodia paint(pixeli); } public void keyPressed(KeyEvent nappi){ napit= KeyEvent.getKeyText(nappi.getKeyCode()); //otetaan näppäimen "nimi" ylös if (napit.equals("Right")) if ( x < 200 ){ //jos painettiin oikeaa nuolta niin siirrytään silmukkaan for(int kieppi=0;kieppi<64;kieppi++){ //pyöritetään looppia kunnes on liikuttu 65 pikseliä x++; //liikutetaan laatikkoa odota(65); //odotetaan 65ms repaint(); //päivitetään näyttö } } } public void keyReleased(KeyEvent nappi){} //turhat pakolliset metodit public void keyTyped(KeyEvent nappi){} public void odota(int aika) { //hidaste try { Thread.sleep(aika); }catch (InterruptedException e) {} } }
Kiitoksia jälleen kaikille aikaansa tähän ongelmaan tuhlanneille!
Kuuntelioiden (listener) säikeen (thread) käyttäminen isoihin operaatioihin ei ole välttämättä hyvä asia.
Mitä jos tekisit oman säikeen ns. pelisilmukalle ja lisäisit painetut näppäimet vaikka HashSet:iin keyPressed-metodissa ja poistaisit ne sieltä keyReleased-metodissa.
Pelisilmukassa käsiteltäisiin ensin pelimaailman muutokset, missä tallennettuja näppäimenpainalluksia käytetään hyväksi, ja tämän jälkeen kutsutaan ruudunpäivitystä. Samalla voisi mitata ruudunpäivityksen käyttämän ajan ja käyttää tätä aikaa hyväksi pelimaailman muuttamisessa. Näin saataisiin muutokset riippumaan ajasta.
Koska en ole paljoakaan säikeiden kanssa pelannut, ja tämä on ensimmäinen hieman isompi projekti, olisi hienoa jos voisit(tai kuka vaan joka viitsii) kirjoittaa kayttaja-2499:n mainitsemat ominaisuudet(tai edes osan niistä) tuohon yllä olevaan esimerkkiin(tai aivan uuteen). Tiedän, että tämä on melko paljon pyydetty, mutta se auttaisi suuresti.
Kaikki tieto ja muu info aiheesta on tervetullutta.
Jos jolla kulla on aikaa ja viitseliäistyyttä arvostaisin sitä suuresti! ^^
Tässä hieman esimerkkiä:
public class Testi extends Applet implements KeyListener, Runnable { private HashSet<Integer> nappaimet = new HashSet<Integer>(); public Testi() { addKeyListener(this); new Thread(this).start(); } public void run() { long alkuaika = System.currentTimeMillis(); long loppuaika; long kesto = 0; double x = 0; double nopeus = 5 / 1000; // 5 yksikköä / sekunnissa while(true) { /* Maailman muuttaminen */ if(nappaimet.contains(KeyEvent.VK_RIGHT)) { x += nopeus * kesto; } repaint(); loppuaika = System.currentTimeMillis(); kesto = loppuaika - alkuaika; // Kesto millisekunteina alkuaika = loppuaika; } } public void keyPressed(KeyEvent e) { nappaimet.add(e.getKeyCode()); } public void keyReleased(KeyEvent e) { nappaimet.remove(e.getKeyCode()); } public void keyTyped(KeyEvent e) {} }
Koodasin suoraan textareaan joten en takaa että koodi toimii. Mutta kaipa tästä jotain vinkkekä saa.
Erittäin suuret kiitokset!
Tuosta oli suuri apu ja sain ohjelmani toimimaan juuri niin, kuin halusin. HashSet-ratkaisu osoittautui toimivaksi ja säikeiden käyttökin alkaa pikkuhiljaa luonnistua.
Erittäin mahtavaa, että jaksoit auttaa minut alkuun!
PS. Pyrin taas vähän aikaa olemaan häiriköimättä foorumeilla(ainakin tämän osion kohdalla). ^^
Aihe on jo aika vanha, joten et voi enää vastata siihen.