2016-02-15 68 views
0

我正在將2D遊戲作爲學習項目工作,並且遇到了一些困難。我無法弄清楚如何使用JPanel中的KeyListener(添加到JFrame中)移動Polygon對象。我試過了frog.translate(int x,int y)方法,它不更新位置。我也嘗試手動更改數組座標。我的代碼示例如下:如何使用Java中的KeyListener移動多邊形對象

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 

public class Board extends JPanel implements KeyListener { 

    private Frog frog; 

    public Board() { 
     setBackground(Color.GREEN); 
     addKeyListener(this); 
     setFocusable(true); 
     setFocusTraversalKeysEnabled(false); 
    } 

    @Override 
    public void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     Graphics2D g2 = (Graphics2D)g;   

     frog = new Frog(); 

     // Frog graphics 
     g2.setColor(Color.BLACK); 
     g2.drawPolygon(frog); 
     g2.setColor(new Color(0,150,15)); 
     g2.fillPolygon(frog); 
    } 

    @Override 
    public void keyTyped(KeyEvent ke) { 
    } 

    @Override 
    public void keyPressed(KeyEvent ke) { 
     int c = ke.getKeyCode(); 
     if(c == KeyEvent.VK_LEFT){ 
      frog.moveFrogLeft(25); 
      //frog.translate(-25,0); 
     } 

     if(c == KeyEvent.VK_RIGHT){ 
      frog.moveFrogRight(25); 
      //frog.translate(25,0); 
     } 

     if(c == KeyEvent.VK_UP){ 
      frog.moveFrogUp(25); 
      //frog.translate(0,-25); 
     } 

     if(c == KeyEvent.VK_DOWN){ 
      frog.moveFrogDown(25); 
      //frog.translate(0,25); 
     } 
     repaint(); 
    } 

    @Override 
    public void keyReleased(KeyEvent ke) { 
    } 
} 

///////////////////////

import java.awt.Polygon; 
import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 

public class Frog extends Polygon { 

    private Integer[] xcoord; 
    private Integer[] ycoord; 

    public Frog(){ 

     xcoord = new Integer[] {5,10,10,15,15,20, 
      20,30,30,35,35,40,40, 
      45,45,40,40,30,30,40, 
      40,45,45,40,40,35,35, 
      30,30,20,20,15,15,10, 
      10,5,5,10,10,20,20, 
      10,10,5,5}; 

     ycoord = new Integer[] {10,10,5,5,20,20, 
      10,10,20,20,5,5,10,10, 
      15,15,25,25,30,30,35,35, 
      40,40,45,45,35,35,40,40, 
      35,35,45,45,40,40,35,35, 
      30,30,25,25,15,15,10}; 

     for(int i = 0; i < xcoord.length; i++){ 
      this.addPoint(xcoord[i],ycoord[i]); 
     } 
    } 

    public void moveFrogLeft(int x) { 
     if(xcoord[0] - x < 0){ 
      //do nothing 
     } else { 
      for(int i = 0; i < xcoord.length; i++){ 
       xcoord[i] = xcoord[i] - x; 
      } 
     } 
    } 

    public void moveFrogRight(int x){ 
     if(xcoord[0] + x > 600){ 
      //do nothing 
     } else { 
      for(int i = 0; i < xcoord.length; i++){ 
       xcoord[i] = xcoord[i] + x; 
      } 
     } 
    } 

    public void moveFrogUp(int y){ 
     if(ycoord[0] - y < 0){ 
      //do nothing 
     } else { 
      for(int i = 0; i < ycoord.length; i++){ 
       ycoord[i] = ycoord[i] - y; 
      } 
     } 
    } 

    public void moveFrogDown(int y){ 
     if(ycoord[0] + y > 600){ 
      //do nothing 
     } else { 
      for(int i = 0; i < ycoord.length; i++){ 
       ycoord[i] = ycoord[i] + y; 
      } 
     } 
    } 
} 

回答

3

這段代碼有一個簡單問題:

@Override 
public void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    Graphics2D g2 = (Graphics2D)g;   

    frog = new Frog();// <-- !!!!! 

    // Frog graphics 
    g2.setColor(Color.BLACK); 
    g2.drawPolygon(frog); 
    g2.setColor(new Color(0,150,15)); 
    g2.fillPolygon(frog); 
} 

標線覆蓋了新的實例青蛙,每次青蛙是畫,因此正在重置原點。除了明顯的問題,這是意外行爲的原因,從來沒有paintComponent(...)-方法做任何不必要的計算。任何預計算,對象生成等都應該在paintComponent之外完成!

+0

我真是個傻瓜......我知道這會是一件簡單的事情。我實現了這種變化,現在它與翻譯方法完美地結合在一起。非常感謝你的幫助! – ineverfinishanyth

+0

@ineverfinishanyth很高興幫助你:) – Paul

0

不要在paintComponent()方法中創建一個Frog!這是扔掉現有的青蛙,並創建一個新的默認位置。 您應該在初始化面板時創建所有的Frog實例,或者可能響應b按鈕單擊以「創建新的青蛙」。

+0

謝謝你的幫助,你證實了保羅所說的話! – ineverfinishanyth

2

首先,我強烈建議您不要使用KeyListener,這是最好的麻煩,更好的選擇是使用Key Bindings API,它是爲了修復KeyListener API的短整數而設計的。第二,你不應該修改多邊形的點,2D圖形API實際上可以有一些非常巧妙的技巧,這使得它更容易和更快地改變你的位置(以及旋轉和縮放)正在繪製。

查看更多細節請看2D Graphics

而是改變多,這是不考慮可視範圍的點,你可以簡單地用一個AffineTransform ...

@Override 
protected void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    Graphics2D g2d = (Graphics2D) g.create(); 
    Point location = frog.getLocation(); 
    AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y); 
    g2d.transform(at); 
    g2d.setColor(new Color(0, 150, 15)); 
    g2d.fill(frog); 
    g2d.setColor(Color.BLACK); 
    g2d.draw(frog); 
    g2d.dispose(); 
} 

這只是簡單地改變了Graphics的原點上下文要繪製多邊形的位置(是的,還有另外一個原因,我使用的是AffineTransform

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Point; 
import java.awt.Polygon; 
import java.awt.Rectangle; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.KeyEvent; 
import java.awt.geom.AffineTransform; 
import javax.swing.AbstractAction; 
import javax.swing.Action; 
import javax.swing.ActionMap; 
import javax.swing.InputMap; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.KeyStroke; 
import javax.swing.Timer; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 

public class Test { 

    public static void main(String[] args) { 
     new Test(); 
    } 

    public Test() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
        ex.printStackTrace(); 
       } 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.add(new Board()); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    protected enum VerticalDirection { 
     NONE, UP, DOWN; 
    } 

    protected enum HorizontalDirection { 
     NONE, LEFT, RIGHT; 
    } 

    public static class Board extends JPanel { 

     protected static final int Y_DELTA = 4; 
     protected static final int X_DELTA = 4; 

     private Frog frog; 
     private VerticalDirection verticalDirection = VerticalDirection.NONE; 
     private HorizontalDirection horizontalDirection = HorizontalDirection.NONE; 

     public Board() { 
      setBackground(Color.GREEN); 
      frog = new Frog(); 

      Timer timer = new Timer(40, new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        Point location = frog.getLocation(); 
        switch (verticalDirection) { 
         case UP: 
          location.y -= Y_DELTA; 
          break; 
         case DOWN: 
          location.y += Y_DELTA; 
          break; 
        } 
        switch (horizontalDirection) { 
         case LEFT: 
          location.x -= X_DELTA; 
          break; 
         case RIGHT: 
          location.x += X_DELTA; 
          break; 
        } 

        Rectangle bounds = frog.getBounds(); 
        int width = bounds.x + bounds.width; 
        int height = bounds.y + bounds.height; 
        if (location.y < 0) { 
         location.y = 0; 
        } else if (location.y + height > getHeight()) { 
         location.y = getHeight() - height; 
        } 
        if (location.x < 0) { 
         location.x = 0; 
        } else if (location.x + width > getWidth()) { 
         location.x = getWidth() - width; 
        } 
        frog.setLocation(location); 
        repaint(); 
       } 
      }); 
      timer.start(); 

      addPressedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.UP)); 
      addPressedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.DOWN)); 
      addPressedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.LEFT)); 
      addPressedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.RIGHT)); 

      addReleasedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.NONE)); 
      addReleasedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.NONE)); 
      addReleasedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.NONE)); 
      addReleasedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.NONE)); 
     } 

     protected void addPressedKeyBinding(String name, int virtuaKey, Action action) { 
      addKeyBinding(name + ".pressed", KeyStroke.getKeyStroke(virtuaKey, 0, false), action); 
     } 

     protected void addReleasedKeyBinding(String name, int virtuaKey, Action action) { 
      addKeyBinding(name + ".released", KeyStroke.getKeyStroke(virtuaKey, 0, true), action); 
     } 

     protected void addKeyBinding(String name, KeyStroke ks, Action action) { 
      InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); 
      ActionMap am = getActionMap(); 

      im.put(ks, name); 
      am.put(name, action); 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(200, 200); 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      Graphics2D g2d = (Graphics2D) g.create(); 
      Point location = frog.getLocation(); 
      AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y); 
      g2d.transform(at); 
      g2d.setColor(new Color(0, 150, 15)); 
      g2d.fill(frog); 
      g2d.setColor(Color.BLACK); 
      g2d.draw(frog); 
      g2d.dispose(); 
     } 

     protected class VerticalMovementAction extends AbstractAction { 

      private VerticalDirection direction; 

      public VerticalMovementAction(VerticalDirection direction) { 
       this.direction = direction; 
      } 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       verticalDirection = direction; 
      } 

     } 

     protected class HorizontalMovementAction extends AbstractAction { 

      private HorizontalDirection direction; 

      public HorizontalMovementAction(HorizontalDirection direction) { 
       this.direction = direction; 
      } 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       horizontalDirection = direction; 
      } 

     } 

    } 

    public static class Frog extends Polygon { 

     private Integer[] xcoord; 
     private Integer[] ycoord; 

     private Point location; 

     public Frog() { 

      location = new Point(0, 0); 

      xcoord = new Integer[]{5, 10, 10, 15, 15, 20, 
       20, 30, 30, 35, 35, 40, 40, 
       45, 45, 40, 40, 30, 30, 40, 
       40, 45, 45, 40, 40, 35, 35, 
       30, 30, 20, 20, 15, 15, 10, 
       10, 5, 5, 10, 10, 20, 20, 
       10, 10, 5, 5}; 

      ycoord = new Integer[]{10, 10, 5, 5, 20, 20, 
       10, 10, 20, 20, 5, 5, 10, 10, 
       15, 15, 25, 25, 30, 30, 35, 35, 
       40, 40, 45, 45, 35, 35, 40, 40, 
       35, 35, 45, 45, 40, 40, 35, 35, 
       30, 30, 25, 25, 15, 15, 10}; 

      for (int i = 0; i < xcoord.length; i++) { 
       this.addPoint(xcoord[i], ycoord[i]); 
      } 
     } 

     public Point getLocation() { 
      return location; 
     } 

     public void setLocation(Point location) { 
      this.location = location; 
     } 

    } 

} 

現在,你可能想知道爲什麼我會用AffineTransform,而不是Graphcis2D#translate,最主要的原因是,它很容易申請其他轉換,如旋轉......

@Override 
protected void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    Graphics2D g2d = (Graphics2D) g.create(); 
    Point location = frog.getLocation(); 

    Rectangle bounds = frog.getBounds(); 
    int width = bounds.x + bounds.width; 
    int height = bounds.y + bounds.height; 
    AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y); 
    at.rotate(Math.toRadians(angle), width/2, height/2); 
    g2d.transform(at); 

    g2d.setColor(new Color(0, 150, 15)); 
    g2d.fill(frog); 
    g2d.setColor(Color.BLACK); 
    g2d.draw(frog); 
    g2d.dispose(); 
} 

這樣做只是申請一個複合轉換,移動Graphics上下文的起源和旋轉矩陣

而對於一個完整的例子...

Frog

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Point; 
import java.awt.Polygon; 
import java.awt.Rectangle; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.KeyEvent; 
import java.awt.geom.AffineTransform; 
import javax.swing.AbstractAction; 
import javax.swing.Action; 
import javax.swing.ActionMap; 
import javax.swing.InputMap; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.KeyStroke; 
import javax.swing.Timer; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 

public class Test { 

    public static void main(String[] args) { 
     new Test(); 
    } 

    public Test() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
        ex.printStackTrace(); 
       } 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.add(new Board()); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    protected enum VerticalDirection { 
     NONE, UP, DOWN; 
    } 

    protected enum HorizontalDirection { 
     NONE, LEFT, RIGHT; 
    } 

    public static class Board extends JPanel { 

     protected static final int Y_DELTA = 4; 
     protected static final int X_DELTA = 4; 

     private Frog frog; 
     private VerticalDirection verticalDirection = VerticalDirection.NONE; 
     private HorizontalDirection horizontalDirection = HorizontalDirection.NONE; 

     private double angle = 0; // Up... 

     public Board() { 
      setBackground(Color.GREEN); 
      frog = new Frog(); 

      Timer timer = new Timer(40, new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        Point location = frog.getLocation(); 
        switch (verticalDirection) { 
         case UP: 
          angle = 0; 
          location.y -= Y_DELTA; 
          break; 
         case DOWN: 
          angle = 180; 
          location.y += Y_DELTA; 
          break; 
        } 
        switch (horizontalDirection) { 
         case LEFT: 
          location.x -= X_DELTA; 
          angle = 270; 
          break; 
         case RIGHT: 
          location.x += X_DELTA; 
          angle = 90; 
          break; 
        } 

        Rectangle bounds = frog.getBounds(); 
        int width = bounds.x + bounds.width; 
        int height = bounds.y + bounds.height; 
        if (location.y < 0) { 
         location.y = 0; 
        } else if (location.y + height > getHeight()) { 
         location.y = getHeight() - height; 
        } 
        if (location.x < 0) { 
         location.x = 0; 
        } else if (location.x + width > getWidth()) { 
         location.x = getWidth() - width; 
        } 
        frog.setLocation(location); 
        repaint(); 
       } 
      }); 
      timer.start(); 

      addPressedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.UP)); 
      addPressedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.DOWN)); 
      addPressedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.LEFT)); 
      addPressedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.RIGHT)); 

      addReleasedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.NONE)); 
      addReleasedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.NONE)); 
      addReleasedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.NONE)); 
      addReleasedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.NONE)); 
     } 

     protected void addPressedKeyBinding(String name, int virtuaKey, Action action) { 
      addKeyBinding(name + ".pressed", KeyStroke.getKeyStroke(virtuaKey, 0, false), action); 
     } 

     protected void addReleasedKeyBinding(String name, int virtuaKey, Action action) { 
      addKeyBinding(name + ".released", KeyStroke.getKeyStroke(virtuaKey, 0, true), action); 
     } 

     protected void addKeyBinding(String name, KeyStroke ks, Action action) { 
      InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); 
      ActionMap am = getActionMap(); 

      im.put(ks, name); 
      am.put(name, action); 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(200, 200); 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      Graphics2D g2d = (Graphics2D) g.create(); 
      Point location = frog.getLocation(); 

      Rectangle bounds = frog.getBounds(); 
      int width = bounds.x + bounds.width; 
      int height = bounds.y + bounds.height; 
      AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y); 
      at.rotate(Math.toRadians(angle), width/2, height/2); 
      g2d.transform(at); 

      g2d.setColor(new Color(0, 150, 15)); 
      g2d.fill(frog); 
      g2d.setColor(Color.BLACK); 
      g2d.draw(frog); 
      g2d.dispose(); 
     } 

     protected class VerticalMovementAction extends AbstractAction { 

      private VerticalDirection direction; 

      public VerticalMovementAction(VerticalDirection direction) { 
       this.direction = direction; 
      } 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       verticalDirection = direction; 
      } 

     } 

     protected class HorizontalMovementAction extends AbstractAction { 

      private HorizontalDirection direction; 

      public HorizontalMovementAction(HorizontalDirection direction) { 
       this.direction = direction; 
      } 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       horizontalDirection = direction; 
      } 

     } 

    } 

    public static class Frog extends Polygon { 

     private Integer[] xcoord; 
     private Integer[] ycoord; 

     private Point location; 

     public Frog() { 

      location = new Point(0, 0); 

      xcoord = new Integer[]{5, 10, 10, 15, 15, 20, 
       20, 30, 30, 35, 35, 40, 40, 
       45, 45, 40, 40, 30, 30, 40, 
       40, 45, 45, 40, 40, 35, 35, 
       30, 30, 20, 20, 15, 15, 10, 
       10, 5, 5, 10, 10, 20, 20, 
       10, 10, 5, 5}; 

      ycoord = new Integer[]{10, 10, 5, 5, 20, 20, 
       10, 10, 20, 20, 5, 5, 10, 10, 
       15, 15, 25, 25, 30, 30, 35, 35, 
       40, 40, 45, 45, 35, 35, 40, 40, 
       35, 35, 45, 45, 40, 40, 35, 35, 
       30, 30, 25, 25, 15, 15, 10}; 

      // I rest the coordinates back to 0x0 because it's easier to 
      // deal with when applying a rotation... 
      for (int index = 0; index < xcoord.length; index++) { 
       xcoord[index] -= 5; 
      } 
      for (int index = 0; index < ycoord.length; index++) { 
       ycoord[index] -= 5; 
      } 

      for (int i = 0; i < xcoord.length; i++) { 
       this.addPoint(xcoord[i], ycoord[i]); 
      } 
     } 

     public Point getLocation() { 
      return location; 
     } 

     public void setLocation(Point location) { 
      this.location = location; 
     } 

    } 

} 

看,不用數學!