JPanel撕裂即使我加倍缓冲?

时间:2013-05-05 16:13:25

标签: java swing jpanel doublebuffered tearing

编辑:这是一个证明我的问题的SSCCE。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

public class Main {
    public static BufferedImage map, tileSand, tileSea;
    public static void main(String[] args) {
        map = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
        for(int x = 0; x < 50 ; x++){
            for(int y = 0; y < 50 ; y++){
                boolean colour = Math.random() < 0.5;
                if (colour){
                    map.setRGB(x, y, -256);
                } else {
                    map.setRGB(x, y, -16776961);
                }
            }
        }
        tileSand = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
        Graphics g = tileSand.getGraphics();
        g.setColor(Color.YELLOW);
        g.fillRect(0, 0, 32, 32);
        tileSea  = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
        g.setColor(Color.BLUE);
        g = tileSea.getGraphics();
        g.fillRect(0, 0, 32, 32);
        Island test = new Main.Island();
        test.start();
        long start, sleep;
        while(true) {
            start  = System.currentTimeMillis();
            test.getCanvas().requestFocus();
            test.getCanvas().repaint();
            sleep = 15-(System.currentTimeMillis()-start);
            try {
                Thread.sleep(sleep > 0 ? sleep : 0);
            } catch (InterruptedException e) {
            }
        }
    }

    static class Island implements Runnable {

        private Tile[][] tiles;
        private JFrame frame;
        private JPanel panel;
        private Thread logicThread;
        private boolean running = false;
        private boolean paused = false;
        private Image image;
        private Player player;

        public Island() {
            image = new BufferedImage(1027, 768, BufferedImage.TYPE_INT_ARGB);
            player = new Player();
            tiles = new Tile[map.getWidth()][map.getHeight()];
            int rgb;
            for(int x = 0; x < map.getWidth(); x++) {
                for(int y = 0; y < map.getHeight(); y++) {
                    rgb = map.getRGB(x, y);
                    switch (rgb) {
                        case -16776961: tiles[x][y] = new Tile("sea");
                                        break;
                        case -256:  tiles[x][y] = new Tile("sand");
                                    break;
                    }
                }
            }
            makeMap();
            makeFrame();
            addBindings();
            logicThread = new Thread(this);
        }

        public JPanel getCanvas() {
            return panel;
        }

        public void start() {
            running = true;
            paused = false;
            logicThread.start();
        }

        public void run() {
            long sleep, before;
            while(running){
                before = System.currentTimeMillis();
                player.move();
                try {
                    sleep = 15-(System.currentTimeMillis()-before);
                    Thread.sleep(sleep > 0 ? sleep : 0);
                } catch (InterruptedException ex) {
                }
                while(running && paused);
            }
            paused = false;
        }

        private void makeFrame() {
            frame = new JFrame("Island");
            panel = new JPanel(){
                @Override
                public void paintComponent(Graphics g) {
                    super.paintComponent(g);
                    Graphics2D g2d = (Graphics2D) image.getGraphics();
                    g2d.setColor(Color.BLACK);
                    g2d.fillRect(0, 0, 1024, 768);
                    long xl, yl;
                    xl = player.getLocation().x-512;
                    yl = player.getLocation().y-384;
                    int x2, y2;
                    x2 = (int) Math.floor(xl / 32);
                    y2 = (int) Math.floor(yl / 32);
                    int xoffset, yoffset;
                    xoffset = (int) (xl % 32);
                    yoffset = (int) (yl % 32);
                    for(int x = x2; x < x2+40; x++) {
                        for(int y = y2; y < y2+40; y++) {
                            if (x < tiles.length && x > 0 && y < tiles[0].length && y > 0) {
                                g2d.drawImage(tiles[x][y].getContent(), (x-x2)*32 - xoffset, (y-y2)*32 - yoffset, null);
                            }
                        }
                    }
                    g.drawImage(image, 0, 0, null);
                }
            };
            panel.setPreferredSize(new Dimension(1024, 768));
            panel.setIgnoreRepaint(true);
            frame.add(panel);
            frame.pack();
            frame.setVisible(true);
        }

        private void addBindings() {
            InputMap inputMap = panel.getInputMap();
            ActionMap actionMap = panel.getActionMap();
            inputMap.put(KeyStroke.getKeyStroke("Q"),"hold-sprint");
            actionMap.put("hold-sprint", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    player.sprint(true);
                }
            });
            inputMap.put(KeyStroke.getKeyStroke("released Q"),"release-sprint");
            actionMap.put("release-sprint", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    player.sprint(false);
                }
            });
            inputMap.put(KeyStroke.getKeyStroke("RIGHT"),"hold-right");
            actionMap.put("hold-right", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    player.right(true);
                }
            });
            inputMap.put(KeyStroke.getKeyStroke("released RIGHT"),"release-right");
            actionMap.put("release-right", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    player.right(false);
                }
            });
            inputMap.put(KeyStroke.getKeyStroke("DOWN"),"hold-down");
            actionMap.put("hold-down", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    player.down(true);
                }
            });
            inputMap.put(KeyStroke.getKeyStroke("released DOWN"),"release-down");
            actionMap.put("release-down", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    player.down(false);
                }
            });
            inputMap.put(KeyStroke.getKeyStroke("LEFT"),"hold-left");
            actionMap.put("hold-left", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    player.left(true);
                }
            });
            inputMap.put(KeyStroke.getKeyStroke("released LEFT"),"release-left");
            actionMap.put("release-left", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    player.left(false);
                }
            });
            inputMap.put(KeyStroke.getKeyStroke("UP"),"hold-up");
            actionMap.put("hold-up", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    player.up(true);
                }
            });
            inputMap.put(KeyStroke.getKeyStroke("released UP"),"release-up");
            actionMap.put("release-up", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    player.up(false);
                }
            });
        }

        private void makeMap() {
            for(int x = 0; x < tiles.length; x++) {
                for(int y = 0; y < tiles[0].length; y++) {
                    switch (tiles[x][y].getType()) {
                        case "sea": tiles[x][y].setContent(tileSea);
                                    break;
                        case "sand":    tiles[x][y].setContent(tileSand);
                                        break;
                    }
                }   
            }
        }
    }

    static class Player{
        private Point location;
        private int xspeed, yspeed;
        private boolean left, up, right, down, sprint;

        public Player() {
            location = new Point(0, 0);
            left = false;
            up = false;
            right = false;
            down = false;
            sprint = false;
            xspeed = yspeed = 0;
        }
        public void left(boolean state){
            left = state;
        }

        public void up(boolean state){
            up = state;
        }

        public void right(boolean state){
            right = state;
        }

        public void down(boolean state){
            down = state;
        }

        public void sprint(boolean state) {
            sprint = state;
        }

        public void move() {
            if (sprint) {
                if (left) {
                    if(xspeed>-10)
                        xspeed--;
                } if (right) {
                    if(xspeed<10)
                        xspeed++;
                } if (up) {
                    if(yspeed>-10)
                        yspeed--;
                } if (down) {
                    if(yspeed<10)
                        yspeed++;
                }
            } else {
                if (left) {
                    if(xspeed>-5)
                        xspeed--;
                } if (right) {
                    if(xspeed<5)
                        xspeed++;
                } if (up) {
                    if(yspeed>-5)
                        yspeed--;
                } if (down) {
                    if(yspeed<5)
                        yspeed++;
                }
            }
            if (!sprint) {
                if (xspeed > 5) {
                    xspeed--;
                } if (xspeed < -5) {
                    xspeed++;
                }   if (yspeed > 5) {
                    yspeed--;
                } if (yspeed < -5) {
                    yspeed++;
                }
            }
            if (!left && !right) {
                if (xspeed > 0) {
                    xspeed--;
                } if (xspeed < 0) {
                    xspeed++;
                }
            } if (!up && !down) {
                if (yspeed > 0) {
                    yspeed--;
                } if (yspeed < 0) {
                    yspeed++;
                }
            }
            location.x = location.x + xspeed;
            location.y = location.y + yspeed;
        }

        public Point getLocation() {
            return location;
        }
    }

    static class Tile {
        private String type;
        private BufferedImage tile;

        public Tile(String type) {
            this.type = type;
        }

        public String getType() {
            return type;
        }

        public void setContent(BufferedImage newTile) {
            tile = newTile;
        }

        public BufferedImage getContent() {
            return tile;
        }
    }
}

我有一个带有修改的paintComponent方法的JPanel。在这种方法中,我根据玩家位置将相关的图块从2d数组绘制到屏幕上。我将所有瓷砖绘制到BufferedImage,然后在paint方法结束时将其应用到屏幕上。从理论上讲,这不应该造成撕裂,因为我是双缓冲?这是适当的代码,image只是一个具有屏幕尺寸的BufferedImage:

    public void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) image.getGraphics();
        g2d.setColor(Color.BLACK);
        g2d.fillRect(0, 0, 1024, 768);
        long xl, yl;
        xl = player.getLocation().x-512;
        yl = player.getLocation().y-384;
        int x2, y2;
        x2 = (int) Math.floor(xl / 32);
        y2 = (int) Math.floor(yl / 32);
        int xoffset, yoffset;
        xoffset = (int) (xl % 32);
        yoffset = (int) (yl % 32);
        for(int x = x2; x < x2+40; x++) {
            for(int y = y2; y < y2+40; y++) {
                if (x < tiles.length && x > 0 && y < tiles[0].length && y > 0) {
                    g2d.drawImage(tiles[x][y].getContent(), (x-x2)*32 - xoffset, (y-y2)*32 - yoffset, null);
                }
            }
        }
        g.drawImage(image, 0, 0, null);
    }

我认为撕裂可能是由于玩家的位置在油漆方法的持续时间内发生变化导致瓷砖不匹配,玩家越快,效果越明显。然而,我在开始时获得玩家位置并将其存储在两个长度中,我的印象是longs是通过值而不是引用传递的,因此玩家位置应该在方法运行时保持不变。

我很乐意提供更多代码:),并提前感谢。

1 个答案:

答案 0 :(得分:-1)

如果人们发现我的问题,并想知道我最终做了什么来“修复”它,切换到c ++和SDL或其他图像库,然后再深入到你的项目中,这对于这种类型的事情。