摆动简单叠加

时间:2016-01-20 16:30:17

标签: java swing graphics

我正在制作一个玩家在迷宫中漫游的2D游戏。 screenshot of the GUI

我想实现某种"黑暗",即使像黑色包围的玩家周围的透明形状一样简单,如下所示: mock-up screenshot

我使用Swing发现的问题是,虽然这是可能的,但这意味着必须重新绘制所有内容,这会产生令人讨厌的"闪烁的"每次发生时效果。有没有办法在Swing中进行某种叠加,或者只是一种很好的方式来做这个?我现在对GUI /视觉资料不是很有经验,所以如果可能的话,我想坚持使用Swing。

编辑:这是我绘制背景的方法,即地板,墙壁和出口:

    public final void paintBG(Graphics g){
    g.setColor(Color.LIGHT_GRAY); // Screen background
    g.fillRect(0, 0, getWidth(), getHeight());
    // Draw the Walls of the maze
    // scalex and y are for scaling images/walls within the maze since I let users specify how big they want the maze
    for (int j = 0; j < this.height; j++, y += scaley) {
        x = 20;
        for (int i = 0; i < this.width; i++, x += scalex) {
            if (!(maze[j][i].northwall.isBroken())) // If the north wall isn't broken
            {
                g.drawImage(walltile, x, y, scalex, scaley / 5, null); // Draw a wall there (image, xpos, ypos, width, height, observer)
            }
            if (!(maze[j][i].eastwall.isBroken())) // etc
            {
                g.drawImage(walltile, x + scalex, y, scalex / 5, scaley, null);
            }
            if (!(maze[j][i].southwall.isBroken())) {
                g.drawImage(walltile, x, y + scaley, scalex, scaley / 5, null);
            }
            if (!(maze[j][i].westwall.isBroken())) {
                g.drawImage(walltile, x, y, scalex / 5, scaley, null);
            }

            if ((j == mazeinfo.getTargetM()) && (i == mazeinfo.getTargetN())) {
                // Draw the exit
                g.drawImage(jeep, x + (scalex / 2), y + (scaley / 2), cx, cy, null);
                g.setColor(Color.LIGHT_GRAY);
                if (maze[j][i].northwall.isEdge()) {
                    // Paint over the edge creating a 'way out'
                    g.fillRect(x, y, scalex, scaley / 4);
                } else if (maze[j][i].eastwall.isEdge()) {
                    g.fillRect(x + scalex, y, scalex / 4, scaley);
                } else if (maze[j][i].southwall.isEdge()) {
                    g.fillRect(x, y + scaley, scalex, scaley / 4);
                } else if (maze[j][i].westwall.isEdge()) {
                    g.fillRect(x, y, scalex / 4, scaley);
                }
            }
        }
    }
}

然后我有&#34; paintPlayer&#34;和#34; paintEnemy&#34;每次移动时绘制这些精灵的方法。背景只在开始时被绘制一次。

2 个答案:

答案 0 :(得分:4)

的可能性:

  • 您可能直接在顶级窗口中绘图,例如JFrame。如果是这样,请不要在JPanel的paintComonent方法中绘制,以便使用自动双缓冲可用。
  • 您可能正在阅读绘画方法中的图像,如果是,请不要。这些方法必须只涂漆和涂漆,并且必须非常快速。
  • 你可能没有在你的绘画方法中使用BufferedImage,而是创建一个de-novo的图像,如果是的话,不要这样做。使用Graphics#drawImage(...)绘制BufferedImage。
  • 也许您的动画代码已关闭。您可能正在从paint或paintComponent中调用repaint(),这是永远不应该做的事情。
  • 可能的猜测可以继续......

修改

您的代码显示您可能会在每次绘画迭代时重新绘制迷宫 - 不要这样做。而是将上面的内容绘制到BufferedImage中,并在paintComponent方法中绘制该图像。如果墙壁结构发生变化,则更改BufferedImage。

请注意,迷宫的逻辑结构(指示哪个墙是打开的,非关闭的非可视数据)应该是程序数据的一部分,而不是其代码。

答案 1 :(得分:3)

这里是使用Oracle的Swing UI文档中的LayerUI的示例。只需将AlphaComposite常量更改为更暗的颜色。

以下是一个LayerUI子类,可以在鼠标在面板内移动的任何地方绘制半透明圆圈。

class SpotlightLayerUI extends LayerUI<JPanel> {
  private boolean mActive;
  private int mX, mY;

  @Override
  public void installUI(JComponent c) {
    super.installUI(c);
    JLayer jlayer = (JLayer)c;
    jlayer.setLayerEventMask(
      AWTEvent.MOUSE_EVENT_MASK |
      AWTEvent.MOUSE_MOTION_EVENT_MASK
    );
  }

  @Override
  public void uninstallUI(JComponent c) {
    JLayer jlayer = (JLayer)c;
    jlayer.setLayerEventMask(0);
    super.uninstallUI(c);
  }

  @Override
  public void paint (Graphics g, JComponent c) {
    Graphics2D g2 = (Graphics2D)g.create();

    // Paint the view.
    super.paint (g2, c);

    if (mActive) {
      // Create a radial gradient, transparent in the middle.
      java.awt.geom.Point2D center = new java.awt.geom.Point2D.Float(mX, mY);
      float radius = 72;
      float[] dist = {0.0f, 1.0f};
      Color[] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK};
      RadialGradientPaint p =
          new RadialGradientPaint(center, radius, dist, colors);
      g2.setPaint(p);
      g2.setComposite(AlphaComposite.getInstance(
          AlphaComposite.SRC_OVER, .6f));
      g2.fillRect(0, 0, c.getWidth(), c.getHeight());
    }

    g2.dispose();
  }

  @Override
  protected void processMouseEvent(MouseEvent e, JLayer l) {
    if (e.getID() == MouseEvent.MOUSE_ENTERED) mActive = true;
    if (e.getID() == MouseEvent.MOUSE_EXITED) mActive = false;
    l.repaint();
  }

  @Override
  protected void processMouseMotionEvent(MouseEvent e, JLayer l) {
    Point p = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
    mX = p.x;
    mY = p.y;
    l.repaint();
  }
}

为了让您的播放器更新聚光灯中心,请为玩家移动创建一个事件并注册LayerUI以收听更新。请参阅下面JLayer链接中的setLayerEventMask()示例。

来源:How to Decorate Components with the JLayer Class