Java 背景图像每次都在不同的地方

Java 背景图像每次都在不同的地方,java,Java,我有一个很大的程序,我会发布一些课程,希望你们能找到问题所在。基本上,有时当我启动它时,它会创建一个很好的游戏,而另一些时候,背景在北面和西面的方向上增加了几个像素,留下了非常难看的空白。我似乎找不到决定它是否这样做的缺失代码。老实说,我觉得我的机器出现了某种渲染故障。无论如何,我已经在调试中加入了一个backgroundgetX和getY方法,并注意到无论背景是否完全拉伸到屏幕上(这是一个自定义背景,因此像素高度和宽度完全匹配),还是向上和向左,背景仍然显示为(0,0)。我将发布从主线程到菜单

我有一个很大的程序,我会发布一些课程,希望你们能找到问题所在。基本上,有时当我启动它时,它会创建一个很好的游戏,而另一些时候,背景在北面和西面的方向上增加了几个像素,留下了非常难看的空白。我似乎找不到决定它是否这样做的缺失代码。老实说,我觉得我的机器出现了某种渲染故障。无论如何,我已经在调试中加入了一个background
getX
getY
方法,并注意到无论背景是否完全拉伸到屏幕上(这是一个自定义背景,因此像素高度和宽度完全匹配),还是向上和向左,背景仍然显示为(0,0)。我将发布从主线程到菜单中创建背景的所有方法。我将留下笔记,指出它通过这段代码到达创建背景的路径。谢谢你的帮助,我会定期查看编辑和更多信息

编辑:添加了background.java 编辑2:添加了解释问题的图片

Menu.java*忽略
文件IO
代码,主要是创建一个新的GamePanel()

接下来是GamePanel.java

    public class GamePanel extends JPanel implements Runnable, KeyListener {
    // ID
    private static final long serialVersionUID = 1L;

    // Dimensions
    public static final int WIDTH = 320;
    public static final int HEIGHT = 240;
    public static final int SCALE = 2;

    // Thread
    private Thread thread;
    private boolean running;
    private int FPS = 30;
    private long targetTime = 1000 / FPS;

    // Image
    private BufferedImage image;
    private Graphics2D g;

    // Game State Manager
    private GameStateManager gsm;

    public GamePanel() {
        super();
        setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
        setFocusable(true);
        requestFocus();
    }

    public void addNotify() {
        super.addNotify();
        if (thread == null) {
            thread = new Thread(this);
            addKeyListener(this);
            thread.start();
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
    }

    private void init() {
        image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        g = (Graphics2D) image.getGraphics();
        running = true;

        gsm = new GameStateManager();
    }

    @Override
    public void run() {
        init();

        long start;
        long elapsed;
        long wait;

        // Game Loop
        while (running) {
            start = System.nanoTime();
            update();
            draw();
            drawToScreen();

            elapsed = System.nanoTime() - start;
            wait = targetTime - (elapsed / 1000000);

            if (wait < 0) {
                wait = 5;
            }

            try {
                Thread.sleep(wait);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void update() {
        gsm.update();
    }

    private void draw() {
        gsm.draw(g);
    }

    private void drawToScreen() {
        Graphics g2 = getGraphics();
        g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
        g2.dispose();
    }

    @Override
    public void keyPressed(KeyEvent k) {
        gsm.keyPressed(k.getKeyCode());
    }

    @Override
    public void keyReleased(KeyEvent k) {
    }

    @Override
    public void keyTyped(KeyEvent arg0) {
    }
}
GameState
是我拥有的一个抽象类,因此它不值得发布,它只包含
init()
draw()
等。下一个类是最后一个类,之所以调用它是因为
GameStateMananger
MENUSTATE
或0开始,当GSM初始化时,它会初始化它的当前状态,这样我们就可以去上课了
MenuState

MenuState.java

public class MenuState extends GameState {

    private Background bg;

    public FontMetrics fontMetrics;

    private int choice = 0;
    private String[] options = { "Slot Machine", "Dice Toss", "Roulette Wheel", "Leaderboards", "Settings", "Help",
            "Quit" };

    private Color titleColor;
    private Font titleFont;

    private Font font;

    public MenuState(GameStateManager gsm) {
        this.gsm = gsm;
        try {
            bg = new Background("/Backgrounds/happybg.png");

            titleColor = Color.WHITE;
            titleFont = new Font("Georgia", Font.PLAIN, 28);
        } catch (Exception e) {
            e.printStackTrace();
        }

        font = new Font("Arial", Font.PLAIN, 12);
    }

    @Override
    public void init() {
    }

    @Override
    public void update() {
    }

    @Override
    public void draw(Graphics2D g) {
        Canvas c = new Canvas();
        fontMetrics = c.getFontMetrics(font);
        // Draw BG
        bg.draw(g);

        // Draw title
        g.setColor(titleColor);
        g.setFont(titleFont);
        String title = "Jolly Jackpot Land!";
        g.drawString(title, 36, 60);

        g.setFont(font);

        for (int i = 0; i < options.length; i++) {
            if (i == choice)
                g.setColor(Color.RED);
            else
                g.setColor(Color.WHITE);
            g.drawString(options[i], 30, 120 + i * 15);
        }
        g.setColor(Color.WHITE);
        g.setFont(new Font("Arial", Font.PLAIN, 10));
        g.drawString("v1.1", 165, 235);

        Object[] a = { ("Name: " + Player.getName()), ("Gil: " + Player.getGil()),
                ("Personal Best: " + Player.getPersonalBest()), ("Winnings: " + Player.getWinnings()),
                ("Wins: " + Player.getWins()), ("Losses: " + Player.getLosses()),
                ("Win/Loss Ratio: " + String.format("%.2f", Player.getRatio()) + "%") };

        g.setFont(font);

        if (Player.getName() != null) {
            for (int x = 0; x < a.length; x++) {
                g.drawString(a[x].toString(), GamePanel.WIDTH - fontMetrics.stringWidth(a[x].toString()) - 30,
                        120 + x * 15);
            }
        }
    }

    private void select() {
        if (choice == 0) {
            // Slots
            gsm.setState(GameStateManager.SLOTGAMESTATE);
        }
        if (choice == 1) {
            // Dice
            gsm.setState(GameStateManager.DICEGAMESTATE);
        }
        if (choice == 2) {
            // Roulette
            gsm.setState(GameStateManager.ROULETTEGAMESTATE);
        }
        if (choice == 3) {
            // Leaderboards
            gsm.setState(GameStateManager.LEADERBOARDSTATE);
        }
        if (choice == 4) {
            // Settings
            gsm.setState(GameStateManager.SETTINGSSTATE);
        }
        if (choice == 5) {
            // Help
            gsm.setState(GameStateManager.HELPSTATE);
        }
        if (choice == 6) {
            // Quit
            System.exit(0);
        }
    }

    @Override
    public void keyPressed(int k) {
        if (k == KeyEvent.VK_ENTER) {
            select();
        }
        if (k == KeyEvent.VK_UP) {
            choice--;
            if (choice == -1) {
                choice = options.length - 1;
            }
        }
        if (k == KeyEvent.VK_DOWN) {
            choice++;
            if (choice == options.length) {
                choice = 0;
            }
        }
    }

    @Override
    public void keyReleased(int k) {
    }
}
这就是它在游戏循环中等待输入的地方。我知道这是很多代码,但很多代码都是略读的,直到一个方法调用将您带到下一个类。我只是不明白为什么它只是偶尔发生,如果它是一致的,我可以调试它。任何帮助都将不胜感激


这些都是通过点击上面程序的.jar得到的,完全相同的.jar,完全相同的源代码,不同的结果。我很困惑。

我们应该从如何
getGraphics
开始,而不是如何在Swing中进行自定义绘制,您基本上正在冒着一种风险,即您将代码绘制到
Graphics
上下文和Swing执行相同的操作之间存在竞争条件,这将导致闪烁发生。如果您需要对绘制过程进行主动控制,则应使用,另请参阅以了解更多详细信息。因此,不要处理非您自己创建的
图形
上下文,这可能会导致某些系统停止绘制无需在您的
draw
方法中创建
Canvas
Graphics2D
有自己的
getFontMetrics
方法。
Background
类是如何自己绘制的?看看这个,看看一个带有图形的简单Swing应用程序应该如何编码。
public class GameStateManager {


    private ArrayList<GameState> gameStates;
    private int currentState;

    public static final int MENUSTATE = 0;
    public static final int SLOTGAMESTATE = 1;
    public static final int DICEGAMESTATE = 2;
    public static final int ROULETTEGAMESTATE = 3;
    public static final int LEADERBOARDSTATE = 4;
    public static final int SETTINGSSTATE = 5;
    public static final int HELPSTATE = 6;


    public GameStateManager() {
        gameStates = new ArrayList<GameState>();

        currentState = 0;
        gameStates.add(new MenuState(this));
        gameStates.add(new SlotGameState(this));
        gameStates.add(new DiceGameState(this));
        gameStates.add(new RouletteGameState(this));
        gameStates.add(new LeaderboardState(this));
        gameStates.add(new SettingsState(this));
        gameStates.add(new HelpState(this));


    }

    public void setState(int state){
        currentState = state;
        gameStates.get(currentState).init();
        currentState = 0;
    }

    public int getState() {
        return currentState;
    }

    public void update() {
        gameStates.get(currentState).init();
    }

    public void draw(java.awt.Graphics2D g){
        gameStates.get(currentState).draw(g);
    }

    public void keyPressed(int k){
        gameStates.get(currentState).keyPressed(k);
    }

    public void keyReleased(int k) {
        gameStates.get(currentState).keyReleased(k);
    }
}
public class MenuState extends GameState {

    private Background bg;

    public FontMetrics fontMetrics;

    private int choice = 0;
    private String[] options = { "Slot Machine", "Dice Toss", "Roulette Wheel", "Leaderboards", "Settings", "Help",
            "Quit" };

    private Color titleColor;
    private Font titleFont;

    private Font font;

    public MenuState(GameStateManager gsm) {
        this.gsm = gsm;
        try {
            bg = new Background("/Backgrounds/happybg.png");

            titleColor = Color.WHITE;
            titleFont = new Font("Georgia", Font.PLAIN, 28);
        } catch (Exception e) {
            e.printStackTrace();
        }

        font = new Font("Arial", Font.PLAIN, 12);
    }

    @Override
    public void init() {
    }

    @Override
    public void update() {
    }

    @Override
    public void draw(Graphics2D g) {
        Canvas c = new Canvas();
        fontMetrics = c.getFontMetrics(font);
        // Draw BG
        bg.draw(g);

        // Draw title
        g.setColor(titleColor);
        g.setFont(titleFont);
        String title = "Jolly Jackpot Land!";
        g.drawString(title, 36, 60);

        g.setFont(font);

        for (int i = 0; i < options.length; i++) {
            if (i == choice)
                g.setColor(Color.RED);
            else
                g.setColor(Color.WHITE);
            g.drawString(options[i], 30, 120 + i * 15);
        }
        g.setColor(Color.WHITE);
        g.setFont(new Font("Arial", Font.PLAIN, 10));
        g.drawString("v1.1", 165, 235);

        Object[] a = { ("Name: " + Player.getName()), ("Gil: " + Player.getGil()),
                ("Personal Best: " + Player.getPersonalBest()), ("Winnings: " + Player.getWinnings()),
                ("Wins: " + Player.getWins()), ("Losses: " + Player.getLosses()),
                ("Win/Loss Ratio: " + String.format("%.2f", Player.getRatio()) + "%") };

        g.setFont(font);

        if (Player.getName() != null) {
            for (int x = 0; x < a.length; x++) {
                g.drawString(a[x].toString(), GamePanel.WIDTH - fontMetrics.stringWidth(a[x].toString()) - 30,
                        120 + x * 15);
            }
        }
    }

    private void select() {
        if (choice == 0) {
            // Slots
            gsm.setState(GameStateManager.SLOTGAMESTATE);
        }
        if (choice == 1) {
            // Dice
            gsm.setState(GameStateManager.DICEGAMESTATE);
        }
        if (choice == 2) {
            // Roulette
            gsm.setState(GameStateManager.ROULETTEGAMESTATE);
        }
        if (choice == 3) {
            // Leaderboards
            gsm.setState(GameStateManager.LEADERBOARDSTATE);
        }
        if (choice == 4) {
            // Settings
            gsm.setState(GameStateManager.SETTINGSSTATE);
        }
        if (choice == 5) {
            // Help
            gsm.setState(GameStateManager.HELPSTATE);
        }
        if (choice == 6) {
            // Quit
            System.exit(0);
        }
    }

    @Override
    public void keyPressed(int k) {
        if (k == KeyEvent.VK_ENTER) {
            select();
        }
        if (k == KeyEvent.VK_UP) {
            choice--;
            if (choice == -1) {
                choice = options.length - 1;
            }
        }
        if (k == KeyEvent.VK_DOWN) {
            choice++;
            if (choice == options.length) {
                choice = 0;
            }
        }
    }

    @Override
    public void keyReleased(int k) {
    }
}
public class Background {

    private BufferedImage image;
    private double x;
    private double y;

    public Background(String s) {
        try {
            image = ImageIO.read(getClass().getResourceAsStream(s));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setPosition(double x, double y) {
        this.setX(x);
        this.setY(y);
    }

    public void draw(Graphics2D g) {
        g.drawImage(image, 0, 0, null);
    }

    public double getX() {
        return x;
    }

    public void setX(double x) {
        this.x = x;
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }
}