Frames painting at different times? [closed]

I have a really annoying bug in my game, the bottom of the frame seems to render earlier than the top of the frame, I'm not sure why it's happening.

I am using a JPanel which does repaint every game loop, my game loop is set to 60FPS. At the start of the paint function, it sets the player X and Y to a variable, which is then used to paint each element, (Since they are painted relative to the player, since the camera follows the player)

I can post any code if needed, to help diagnose the problem, but there is too much code, and I don't know which part of it is the problem; So I am mainly asking if anyone has any idea of what could be wrong, from my explanation.

I cannot post a video of the problem, since it doesn't pick up on video, however feel free to see it yourself in the game, link to game, and virus scan here

If you download the game, then when you open it, enter a name (or leave default) and click no when it asks about a server. When you move around with WASD, you should see a horizontal line flickering effect somewhere on the screen. If the game doesn't open, try again, there is a small chance of it being unable to open (This is a known bug, and im planning to fix it soon)

Sorry for the bad explanation, I find it difficult to describe my problem. I have been stuck with this for hours, and cannot find a solution even after searching the internet for one.

EDIT: Entire Source Code: Here

EDIT2: It requires the kryonet lib, located here

EDIT3: Github


Solution 1:

The is a demonstration of two basic principles, but is basically a series of buffers designed to reduce the amount of work that the paintComponent does...

Generally speaking, it is faster to BLIT a image onto the graphics card then it is to "paint" pixels, with this in mind, this example does two things...

Firstly, it pre-renders the background map. This example just randomly generates the map when it's run, but creates a map which is about 4 times that of full HD.

Secondly, it employees it's own double buffering. The "view" has two buffers, an active and an update. The active buffer is what gets painted to the screen, the update buffer is what's used by the Engine to render the current state of the output...

This is important, because the view's buffers are ALWAYS the same size of the view, so you are never rendering anything that doesn't appear off the screen.

This example pushes the rendering of additional content (like animation, special effects) to the Engine...

I had this example running on my 30" monitor at 2560x1600 with very little issues, the movement delta is very small so I can pan faster, making it large would have nullified these issues...

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestRender {

    public static void main(String[] args) {
        new TestRender();
    }

    public TestRender() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface View {

        public BufferedImage switchBuffers();
        public int getWidth();
        public int getHeight();

    }

    public enum KeyState {
        UP, DOWN, LEFT, RIGHT;
    }

    public class TestPane extends JPanel implements View {

        private Engine engine;

        private BufferedImage active;
        private BufferedImage update;

        private ReentrantLock lckBuffer;

        public TestPane() {
            lckBuffer = new ReentrantLock();
            initBuffers();
            engine = new Engine(this);
            engine.gameStart();

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "up_pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "down_pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "left_pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "right_pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "up_released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "down_released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "left_released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "right_released");

            ActionMap am = getActionMap();
            am.put("up_pressed", new AddState(engine, KeyState.UP));
            am.put("up_released", new RemoveState(engine, KeyState.UP));
            am.put("down_pressed", new AddState(engine, KeyState.DOWN));
            am.put("down_released", new RemoveState(engine, KeyState.DOWN));
            am.put("left_pressed", new AddState(engine, KeyState.LEFT));
            am.put("left_released", new RemoveState(engine, KeyState.LEFT));
            am.put("right_pressed", new AddState(engine, KeyState.RIGHT));
            am.put("right_released", new RemoveState(engine, KeyState.RIGHT));
        }

        protected void initBuffers() {
            if (getWidth() > 0 && getHeight() > 0) {
                try {
                    lckBuffer.lock();
                    active = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
                    update = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
                } finally {
                    lckBuffer.unlock();
                }
            }
        }

        @Override
        public void invalidate() {
            super.invalidate();
            initBuffers();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(1920, 1080);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            try {
                lckBuffer.lock();
                if (active != null) {
                    g2d.drawImage(active, 0, 0, this);
                }
            } finally {
                lckBuffer.unlock();
            }
            g2d.dispose();
        }

        @Override
        public BufferedImage switchBuffers() {
            try {
                lckBuffer.lock();
                BufferedImage tmp = active;
                active = update;
                update = tmp;
                repaint();
            } finally {
                lckBuffer.unlock();
            }
            return update;
        }

    }

    public static class Engine {

        public static final int MAP_WIDTH = 15 * 4;
        public static final int MAP_HEIGHT = 9 * 4;
        public static final int X_DELTA = 32;
        public static final int Y_DELTA = 32;

        //This value would probably be stored elsewhere.
        public static final double GAME_HERTZ = 60.0;
        //Calculate how many ns each frame should take for our target game hertz.
        public static final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
        //We will need the last update time.
        static double lastUpdateTime = System.nanoTime();
        //Store the last time we rendered.
        static double lastRenderTime = System.nanoTime();

        //If we are able to get as high as this FPS, don't render again.
        final static double TARGET_FPS = GAME_HERTZ;
        final static double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;

        //Simple way of finding FPS.
        static int lastSecondTime = (int) (lastUpdateTime / 1000000000);

        public static int fps = 60;
        public static int frameCount = 0;

        private boolean isGameFinished;

        private BufferedImage map;
        private BufferedImage tiles[];

        private View view;

        private int camX, camY;
        private Set<KeyState> keyStates;

        public Engine(View bufferRenderer) {
            keyStates = new HashSet<>(4);
            this.view = bufferRenderer;
            tiles = new BufferedImage[7];
            Random rnd = new Random();
            map = new BufferedImage(MAP_WIDTH * 128, MAP_HEIGHT * 128, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = map.createGraphics();
            for (int row = 0; row < MAP_HEIGHT; row++) {
                for (int col = 0; col < MAP_WIDTH; col++) {
                    int tile = rnd.nextInt(7);
                    int x = col * 128;
                    int y = row * 128;
                    g2d.drawImage(getTile(tile), x, y, null);
                }
            }
            g2d.dispose();
        }

        protected BufferedImage getTile(int tile) {
            BufferedImage img = tiles[tile];
            if (img == null) {
                try {
                    img = ImageIO.read(getClass().getResource("/" + tile + ".png"));
                    img = img.getSubimage(0, 64, 128, 128);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
                tiles[tile] = img;
            }
            return img;
        }

        public void gameStart() {

            Thread gameThread = new Thread() {
                // Override run() to provide the running behavior of this thread.
                @Override
                public void run() {
                    gameLoop();
                }
            };
            gameThread.setDaemon(false);
            // Start the thread. start() calls run(), which in turn calls gameLoop().
            gameThread.start();
        }

        public void gameLoop() {
            BufferedImage buffer = view.switchBuffers(); // initial buffer...
            while (!isGameFinished) {
                double now = System.nanoTime();
                lastUpdateTime += TIME_BETWEEN_UPDATES;
                gameUpdate(buffer);
                renderBuffer(buffer);
                buffer = view.switchBuffers(); // Push the buffer back
                frameCount++;
                lastRenderTime = now;

                int thisSecond = (int) (lastUpdateTime / 1000000000);
                if (thisSecond > lastSecondTime) {
                    fps = frameCount;
                    frameCount = 0;
                    lastSecondTime = thisSecond;
                }

                //Yield until it has been at least the target time between renders. This saves the CPU from hogging.
                while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
                //Thread.yield();

                    //This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
                    //You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
                    //FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
                    try {
                        Thread.sleep(1);
                    } catch (Exception e) {
                    }

                    now = System.nanoTime();
                }
            }
        }

        protected void renderBuffer(BufferedImage buffer) {
            if (buffer != null) {
                Graphics2D g2d = buffer.createGraphics();
                g2d.drawImage(map, camX, camY, null);
                g2d.dispose();
            }
        }

        protected void gameUpdate(BufferedImage buffer) {
            // render transient effects here
            if (keyStates.contains(KeyState.DOWN)) {
                camY -= Y_DELTA;
            } else if (keyStates.contains(KeyState.UP)) {
                camY += Y_DELTA;
            }
            if (camY < -(map.getHeight() - view.getHeight())) {
                camY = -(map.getHeight() - view.getHeight());
            } else if (camY > 0) {
                camY = 0;
            }
            if (keyStates.contains(KeyState.RIGHT)) {
                camX -= Y_DELTA;
            } else if (keyStates.contains(KeyState.LEFT)) {
                camX += Y_DELTA;
            }
            if (camX < -(map.getWidth() - view.getWidth())) {
                camX = -(map.getWidth() - view.getWidth());
            } else if (camX > 0) {
                camX = 0;
            }
        }

        public void addKeyState(KeyState state) {
            keyStates.add(state);
        }

        public void removeKeyState(KeyState state) {
            keyStates.remove(state);
        }

    }

    public class AddState extends AbstractAction {

        private Engine engine;
        private KeyState state;

        public AddState(Engine engine, KeyState state) {
            this.engine = engine;
            this.state = state;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            engine.addKeyState(state);
        }

    }

    public class RemoveState extends AbstractAction {

        private Engine engine;
        private KeyState state;

        public RemoveState(Engine engine, KeyState state) {
            this.engine = engine;
            this.state = state;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            engine.removeKeyState(state);
        }

    }

}

During my experimentation, I did notice that, if you tried to render the content "beyond" the range of the buffer (ie allow the top of the map to slip down inside the buffer area), you would get nasty paint effects, so beware that you are always rendering within the viewable area of the buffer...

There are probably other areas that need tidying up, but this demonstrates the basics...

Solution 2:

Sorry, run out room in my previous answer :P

Update with VolitileImage example

Now, before you get to excited, my experience with VolitileImage is about 20mins more than it took me to create this example, so it might not be the best possible example...

Scrolling

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import java.io.IOException;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import static javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestVolitile {

    public static void main(String[] args) {
        new TestVolitile();
    }

    public TestVolitile() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new ViewPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface View {

        public VolatileImage getOffscreenBuffer();

        public void show(VolatileImage img);

        public boolean isIncompatiable(VolatileImage img);

        public int getWidth();

        public int getHeight();

    }

    public enum KeyState {

        UP, DOWN, LEFT, RIGHT;
    }

    public class ViewPane extends JPanel implements View {

        private VolatileImage offscreen;
        private BufferedImage onscreen;
        private ReentrantLock lckBuffers;

        private Engine engine;

        public ViewPane() {
            lckBuffers = new ReentrantLock();

            engine = new Engine(this);
            engine.gameStart();

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "up_pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "down_pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "left_pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "right_pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "up_released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "down_released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "left_released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "right_released");

            ActionMap am = getActionMap();
            am.put("up_pressed", new AddState(engine, KeyState.UP));
            am.put("up_released", new RemoveState(engine, KeyState.UP));
            am.put("down_pressed", new AddState(engine, KeyState.DOWN));
            am.put("down_released", new RemoveState(engine, KeyState.DOWN));
            am.put("left_pressed", new AddState(engine, KeyState.LEFT));
            am.put("left_released", new RemoveState(engine, KeyState.LEFT));
            am.put("right_pressed", new AddState(engine, KeyState.RIGHT));
            am.put("right_released", new RemoveState(engine, KeyState.RIGHT));
        }

        @Override
        public void invalidate() {
            super.invalidate();
            onscreen = null;
//            offscreen = null;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {

            super.paintComponent(g);
            try {
                lckBuffers.lock();
                // Make sure the buffer is okay for painting....
                if (onscreen != null) {
                    Graphics2D g2d = (Graphics2D) g.create();
                    g2d.drawImage(onscreen, 0, 0, this);
                    g2d.dispose();
                }
            } finally {
                lckBuffers.unlock();
            }

        }

        protected VolatileImage createVolatileImage(int width, int height, int transparency) {

            GraphicsConfiguration gc = getGraphicsConfiguration();
            VolatileImage image = null;

            if (gc != null && width > 0 && height > 0) {

                image = gc.createCompatibleVolatileImage(width, height, transparency);

                int valid = image.validate(gc);

                if (valid == VolatileImage.IMAGE_INCOMPATIBLE) {

                    image = this.createVolatileImage(width, height, transparency);

                }

            }

            return image;

        }

        @Override
        public VolatileImage getOffscreenBuffer() {
            if (isIncompatiable(offscreen)) {
                offscreen = createVolatileImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
            }

            return offscreen;
        }

        @Override
        public void show(VolatileImage img) {

            try {
                lckBuffers.lock();
                GraphicsConfiguration gc = getGraphicsConfiguration();
                if (gc != null) {
                    if (onscreen == null) {
                        onscreen = gc.createCompatibleImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
                    }
                    if (isOkay(img)) {
                        Graphics2D g2d = onscreen.createGraphics();
                        g2d.drawImage(img, 0, 0, this);
                        g2d.dispose();
                        repaint();
                    }
                }
            } finally {
                lckBuffers.unlock();
            }

        }

        @Override
        public boolean isIncompatiable(VolatileImage offscreen) {

            boolean isIncompatiable = true;
            GraphicsConfiguration gc = getGraphicsConfiguration();
            if (gc != null) {
                if (offscreen != null) {
                    if (offscreen.getWidth() == getWidth() && offscreen.getHeight() == getHeight()) {
                        if (offscreen.validate(gc) != VolatileImage.IMAGE_INCOMPATIBLE) {
                            isIncompatiable = false;
                        }
                    }
                }
            }

            return isIncompatiable;

        }

        public boolean isOkay(VolatileImage buffer) {

            boolean isOkay = false;
            GraphicsConfiguration gc = getGraphicsConfiguration();
            if (gc != null) {
                if (buffer != null) {
                    if (buffer.getWidth() == getWidth() && buffer.getHeight() == getHeight()) {
                        if (buffer.validate(gc) == VolatileImage.IMAGE_OK) {
                            isOkay = true;
                        }
                    }
                }
            }

            return isOkay;

        }
    }

    public static class Engine {

        public static final int MAP_WIDTH = 15 * 4;
        public static final int MAP_HEIGHT = 9 * 4;
        public static final int X_DELTA = 4;
        public static final int Y_DELTA = 4;

        public boolean isGameFinished = false;

        //This value would probably be stored elsewhere.
        public static final long GAME_HERTZ = 25;
        //Calculate how many ns each frame should take for our target game hertz.
        public static final long TIME_BETWEEN_UPDATES = Math.round(1000000000 / (double) GAME_HERTZ);
        //We will need the last update time.
        static long lastUpdateTime = System.nanoTime();
        //Store the last time we rendered.
        static long lastRenderTime = System.nanoTime();

        //If we are able to get as high as this FPS, don't render again.
        final static long TARGET_FPS = GAME_HERTZ;
        final static long TARGET_TIME_BETWEEN_RENDERS = Math.round(1000000000 / (double) TARGET_FPS);

        //Simple way of finding FPS.
        static int lastSecondTime = (int) (lastUpdateTime / 1000000000);

        public int fps = 60;
        public int frameCount = 0;

        private View view;
        private int camX, camY;
        private Set<KeyState> keyStates;

        private BufferedImage map;
        private BufferedImage tiles[];

        public Engine(View view) {
            this.view = view;
            keyStates = new HashSet<>(4);
            tiles = new BufferedImage[22];
            Random rnd = new Random();
            GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
            map = gc.createCompatibleImage(MAP_WIDTH * 128, MAP_HEIGHT * 128, Transparency.TRANSLUCENT)

            Graphics2D g2d = map.createGraphics();
            for (int row = 0; row < MAP_HEIGHT; row++) {
                for (int col = 0; col < MAP_WIDTH; col++) {
                    int tile = rnd.nextInt(22);
                    int x = col * 128;
                    int y = row * 128;
                    g2d.drawImage(getTile(tile), x, y, null);
                }
            }
            g2d.dispose();
        }

        protected BufferedImage getTile(int tile) {
            BufferedImage img = tiles[tile];
            if (img == null) {
                try {
                    img = ImageIO.read(getClass().getResource("/" + tile + ".png"));
                    img = img.getSubimage(0, 64, 128, 128);
                    img = toCompatiableImage(img);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
                tiles[tile] = img;
            }
            return img;
        }

        public void gameStart() {

            Thread gameThread = new Thread() {
                // Override run() to provide the running behavior of this thread.
                @Override
                public void run() {
                    gameLoop();
                }
            };
            // Start the thread. start() calls run(), which in turn calls gameLoop().
            gameThread.start();
        }

        public void gameLoop() {
            while (!isGameFinished) {
                long startTime = System.nanoTime();
                lastUpdateTime += TIME_BETWEEN_UPDATES;
                updateGame();
                renerGame();
                frameCount++;
                lastRenderTime = startTime;

                long duration = System.nanoTime() - startTime;

                int thisSecond = (int) (lastUpdateTime / 1000000000);
                if (thisSecond > lastSecondTime) {
                    fps = frameCount;
                    frameCount = 0;
                    lastSecondTime = thisSecond;
                }

                if (duration < TARGET_TIME_BETWEEN_RENDERS) {

                    duration = TARGET_TIME_BETWEEN_RENDERS - duration;
                    long milli = TimeUnit.NANOSECONDS.toMillis(duration);
                    try {
                        Thread.sleep(milli);
                    } catch (InterruptedException ex) {
                    }
                }
            }
        }

        protected void updateGame() {
            if (keyStates.contains(KeyState.DOWN)) {
                camY -= Y_DELTA;
            } else if (keyStates.contains(KeyState.UP)) {
                camY += Y_DELTA;
            }
            if (camY < -(map.getHeight() - view.getHeight())) {
                camY = -(map.getHeight() - view.getHeight());
            } else if (camY > 0) {
                camY = 0;
            }
            if (keyStates.contains(KeyState.RIGHT)) {
                camX -= Y_DELTA;
            } else if (keyStates.contains(KeyState.LEFT)) {
                camX += Y_DELTA;
            }
            if (camX < -(map.getWidth() - view.getWidth())) {
                camX = -(map.getWidth() - view.getWidth());
            } else if (camX > 0) {
                camX = 0;
            }
        }

        protected void renerGame() {
            VolatileImage buffer = view.getOffscreenBuffer();
            if (buffer != null) {
                Graphics2D g2d = null;
                do {

                    if (view.isIncompatiable(buffer)) {
                        buffer = view.getOffscreenBuffer();
                    }

                    try {
                        g2d = buffer.createGraphics();
                    } finally {
                        if (g2d != null) {
                            g2d.drawImage(map, camX, camY, null);
                            // Draw effects here...

                            FontMetrics fm = g2d.getFontMetrics();
                            g2d.setColor(Color.RED);
                            g2d.drawString(Integer.toString(fps), 0, fm.getAscent());
                            g2d.dispose();
                        }
                    }

                } while (buffer.contentsLost());

                view.show(buffer);
            }
        }

        public void addKeyState(KeyState state) {
            keyStates.add(state);
        }

        public void removeKeyState(KeyState state) {
            keyStates.remove(state);
        }

        protected BufferedImage toCompatiableImage(BufferedImage img) {
            GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
            BufferedImage compImg = gc.createCompatibleImage(img.getWidth(), img.getHeight(), img.getTransparency());
            Graphics2D g2d = compImg.createGraphics();
            g2d.drawImage(img, 0, 0, null);
            g2d.dispose();
            return compImg;
        }

    }

    public class AddState extends AbstractAction {

        private Engine engine;
        private KeyState state;

        public AddState(Engine engine, KeyState state) {
            this.engine = engine;
            this.state = state;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            engine.addKeyState(state);
        }

    }

    public class RemoveState extends AbstractAction {

        private Engine engine;
        private KeyState state;

        public RemoveState(Engine engine, KeyState state) {
            this.engine = engine;
            this.state = state;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            engine.removeKeyState(state);
        }

    }
}

Now, if this still gives you trouble, you could try replacing...

g2d.drawImage(map, camX, camY, null);

with...

BufferedImage clip = map.getSubimage(camX * -1, camY * -1, view.getWidth(), view.getHeight());
g2d.drawImage(clip, 0, 0, null);

This reduces any possible "overhang" which might exist within the graphics buffer. Not 100% sure if it will make a difference, but it can't hurt. You could do this in both examples...

If you still have tearing/shearing occurring, you could investigate trying to disabling directx or opengl rendering pipelines (these are command line options) to see if it makes a difference...

Take a look at VolatileImage, The Game Programming Wiki, Java:Tutorials:VolatileImage and Java 2D: Hardware Accelerating - Part 1 - Volatile Images for more ideas.

I also changed your game loop "timing" loop a little, have NO idea if it will make a difference, but Thread.sleep(1) always scares me...

Updated

I've updated the code so that there is only a single VolatileImage. The paintComponent uses an actually BufferedImage, optimised to the GraphicsConfiguration instead. This ensures that contents is always painted an only update as required (within the show method). Should help prevent flickering...

I've also optimised all the tiles as they are loaded to be optimised for the GraphicsConfiguration as well, this means that their color models don't need to be converted when they are rendered to the screen as they are the same, should help save some time...

Take a look at the toCompatiableImage method for more details