Terve
Mulla on JFrame, johon olen lisännyt luokan, joka periytyy JPanelista sekä toteuttaa ActionListener-luokan. Kun lisään sen sen JFrameen (add(new Board())), tämän lisätyn luokan ActionListener ei toimi.
Koodit näyttävät yksinkertaistettuna tältä.
class Board extends JPanel implements ActionListener { Board(...) { addKeyListener(new KListener()); } public void actionPerformed(ActionEvent e) { if(playing) { ... } repaint(); } private class KListener extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { ... } } }
public class Snake extends javax.swing.JFrame { private Board board; public void startGame() { board = new Board(...); this.getContentPane().add(board); this.revalidate(); } }
Miten ylempi luokka saisi kuunneltua noita näppäimenpainalluksia, jotta se tietäisi mihin suuntaan matopelin matoa pitäisi liikutella?
Minun silmääni tossa on jotain todella outoa. Ensinnäkin kiinnostaisi, että missä kohtaa näkyy ActionListener-rajapinnan ainut toteutettava metodi actionPerformed() ? Niin, ja mikä idea on käyttää tässä kohtaa sisäkkäisiä luokkia? Oikeastaan mihin sä ylipäätään tarvitset koko ActionListeneriä eikös KeyListener olisi sopivampi?
Edit. Eli jotenkin näin:
class Board extends JPanel implements KeyListener { public Board() { this.addKeyListener( this ); } public void keyPressed( KeyEvent e ) {} public void keyReleased( KeyEvent e ) {} public void keyTyped( KeyEvent e ) {} }
Lisäsin sen actionPerformed-metodin sinne alkuperäiseen viestiin. Unohtui laittaa tonne, kun tiivistin koodista jonkin järkevän tiivistelmän.
Tossa on siksi ActionListener eikä KeyListener, kun sen pitää reagoida myös Timerin aiheuttamiin tapahtumiin.
Voithan sä toteuttaa molemmat rajapinnat...
Lopputulos olisi kumminkin sama, vain vähän eri tavalla toteutettuna.
What about the real problem?
Minkälainen tuo sinun pelisi main-metodi on? Eli miten käytät tuota Snake-luokasta luotua oliota?
Halusit tietää varmaan, että mitä sille tehdään, ja se ei kyllä valitettavasti löytynytkään noista kahdesta, joten tässä nämä..
public void startGame() { board = new Board(paljon parametrejä); this.getContentPane().remove(mainmenu); this.getContentPane().add(board); this.revalidate(); } public void endGame() { this.getContentPane().remove(board); this.getContentPane().add(mainmenu); this.revalidate(); board.endGame(); updateStats(); }
Suosittelen, että luet alkajaisiksi Java-oppaat aiheista Key Listener ja Key Bindings. Sinun tulisi mahdollisesti käyttää näistä tavoista jälkimmäistä. Jos kuitenkin välttämättä haluat yrittää ensimmäisellä, lisää paneelille ainakin setFocusable(true) ja tarpeen mukaan myös requestFocusInWindow() tai setFocusTraversalKeysEnabled(false).
Kiitos. Ymmärsinköhän kumminkin jotain väärin, kun lopputulos oli tämmöistä kauheaa toistoa? Lopputuloksesta tuli kumminkin ihan toimiva.
this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke((KeyEvent.VK_DOWN), 0, false), "DOWN"); this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke((KeyEvent.VK_UP), 0, false), "UP"); this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke((KeyEvent.VK_RIGHT), 0, false), "RIGHT"); ... this.getActionMap().put("DOWN", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { ... } }); this.getActionMap().put("UP", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { ... } }); ...
KeyListener, keyPressed ?
http://docs.oracle.com/javase/tutorial/uiswing/
... int keyCode = e.getKeyCode(); switch (keyCode) { case 37: //Vasen break; case 38: //Ylos break; case 39: //Oikea break; case 40: //Alas break; default: //Muu mikä.. break; } ...
Minusta silti tuntuu, että teet tuon nyt vaikeamman kautta... ts. itse olen ainakin toteuttanut matopelin ohjauksen huomattavasti helpommalla...
Ohjaus on toteutettu niin, että loopissa tarkistetaan mikä muuttujista (right, left, up, down) on tosi ja liikutaan siihen suuntaan. Ohjaus on oikeastaan tehty tämän oppaan perusteella. Se toimii, mutta se kyllä tuntuu vähän erikoiselta.
Jos matopelitoteutuksesi on nähtävillä, niin olisin halukas näkemään jos voisin tehdä jotain paremmin.
Eikö olisi helpompaa pitää vain yhtä muuttujaa, josta selviäisi suunta?
void lisaaNappi(String nimi, int vk, final Suunta uusiSuunta) { this.getInputMap(...).put(KeyStroke.getKeyStroke(vk, 0, false), nimi); this.getActionMap().put(nimi, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { suunta = uusiSuunta; } }); } // ... lisaaNappi("DOWN", KeyEvent.VK_DOWN, Suunta.ALAS); lisaaNappi("UP", KeyEvent.VK_UP, Suunta.YLOS);
Tai jopa suunta suoraan vektorimuodossa (dx, dy).
Metabolix kirjoitti:
Tai jopa suunta suoraan vektorimuodossa (dx, dy).
Itse kallistuisin täman ratkaisun kannalle. En tiedä Javan valmiita luokkia, mutta esim. Infernolla löytyy Point adt, mikä soveltuu tämän tyyppiseen touhuun erinomaisesti.
esim. Infernolla tuo suunnan muutos ja 180° käännöksen estäminen sujuisi:
if (!newdir.eq(dir) && !newdir.eq(dir.mul(-1))) { # don't allow 180° turn. dir = newdir; }
Nyt kun eksyttiin jo sivuraiteille, niin muokkasin otsikkoa sopivammaksi ja kysyn lisää.
Mikä sitten olisi hyvä tapa liikkumiseen? Mainitsit vektorit, mutta minulla ei ole mitään käsitystä niiden käytöstä edes Ohjelmointiputkan oppaan lukemisen jälkeen (ehkä niitä tulee vasta myöhemmin koulussa..).
Tässä tapauksessa vektori olisi vain (x, y) pari ja matematiikka normaalia peruskoulun laskuoppia...
Otetaan esimerkki:
Olet kartalla koordinaateissa (x = 5, y = 5) ja haluat liikkua ruudun verran vaakasuorassa vasemmalle.
Tarvittava suuntavektori olisi tuolloin: (x = -1, y = 0)
Arvasit varmaan, että laskemalla sijaintivektorin ja suuntavektorin x -ja y-komponentit yhteen saat uuden sijainti vektorin: (x = 4, y = 5).
Tyydyin kumminkin tähän nykyiseen tapaan, kun sainkin sen viritettyä toimimaan.
Kiitos vastauksista.
Aihe on jo aika vanha, joten et voi enää vastata siihen.