1

我正在尝试在 JFrame 中制作游戏,但遇到了问题。我创建了一个由四个图像串成一个的对象。我的问题是,我如何在 JFrame 中绘制这个对象?

这是代码:

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.*;

public class t4
{
    static boolean running;

    public static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    public static double width = screenSize.getWidth();
    public static double height = screenSize.getHeight();

    public static int x = ( 250 );
    public static int y = ( 150 );

    public static final int sx = (int)width;
    public static final int sy = (int)height;

    public static void main( String[] args ) throws IOException, InterruptedException
    {
        Image ur = new ImageIcon("redBlock.gif").getImage();
        Image ll = new ImageIcon("redBlock.gif").getImage();
        Image ul = new ImageIcon("blueBlock.gif").getImage();
        Image lr = new ImageIcon("blueBlock.gif").getImage();

        // Create game window...
        JFrame app = new JFrame();
        app.setIgnoreRepaint( true );
        app.setUndecorated( true );

        // Add ESC listener to quit...
        app.addKeyListener( new KeyAdapter()
        {
            public void keyPressed( KeyEvent e )
            {
                if( e.getKeyCode() == KeyEvent.VK_ESCAPE )
                    running = false;
                if((e.getKeyCode()==KeyEvent.VK_LEFT)||(e.getKeyCode()==KeyEvent.VK_KP_LEFT))
                    x-=10;
                if((e.getKeyCode()==KeyEvent.VK_RIGHT)||(e.getKeyCode()==KeyEvent.VK_KP_RIGHT))
                    x+=10;
                if((e.getKeyCode()==KeyEvent.VK_UP)||(e.getKeyCode()==KeyEvent.VK_KP_UP))
                    y-=10;
                if((e.getKeyCode()==KeyEvent.VK_DOWN)||(e.getKeyCode()==KeyEvent.VK_KP_DOWN))
                y+=10;
            }
        });

        // Get graphics configuration...
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        GraphicsConfiguration gc = gd.getDefaultConfiguration();

        // Change to full screen
        gd.setFullScreenWindow( app );
        if( gd.isDisplayChangeSupported() )
        {
            gd.setDisplayMode(new DisplayMode( sx, sy, 32, DisplayMode.REFRESH_RATE_UNKNOWN ));
        }

        // Create BackBuffer...
        app.createBufferStrategy( 2 );
        BufferStrategy buffer = app.getBufferStrategy();

        // Create off-screen drawing surface
        BufferedImage bi = gc.createCompatibleImage( sx, sy );

        // Objects needed for rendering...
        Graphics graphics = null;
        Graphics2D g2d = null;
        Color background = Color.BLACK;
        Random rand = new Random();

        // Variables for counting frames per seconds
        int fps = 0;
        int frames = 0;
        long totalTime = 0;
        long curTime = System.currentTimeMillis();
        long lastTime = curTime;

        running = true;
        while( running )
        {
            try
            {
                //    wait(500);

                // count Frames per second...
                lastTime = curTime;
                curTime = System.currentTimeMillis();
                totalTime += curTime - lastTime;
                if( totalTime > 1000 )
                {
                    totalTime -= 1000;
                    fps = frames;
                    frames = 0;
                } 
                ++frames;

                // clear back buffer...
                g2d = bi.createGraphics();
                g2d.setColor( background );
                g2d.fillRect( 0, 0, sx, sy );

                // draw some rectangles...
                /* int r = 45;
                int g = 232;
                int b = 163;
                g2d.setColor( new Color(r,g,b) );
                int w = ( 250 );
                int h = ( 150 );
                g2d.fillRect( x+25, y+25, w, h );*/

                if(y<775)
                {
                    y++;
                }
                else
                {
                    y=0;
                }

                // display frames per second...
                g2d.setFont( new Font( "Courier New", Font.PLAIN, 12 ) );
                g2d.setColor( Color.GREEN );
                g2d.drawString( String.format( "FPS: %s", fps ), 20, 20 );

                // Blit image and flip...
                graphics = buffer.getDrawGraphics();
                graphics.drawImage( bi, 0, 0, null );

                graphics.drawImage(ur,x,y,null);
                graphics.drawImage(ll,x+50,y+50,null);
                graphics.drawImage(ul,x,y+50,null);
                graphics.drawImage(lr,x+50,y,null);

                if( !buffer.contentsLost() )
                    buffer.show();

            }
            finally
            {
                // release resources
                if( graphics != null ) 
                    graphics.dispose();
                if( g2d != null ) 
                    g2d.dispose();
            }
        }

        gd.setFullScreenWindow( null );
        System.exit(0);
    }

    public static void wait(int x) throws InterruptedException
    {
        Thread.currentThread().sleep(x);
    }
}

我想创建一个包含图像 ur、ll、ul 和 lr 的对象,并能够在屏幕上绘制它。

4

1 回答 1

6

这是你应该做的:

  1. 修改类以使其扩展javax.swing.JComponent
  2. 覆盖paintComponent(Graphics)
  3. 创建一个javax.swing.Timer来管理帧速率。
  4. 覆盖getPreferredSize()

首先(按照 DavidB 的要求)我会解释为什么你应该做这些事情,然后我会告诉你如何做。

解释

  • 由于您尝试将组件添加到 JFrame,因此您需要组件的类与 JFrame 的add方法兼容(实际上,它属于Container,但这并不重要)。如果您查看JavaDoc 文档add,您会发现它不接受任何Object; 相反,它需要一个Component(或其子类)的实例。您可以子类Component化而不是JComponent,但Component更多的是 AWT 应用程序而不是 Swing 应用程序。
    简而言之:子类JComponent,以便JFrame.add将其作为参数接受。
  • 子类JComponent化后,您需要实际告诉窗口管理器要绘制什么。您可以将绘图代码放在任何地方,但请记住它不会被调用(使用),除非实际调用该方法。图形环境调用开始绘制过程的方法称为paintComponent*. 如果您覆盖此方法,则图形环境将调用您的自定义绘画代码。
    简而言之:覆盖paintComponent,因为这是图形环境所关心的。
  • 由于您很可能会在游戏中制作动画,因此您需要保持每秒帧数的恒定速率,对吗?如果你不这样做,有很多因素(计算机能力、运行的其他应用程序、绘图复杂性等)可能会导致帧速率失控。为此,您需要repaint每秒调用该方法指定的次数(每帧一次)。这是 Swing 计时器的重点。你给它一段代码和几毫秒,它会在每次指定的时间间隔过去时运行该代码。
    简而言之:使用 Swing 计时器,以便您可以保持帧速率恒定并受到控制。
  • 想象一下,您有一个文字处理应用程序。它的顶部有一个菜单栏,中间有一个文档窗口,底部有一个工具栏。显然,您希望菜单栏和工具栏很小,并且文档占用尽可能多的空间,对吗?这就是为什么您需要让每个组件告诉您它的大小应该是多少,称为它的首选大小。覆盖getPreferredSize允许您返回所需的任何大小,从而控制组件的大小。**
    简而言之:覆盖getPreferredSize,以便窗口管理器和图形环境获得正确的所有大小。

* 实际上并不是paintComponent这样调用的;它是paint。但是,该paint方法调用paintComponentpaintBorderpaintChildren

该方法实际上将绘画工作委托给了三个受保护的方法:paintComponent、paintBorder 和paintChildren。它们按列出的顺序调用,以确保子组件出现在组件本身的顶部。一般来说,组件及其子组件不应该在分配给边框的 insets 区域中进行绘制。子类可以像往常一样覆盖这个方法。只想专门化 UI(外观和感觉)委托的绘制方法的子类应该重写paintComponent。

(来源:JavaDoc

** 覆盖getPreferredSize实际上并不能保证这是显示组件的大小。它仅指定显示的大小。一些布局管理器会选择忽略这一点(例如 BorderLayout)。但是,当您调用pack正确调整窗口大小时,它应该根据此大小计算首选大小。

程序

扩展 JComponent

要使类扩展 JComponent,只需将类签名更改为:

import javax.swing.JComponent;

public class MyGameDisplay extends JComponent {
    ...
}

覆盖paintComponent

您需要导入java.awt.Graphics该类。请参阅此示例代码以了解如何使用paintComponent

import javax.swing.JComponent;
import java.awt.Graphics;

public class MyGameDisplay extends JComponent {
    // Some code here

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g); // this line is crucial; see below
        g.drawString(100,100,"Hello world");
    }
}

注意:上面,我提到了super.paintComponent在方法中调用 from的必要性paintComponent。这样做的原因是它将(除其他外)清除您之前显示的所有图形。因此,例如,如果您的程序绘制了一个在屏幕上移动的圆,则绘图的每次迭代也将包含来自先前绘图的圆的轨迹,除非您调用super.paintComponent.

用一个Timer

要获得所需的 FPS 速率,请修改类以包含 Swing 计时器,如下所示:

// Include these imports:
import javax.swing.Timer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MyGameDisplay extends JComponent {
    private Timer t;
    public MyGameDisplay() {
        ActionListener al = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                repaint();
            }
        }
        t = new Timer(1000 / 30 /* frame rate */, al);
        t.start();
    }
}

覆盖getPreferredSize

覆盖的原因getPreferredSize是布局管理器将知道如何正确调整容器的大小。

虽然编写实际逻辑来计算大小可能很困难,但覆盖getPreferredSize本身并不困难。这样做:

@Override
public Dimension getPreferredSize() {
    return new Dimension(400, 400); // for example
}

完成后,您只需运行以下代码:

import javax.swing.JFrame;

public class Test {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        MyGameDisplay mgd = new MyGameDisplay();
        frame.add(mgd);
        frame.pack();
        frame.setVisible(true);
    }
}
于 2012-05-23T21:05:35.300 回答