【问题标题】:Swing simple overlay [duplicate]摆动简单叠加[重复]
【发布时间】:2016-04-26 14:45:12
【问题描述】:

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

我想实现某种“黑暗”,甚至是像玩家周围被黑色包围的透明形状这样简单的东西,就像这样:

我在使用 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);
                }
            }
        }
    }
}

然后我有“paintPlayer”和“paintEnemy”方法来绘制这些精灵每次移动时。背景只在开始时绘制一次。

【问题讨论】:

标签: java swing graphics


【解决方案1】:

这里是使用 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

【讨论】:

  • 这用于半透明覆盖,但不适用于原始海报实际需要的背景图像。
【解决方案2】:

可能性:

  • 您可以直接在顶级窗口(例如 JFrame)中绘图。如果是这样,请不要在 JPanel 的 paintComonent 方法中绘制,以便使用自动双缓冲功能。
  • 您可能正在从绘画方法中读取图像,如果是这样,请不要。这些方法必须只绘制和绘制,并且必须非常快。
  • 您可能没有在绘画方法中使用 BufferedImage,而是从头创建图像,如果是这样,请不要。使用 Graphics#drawImage(...) 绘制 BufferedImage。
  • 可能您的动画代码已关闭。您可能会从paint 或paintComponent 中调用repaint(),这是绝对不应该做的事情。
  • 而且可能的猜测可能会一直持续下去......

编辑

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

请注意,迷宫的逻辑结构(判断哪堵墙打开,哪堵墙关闭的非视觉数据)应该是程序数据的一部分,而不是它的代码。

【讨论】:

    猜你喜欢
    • 2010-11-28
    • 2014-04-19
    • 1970-01-01
    • 1970-01-01
    • 2012-06-03
    • 1970-01-01
    • 1970-01-01
    • 2015-10-26
    • 1970-01-01
    相关资源
    最近更新 更多