JPanel разрывается, хотя я удваиваю буфер?

РЕДАКТИРОВАТЬ: это 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;
        }
    }
}

У меня есть JPanel с модифицированным методом paintComponent. В этом методе я рисую соответствующие тайлы из двумерного массива на экране в зависимости от местоположения игрока. Я рисую все свои плитки в BufferedImage, а затем применяю его к экрану в конце метода рисования. Теоретически это не должно создавать разрывов, поскольку я использую двойную буферизацию? Вот соответствующий код, изображение — это просто 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);
    }

Я думаю, что разрыв может быть вызван изменением местоположения игрока в течение продолжительности метода рисования, из-за чего плитки не совпадают, чем быстрее игрок движется, тем более очевиден эффект. Однако я получаю местоположение игроков в начале и сохраняю его в двух лонгах, у меня сложилось впечатление, что лонги передаются по значению, а не по ссылке, поэтому местоположение игрока должно быть постоянным, пока метод работает.

Я рад предоставить больше кода :), и спасибо заранее.


person Iain    schedule 05.05.2013    source источник
comment
Чтобы быстрее получить помощь, опубликуйте SSCCE.   -  person Andrew Thompson    schedule 05.05.2013
comment
Изменить public void paintComponent(Graphics g) {.. на public void paintComponent(Graphics g) { super.paintComponent(g)..   -  person Andrew Thompson    schedule 05.05.2013
comment
Только что проверил, и я боюсь, что это не повлияет, у меня сложилось впечатление, что вам нужно делать это только в том случае, если вы рисуете компоненты из качания на панели? Еще одна вещь, которую нужно добавить: я не расширяю свой класс с помощью JPanel, я переопределяю paintComponent после инициализации JPanel в моем конструкторе. Вот так: panel = new JPanel(){ @Override public void paintComponent(Graphics g){ и т.д...   -  person Iain    schedule 05.05.2013
comment
Вызов super также очищает BG, рисует границы и т. д. В принципе, вы не ошибетесь, если воспользуетесь им. Чтобы быстрее получить помощь, опубликуйте SSCCE.   -  person Andrew Thompson    schedule 05.05.2013
comment
Теперь я добавил то, что я считаю SSCCE, чтобы увидеть проблему, просто запустите его и используйте клавиши со стрелками, возможно, также удерживая q.   -  person Iain    schedule 05.05.2013
comment
Я прочитал статью. Судя по всему, я сделал что-то не так, я урезал свой код, чтобы попытаться изолировать проблему, я заменил множество плиток и большое изображение карты на более простые, используя фиктивные данные, как вы предлагаете в своей статье. Я пытался добавить код к моему вопросу напрямую, однако всякий раз, когда я пытался опубликовать, он жаловался на отступы в четыре пробела, которые я сделал, а также у меня недостаточно представителей для загрузки изображений, поэтому я подумал, что было бы лучше сделать zip и загрузите в дропбокс. Извините, если я сделал это неправильно, я искренне думал, что это то, что вы хотели.   -  person Iain    schedule 06.05.2013
comment
Я добавил SSCCE, чтобы продемонстрировать свою проблему, я думаю, что это должно работать само по себе. Чтобы лучше увидеть проблему, посмотрите на один из вертикальных краев карты и используйте клавиши со стрелками для многократного перемещения справа налево, удерживание q поможет.   -  person Iain    schedule 06.05.2013
comment
ОК - после небольшой настройки, чтобы сделать его компилируемым в Java 6 SDK (switch на String - наконец-то ..), я не увидел... никакого разрыва здесь, даже в "быстром" режиме. Может это ваше оборудование?   -  person Andrew Thompson    schedule 06.05.2013
comment
В моем ноутбуке установлена ​​карта nvidia optimus, для большей части графики используется интегрированная карта Intel, а для более ресурсоемких программ используется высокопроизводительная карта nvidia. Я только что пытался запустить свой проект на моей карте nvidia, а не на карте Intel, но пока безуспешно. Я попытался добавить javaw.exe, чтобы он всегда запускался на карте nvidia, но, похоже, это не дало никакого эффекта. Я собираюсь протестировать его на нескольких других компьютерах, которые у меня есть, и посмотреть, не порвется ли он.   -  person Iain    schedule 06.05.2013
comment
Хорошо, я пробовал это на разных компьютерах, и я все еще вижу разрывы или то, что я думаю, разрывы. Вы уверены, что не видите каких-либо графических проблем?   -  person Iain    schedule 07.05.2013
comment
Меня это очень раздражает, поэтому я просто собираюсь переключиться на c++ и SDL, пока не поздно, ха-ха. Я полагаю, что в любом случае это будет более подходящим :), спасибо, что все же нашли время посмотреть на мой код!   -  person Iain    schedule 07.05.2013


Ответы (1)


Просто так, если люди найдут мой вопрос и зададутся вопросом, что я в итоге сделал, чтобы «исправить» его, переключиться на С++ и SDL или какую-либо другую библиотеку изображений, прежде чем вы зайдете далеко в свой проект, это просто лучше для такого рода вещей.

person Iain    schedule 07.05.2013
comment
Почему вы проголосовали против моего ответа? это единственное решение, которое я смог найти. - person Iain; 08.05.2013