I was wondering if someone could quickly skim over this for me really quickly and tell me if there's anything I could improve code-wise. What I have right now seems pretty big and clustered, and I'm almost positive that there's a way it can be compressed and made more efficient, and maybe even faster. I'm also hopeful for a way to remove the threads in my code if possible. Thanks for the help :PBy the way, this game is an attempted remake of The World's Hardest Game by Snubby Land and Armor Games, and it can be played here: http://armorgames.com/play/1043/the-worlds-hardest-gameGame.javapackage net.thedanpage.worldshardestgame;import java.awt.BasicStroke;import java.awt.Color;import java.awt.Dialog;import java.awt.Dimension;import java.awt.Font;import java.awt.FontMetrics;import java.awt.GradientPaint;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Image;import java.awt.Toolkit;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.font.FontRenderContext;import java.awt.font.TextLayout;import java.awt.geom.AffineTransform;import java.io.BufferedReader;import java.io.File;import java.io.FileReader;import java.io.IOException;import java.io.PrintWriter;import java.io.StringWriter;import java.text.SimpleDateFormat;import java.util.Date;import java.util.logging.Level;import java.util.logging.Logger;import javax.swing.ImageIcon;import javax.swing.JFrame;import javax.swing.JOptionPane;import javax.swing.JPanel;import javax.swing.Timer;import kuusisto.tinysound.Music;import kuusisto.tinysound.Sound;import kuusisto.tinysound.TinySound;public class Game extends JPanel implements ActionListener {    /** An instance of the game. */    private static Game game;    /** The timer used for the game's clock. */    private Timer t = new Timer(5, this);    /** Used for logging information during the game. */    public final static Logger logger = Logger.getLogger(Game.class.getName());    static String logFilePath = System.getProperty(user.home)              + /worldshardestgame/logs/ +  new SimpleDateFormat(YY-MM-dd).format(new Date()) + .log;    private static final long serialVersionUID = 1L;    /** The frame that the panel goes in. */    static JFrame frame = new JFrame();    /** The enum instance used for switching the state of the game. */    static final int INTRO = 0, MAIN_MENU = 1, LEVEL_TITLE = 2, LEVEL = 3;    /** The integer used for the game state. */    static int gameState = INTRO;    /** Used for when the instructions should be shown. */    private boolean showIntro = false;    /** This is the level that the player is on. */    static int levelNum = 0;    /** A player class, used to get information about the player. */    private Player player = new Player();    /** The data of the current level. This should be given data in initLevel(). */    static GameLevel level = new GameLevel();    /** Controls whether the game has sound or not. */    static boolean muted = false;    /** Images for indicating volume. */    private final Image VOLUME_BLACK = new ImageIcon(ClassLoader.getSystemResource(            net/thedanpage/worldshardestgame/resources/volume_black.png)).getImage();    private final Image VOLUME_BLACK_MUTE = new ImageIcon(ClassLoader.getSystemResource(            net/thedanpage/worldshardestgame/resources/volume_black_mute.png)).getImage();    private final Image VOLUME_WHITE = new ImageIcon(ClassLoader.getSystemResource(            net/thedanpage/worldshardestgame/resources/volume_white.png)).getImage();    private final Image VOLUME_WHITE_MUTE = new ImageIcon(ClassLoader.getSystemResource(            net/thedanpage/worldshardestgame/resources/volume_white_mute.png)).getImage();    /** Background music. */    static Thread bgMusic = new Thread() {        public void run() {            TinySound.init();            Music bgmusic = TinySound.loadMusic(ClassLoader.getSystemResource(                    net/thedanpage/worldshardestgame/resources/music.ogg));            bgmusic.play(true);        }    };    Thread endIntro = new Thread() {        public void run() {            try {                Thread.sleep(1500);            } catch (InterruptedException e) {                TextFileWriter.appendToFile(logFilePath, e.getMessage());            }            gameState = MAIN_MENU;            easyLog(logger, Level.INFO, Game state set to MAIN_MENU);        }    };    static boolean doLogging = false;    private static int totalLevels = 0;    //Intro objects    /** True if the intro text should move down. */    private boolean fadeOutIntro = false;    /** The opacity of the intro text. */    private int introTextOpacity = 0;    /** A whoosh sound. */    Sound drone = TinySound.loadSound(ClassLoader.getSystemResource(net/thedanpage/worldshardestgame/resources/drone.ogg));    /** A bell sound. */    Sound bell = TinySound.loadSound(ClassLoader.getSystemResource(net/thedanpage/worldshardestgame/resources/bell.wav));    public void paintComponent(final Graphics g) {        super.paintComponent(g);        update(g);        render(g);        //Start the timer        t.start();        Toolkit.getDefaultToolkit().sync();    }    /** Update the game.     *      * @param g     * */    public void update(Graphics g) {        if (gameState == INTRO) {            if (introTextOpacity == 0 && !fadeOutIntro) {                drone.play();            }            if (introTextOpacity < 255 && !fadeOutIntro) {                introTextOpacity += 255/10;                if (introTextOpacity > 255) introTextOpacity = 255;            }            if (introTextOpacity == 225) {                new Thread() {                    public void run() {                        try {                            Thread.sleep(3500);                        } catch (InterruptedException e) {                            TextFileWriter.appendToFile(logFilePath, e.getMessage());                        }                        fadeOutIntro = true;                        bgMusic.start();                    }                }.start();            }            if (fadeOutIntro) {                if (introTextOpacity > 0) {                    introTextOpacity -= 255/20;                    if (introTextOpacity < 0) introTextOpacity = 0;                }            }            if (fadeOutIntro && introTextOpacity == 0 && !endIntro.isAlive()) {                endIntro.start();            }        } else if (gameState == MAIN_MENU) {            if (showIntro) {                if (Input.enter.isPressed) {                    showIntro = false;                    gameState = LEVEL_TITLE;                    easyLog(logger, Level.INFO, Game state set to LEVEL_TITLE);                    player.reset();                    levelNum = 1;                    level.init(player, levelNum);                    //Wait 1.75 seconds then start the level.                    new Thread() {                        public void run() {                            try { Thread.sleep(1750); } catch (InterruptedException e) { TextFileWriter.appendToFile(logFilePath, e.getMessage()); }                            gameState = LEVEL;                            easyLog(logger, Level.INFO, Game state set to LEVEL);                        }                    }.start();                }            } else {                //Click to start the first level                if (Input.mousePressed && Input.mouseCoords.x > 304 && Input.mouseCoords.y < 323                        && Input.mouseCoords.x < 515 && Input.mouseCoords.y > 192) {                    showIntro = true;                    bell.play();                }               }        } else if (gameState == LEVEL) {            if (Input.mouseOnWindow && Input.mouseCoords.x <= 65 && Input.mouseCoords.y <= 22                    && Input.mousePressed) {                gameState = MAIN_MENU;                easyLog(logger, Level.INFO, Game state set to MAIN_MENU);            }        }    }    /** Draw the game's graphics.     *      * @param g     */    private void render(Graphics g) {        Graphics2D g2 = (Graphics2D) g;        if (gameState == INTRO) {            //Background            g2.setPaint(new GradientPaint(0, 0, new Color(213, 213, 255), 0, 600, Color.WHITE));            g2.fillRect(0, 0, 800, 600);            g2.setFont(new Font(Tahoma, Font.BOLD, 50));            g2.setColor(new Color(0, 0, 0, introTextOpacity));            drawCenteredString(Made by Dan95363, 400, 250, g2);        } else if (gameState == MAIN_MENU) {            if (showIntro) {                //Instructions                g2.setFont(new Font(Tahoma, Font.BOLD, 20));                g2.setColor(Color.BLACK);                drawString(You are the red square. Avoid the blue circles and collect the\n +                           yellow circles. Once you have collected all of the yellow\n +                           circles, move to the green beacon to complete the level.\n +                           Some levels consist of more than one beacon; the\n +                           intermediary beacons act as checkpoints. You must complete\n +                           all 30 levels in order to submit your score. Your score is a\n +                           reflection of how many times you have died; the less, the better., 30, 40, g2);                g2.setColor(Color.BLUE);                drawCenteredString(Press enter to continue, 400, 350, g2);            } else {                //Background                g2.setPaint(new GradientPaint(0, 0, new Color(213, 213, 255), 0, 600, Color.WHITE));                g2.fillRect(0, 0, 800, 600);                //Draw and outline the title                g2.setPaint(Color.BLACK);                g2.setFont(new Font(SansSerif, Font.BOLD, 32));                g2.drawString(The world's..., 40, 60);                g2.setPaint(new Color(66, 117, 192));                g2.setFont(new Font(SansSerif, Font.BOLD, 80));                g2.drawString(HARDEST GAME, 40, 145);                g2.setPaint(Color.BLACK);                drawTextOutline(HARDEST GAME, 40, 145, 5, g2);                g2.setFont(new Font(SansSerif, Font.BOLD, 60));                //Gradient of play game text depending on the mouse location                if (Input.mouseCoords.x > 284 && Input.mouseCoords.y < 343                        && Input.mouseCoords.x < 515 && Input.mouseCoords.y > 192) {                    g2.setPaint(new GradientPaint(0, 175, new Color(220, 220, 220), 0, 255, new Color(190, 60, 60)));                } else {                    g2.setPaint(new GradientPaint(0, 175, Color.WHITE, 0, 255, Color.RED));                }                //Draw and outline the play game text                drawCenteredString(PLAY, 400, 255, g2);                drawCenteredString(GAME, 400, 320, g2);                g2.setColor(Color.BLACK);                drawTextOutline(PLAY, 315, 255, 3, g2);                drawTextOutline(GAME, 302, 320, 3, g2);            }        } else if (gameState == LEVEL) {            if (levelNum != 0) {                level.drawTiles(g);                level.drawCoins(g);                level.drawDots(g);                level.updateDots();                player.draw(g);                player.update(level);                g.setColor(Color.BLACK);                g.fillRect(0, 0, 800, 22);                g.setColor(Color.WHITE);                g.setFont(new Font(Tahoma, Font.BOLD, 18));                drawRightJustifiedString(Deaths:  + player.getDeaths(), 750, 17, g);                drawCenteredString(levelNum + / + totalLevels, 400, 17, g);                if (Input.mouseOnWindow && Input.mouseCoords.x <= 65 && Input.mouseCoords.y <= 22) {                    g.setColor(Color.LIGHT_GRAY);                }                g.drawString(MENU, 0, 17);                if (muted) {                    g.drawImage(VOLUME_WHITE_MUTE, 760, -12, null);                } else {                    g.drawImage(VOLUME_WHITE, 760, -12, null);                }            }        } else if (gameState == LEVEL_TITLE) {            //Background            g2.setPaint(new GradientPaint(0, 0, new Color(213, 213, 255), 0, 600, Color.WHITE));            g2.fillRect(0, 0, 800, 600);            //Draw the title text            g2.setFont(new Font(Tahoma, Font.BOLD, 48));            g.setColor(Color.BLACK);            int textY = 200;            for (String s : level.getTitle().split(\n)) {                drawCenteredString(s, 400, textY += g.getFontMetrics().getHeight(), g);            }        }        if (gameState != LEVEL) {            if (muted) {                g.drawImage(VOLUME_BLACK_MUTE, 760, -12, null);            } else {                g.drawImage(VOLUME_BLACK, 760, -12, null);            }        }        g.dispose();    }    public void actionPerformed(ActionEvent arg0) {        repaint();    }    /** Draw a string centered on its x axis.     *      * @param text     *      the text to be drawn     * @param x     *      the x coordinate of the text     * @param y     *      the y coordinate of the text     * @param g     *      the graphics the text will be drawn with     */    private void drawCenteredString(String s, int w, int h, Graphics g) {        FontMetrics fm = g.getFontMetrics();        int x = (w*2 - fm.stringWidth(s)) / 2;        g.drawString(s, x, h);    }    /** Draw a string centered on its x axis.     *      * @param text     *      the text to be drawn     * @param x     *      the x coordinate of the text     * @param y     *      the y coordinate of the text     * @param g2     *      the 2D graphics the text will be drawn with     */    private void drawCenteredString(String s, int w, int h, Graphics2D g2) {        FontMetrics fm = g2.getFontMetrics();        int x = (w*2 - fm.stringWidth(s)) / 2;        g2.drawString(s, x, h);    }    /** Draw a right-justified string.     *      * @param text     *      the text to be drawn     * @param x     *      the x coordinate of the text     * @param y     *      the y coordinate of the text     * @param g2     *      the 2D graphics the text will be drawn with     */    private void drawRightJustifiedString(String s, int w, int h, Graphics g) {        FontMetrics fm = g.getFontMetrics();        int x = (w - fm.stringWidth(s));        g.drawString(s, x, h);    }    /** Draw the outline of a string of text.     *      * @param text     *      the text to be drawn     * @param x     *      the x coordinate of the text     * @param y     *      the y coordinate of the text     * @param thickness     *      the thickness of the outline     * @param g2     *      the 2D graphics the text will be drawn with     */    private void drawTextOutline(String text, int x, int y, int thickness, Graphics2D g2) {        TextLayout tl = new TextLayout(text, g2.getFont(), new FontRenderContext(null,false,false));        AffineTransform textAt = new AffineTransform();        textAt.translate(x, y);        g2.setStroke(new BasicStroke(thickness));        g2.draw(tl.getOutline(textAt));        g2.setStroke(new BasicStroke());    }    /** Draw a string, with the use of \n implemented.     *      * @param text     *      the text to be drawn     * @param x     *      the x coordinate of the text     * @param y     *      the y coordinate of the text     * @param g     *      the graphics the text will be drawn with     */    private void drawString(String text, int x, int y, Graphics g) {        for (String line : text.split(\n))            g.drawString(line, x, y += g.getFontMetrics().getHeight());    }    /**     * Convert an exception to a String with full stack trace     *      * @param ex     *            the exception     * @return A string with the full stacktrace error text     */    public static String getStringFromStackTrace(Throwable ex) {        if (ex == null) {            return ;        }        StringWriter str = new StringWriter();        PrintWriter writer = new PrintWriter(str);        try {            ex.printStackTrace(writer);            return str.getBuffer().toString();        } finally {            try {                str.close();                writer.close();            } catch (IOException e) {                // ignore            }        }    }    /**     * Easily log a string of text, and write it to the log file     *      * @param logger     *      The logger for the string to be logged with     * @param level     *      The level of the logger     * @param s     *      The string of text to be logged     */    static void easyLog(Logger logger, Level level, String s) {        if (doLogging) {            logger.setLevel(level);            if (level == Level.CONFIG) logger.config(s);            else if (level == Level.FINE) logger.fine(s);            else if (level == Level.FINER) logger.finer(s);            else if (level == Level.FINEST) logger.finest(s);            else if (level == Level.INFO) logger.info(s);            else if (level == Level.SEVERE) logger.severe(s);            else if (level == Level.WARNING) logger.warning(s);            else {                logger.setLevel(Level.WARNING);                logger.warning(Logging error);            }            TextFileWriter.appendToFile(logFilePath, new SimpleDateFormat(                    MMM dd, YYYY h:mm:ss a).format(new Date())                    +  net.thedanpage.worldshardestgame easyLog\n + level + :  + s);        }    }    public static void main(String[] args) {        int option = JOptionPane.showConfirmDialog(                new Dialog(frame, true),                Would you like to enable logging to  + System.getProperty(user.home) + /worldshardestgame/logs?,                Setup,                JOptionPane.YES_NO_OPTION);        if (option == JOptionPane.YES_OPTION) Game.doLogging = true;        else Game.doLogging = false;        if (Game.doLogging) {            //Create directory for logs if it does not exist            if (!new File(System.getProperty(user.home) + /worldshardestgame/logs).isDirectory()) {                new File(System.getProperty(user.home) + /worldshardestgame/logs).mkdirs();            }            if (new File(Game.logFilePath + .zip).exists()) {                LogZipper.unzip(                    System.getProperty(user.home) + /worldshardestgame/logs, Game.logFilePath + .zip);                new File(Game.logFilePath + .zip).delete();            }            try {                if (new File(Game.logFilePath).exists() && new BufferedReader(new FileReader(Game.logFilePath)).readLine() != null) {                    TextFileWriter.appendToFile(Game.logFilePath, \n);                }            } catch (IOException e) {                Game.easyLog(Game.logger, Level.WARNING, Game.getStringFromStackTrace(e));            }        }        try {            while (new File(ClassLoader                    .getSystemResource(net/thedanpage/worldshardestgame/resources/maps/level_ + (totalLevels+1) + .txt).toURI())                            .exists()) {                totalLevels++;            }        } catch (Exception e) {            System.out.println(Total levels:  + totalLevels);        }        Game.easyLog(Game.logger, Level.INFO, Starting The World's Hardest Game);        TinySound.init();        Game.easyLog(Game.logger, Level.INFO, TinySound initialized);        if (Game.muted) TinySound.setGlobalVolume(0);        Input.init();        Game.easyLog(Game.logger, Level.INFO, Input initialized);        frame.setTitle(World's Hardest Game);        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        frame.setSize(new Dimension(800, 622));        frame.setResizable(false);        frame.setLocationRelativeTo(null);        game = new Game();        frame.add(game);        frame.setIconImage(new ImageIcon(ClassLoader.getSystemResource(net/thedanpage/worldshardestgame/resources/favicon.png)).getImage());        frame.setVisible(true);    }}GameLevel.javapackage net.thedanpage.worldshardestgame;import java.awt.Color;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Point;import java.awt.Rectangle;import java.awt.geom.Area;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.Scanner;import java.util.logging.Level;public class GameLevel {    @Override    public String toString() {        return GameLevel [spawnPoint= + spawnPoint + , id= + id                + , levelTitle= + levelTitle + , tileMap= + tileMap                + , dots= + dots + , coins= + coins + , levelArea=                + levelArea + ];    }    /** Spawn point of the level. */    private Point spawnPoint;    /** The ID of the level. The first level has an ID of 1. */    private int id;    /** The message that is displayed before the level. */    private String levelTitle;    /** A list of all of the level's tiles. */    private ArrayList<Tile> tileMap;    /** A list of all of the level's dots. */    public ArrayList<Dot> dots;    /** A list of all of the level's coins. **/    public ArrayList<Coin> coins;    /** The area of the level, not including background tiles. */    Area levelArea;    public GameLevel() {        this.levelArea = new Area();        this.tileMap = new ArrayList<Tile>();        this.dots = new ArrayList<Dot>();        this.spawnPoint = new Point(20, 20);        this.id = -1;        this.levelTitle = \Intimidating message\nhere\;    }    public GameLevel(Point spawn, int id) {        this.levelArea = new Area();        this.tileMap = new ArrayList<Tile>();        this.dots = new ArrayList<Dot>();        this.spawnPoint = spawn;        this.id = id;        this.levelTitle = \Intimidating message\nhere\;    }    /**     * @return spawnPoint     */    public Point getSpawnPoint() {        return this.spawnPoint;    }    /**     * @return id     */    public int getID() {        return this.id;    }    /**     * @return tileMap     */    public ArrayList<Tile> getTileMap() {        return this.tileMap;    }    /**     * @return levelTitle     */    public String getTitle() {        return this.levelTitle;    }    /** Draw the tiles based on a text file in the maps package.      *      *      * */    public void drawTiles(Graphics g) {        Graphics2D g2 = (Graphics2D) g;        try {            /*for (Tile t : this.tileMap) {                //Background                if (t.getType() == 0) {                    g.setColor(new Color(180, 181, 254));                    g.fillRect(t.getX(), t.getY(), 40, 40);                }            }*/            g.setColor(new Color(180, 181, 254));            g.fillRect(0, 22, 800, 622);            //Border around level            g2.setColor(Color.BLACK);            g2.fill(this.levelArea);            for (Tile t : this.tileMap) {                t.draw(this, g);            }        } catch (Exception e) {            System.out.println(File not found.);            TextFileWriter.appendToFile(Game.logFilePath, Game.getStringFromStackTrace(e));        }    }    public void drawDots(Graphics g) {        for (Dot dot : this.dots) dot.draw(g);    }    public void updateDots() {        if (this.dots != null)            for (Dot dot : this.dots) dot.update();    }    public void drawCoins(Graphics g) {        if (this.coins != null)            for (Coin coin : this.coins) coin.draw(g);    }    public boolean allCoinsCollected() {        if (this.coins != null) {            for (Coin coin : this.coins) {                if (!coin.collected) return false;            }        }        return true;    }    /**     * Load the current level data from     * net.thedanpage.worldshardestgame.resources.maps     */    public void init(Player player, int levelNum) {        if (ClassLoader.getSystemResource(net/thedanpage/worldshardestgame/resources/maps/level_ + levelNum + .txt) != null)            Game.easyLog(Game.logger, Level.INFO, The file for level  + levelNum +  has been found);        else Game.easyLog(Game.logger, Level.SEVERE, The file for level  + levelNum +  could not be found);        Game.easyLog(Game.logger, Level.INFO, Level  + Game.levelNum +  is being initialized);        //Clears the tile data        this.tileMap = new ArrayList<Tile>();        //Clears the dot data        this.dots = new ArrayList<Dot>();        //Clears the coin data        this.coins = new ArrayList<Coin>();        //Clears the level area data        this.levelArea = new Area();        //Resets the level title        this.levelTitle = \Intimidating message\nhere\;        try {            this.spawnPoint = new Point(                            Integer.parseInt(PropLoader                                    .loadProperty(spawn_point,                                            net/thedanpage/worldshardestgame/resources/maps/level_ + levelNum + .properties)                                    .split(,)[0]) * 40 + 20,                            Integer.parseInt(PropLoader                                    .loadProperty(spawn_point,                                            net/thedanpage/worldshardestgame/resources/maps/level_ + levelNum + .properties)                                    .split(,)[1]) * 40 + 20);            this.id = Integer.parseInt(PropLoader.loadProperty(level_id,                    net/thedanpage/worldshardestgame/resources/maps/level_                            + levelNum + .properties));            this.levelTitle = PropLoader.loadProperty(level_title,                     net/thedanpage/worldshardestgame/resources/maps/level_                            + levelNum + .properties).toString();            String coinData = null;            //Retrieves the coin data            if (PropLoader.loadProperty(coins,                     net/thedanpage/worldshardestgame/resources/maps/level_                            + levelNum + .properties) != null) {                coinData = PropLoader.loadProperty(coins,                         net/thedanpage/worldshardestgame/resources/maps/level_                                + levelNum + .properties);            }            if (coinData != null) {                coinData = coinData.replaceAll(\\Z, );                if (coinData.contains(-)) {                    String[] coins = coinData.split(-);                    for (String s : coins) this.coins.add(new Coin((int) (Double.parseDouble(s.split(,)[0]) * 40),                            (int) (Double.parseDouble(s.split(,)[1]) * 40)));                } else this.coins.add(new Coin((int) (Double.parseDouble(coinData.split(,)[0]) * 40),                        (int) (Double.parseDouble(coinData.split(,)[1]) * 40)));            }            Game.easyLog(Game.logger, Level.INFO, All coins have been added);            //Retrieves the tile data            InputStreamReader isr = new InputStreamReader(ClassLoader                    .getSystemResource(                            net/thedanpage/worldshardestgame/resources/maps/level_                                    + levelNum + .txt).openStream());            String content = ;            Scanner scanner = new Scanner(isr);            content = scanner.useDelimiter(\\Z).next();            scanner.close();            Game.easyLog(Game.logger, Level.INFO, Level data:\n\n + content);            content = content.replaceAll(\n, );            for (int i = 0; i < content.length(); i++) {                if (i > 299)                    break;                else                    this.tileMap.add(new Tile((i % 20) * 40, (i / 20) * 40,                            Character.getNumericValue(content.charAt(i))));            }            this.levelArea = new Area();            for (Tile t : this.tileMap) {                if (t.getType() != 0) {                    this.levelArea.add(new Area(                            new Rectangle(t.getX() - 3, t.getY() - 3 + 22, 46, 46)));                }            }        } catch (Exception e) {            Game.easyLog(Game.logger, Level.SEVERE, Map unable to be loaded:\n + Game.getStringFromStackTrace(e));        }        //Retrieves the dot data        try {            InputStreamReader isr = new InputStreamReader(ClassLoader                    .getSystemResource(                            net/thedanpage/worldshardestgame/resources/maps/level_                                    + levelNum + .txt).openStream());            Scanner scanner = new Scanner(isr);            String content = scanner.useDelimiter(\\Z).next();            String[] lines = content.split(\n);            scanner.close();            for (int i=19; lines[i] != null; i++) {                String line = lines[i];                String[] dotData = line.replaceAll( , ).split(-);                this.dots.add(new Dot(                            Integer.parseInt(dotData[0]),                            Integer.parseInt(dotData[1]),                            new Point(Integer.parseInt(dotData[2].split(,)[0]),                                      Integer.parseInt(dotData[2].split(,)[1])),                            new Point(Integer.parseInt(dotData[3].split(,)[0]),                                      Integer.parseInt(dotData[3].split(,)[1])),                            Double.parseDouble(dotData[4]),                            Boolean.parseBoolean(dotData[5]),                            Boolean.parseBoolean(dotData[6])                        ));            }            Game.easyLog(Game.logger, Level.INFO, All dots have been added);        } catch (Exception e) {            if (e.getClass().getName() != java.lang.ArrayIndexOutOfBoundsException)                Game.easyLog(Game.logger, Level.SEVERE, Dots unable to be loaded:\n + Game.getStringFromStackTrace(e));        }        if (this.tileMap.size() == 300) Game.easyLog(Game.logger, Level.INFO, All tiles have been added);        else Game.easyLog(Game.logger, Level.WARNING, Not all tiles were added);        player.respawn(this);    }}Player.javapackage net.thedanpage.worldshardestgame;import java.awt.Color;import java.awt.Graphics;import java.awt.Rectangle;import java.awt.Shape;import java.util.ArrayList;import java.util.logging.Level;import kuusisto.tinysound.Sound;import kuusisto.tinysound.TinySound;public class Player {    /** The X coordinate of the player. */    private int x;    /** The Y coordinate of the player. */    private int y;    /**     * The X coordinate of the player, snapped to the grid of 40x40 tiles.     * snapX = x/40     */    private int snapX;    /**     * The Y coordinate of the player, snapped to the grid of 40x40 tiles.     * snapY = y/40     */    private int snapY;    /** True if the player is colliding with a tile above them. */    private boolean collidingUp;    /** True if the player is colliding with a tile below them. */    private boolean collidingDown;    /** True if the player is colliding with a tile to their left. */    private boolean collidingLeft;    /** True if the player is colliding with a tile to their right. */    private boolean collidingRight;    /** The smack sound. */    Sound smack;    /** The number of times the player has died. */    private int deaths;    /** True if the player has been hit and is not allowed to move. */    private boolean dead;    /** The opacity of the player. */    private double opacity;    public Player() {        this.x = 400;        this.y = 300;        this.snapX = x/40;        this.snapY = y/40;        this.collidingUp = false;        this.collidingDown = false;        this.collidingLeft = false;        this.collidingRight = false;        this.deaths = 0;        this.dead = false;        this.opacity = 255;    }    public Player(int x, int y) {        this.x = x;        this.y = y;        this.snapX = x/40;        this.snapY = y/40;        this.collidingUp = false;        this.collidingDown = false;        this.collidingLeft = false;        this.collidingRight = false;        this.deaths = 0;        this.dead = false;        this.opacity = 255;    }    public void draw(Graphics g) {        g.setColor(new Color(0, 0, 0, (int) opacity));        g.fillRect(x - 15, y - 15 + 22, 31, 31);        g.setColor(new Color(255, 0, 0, (int) opacity));        g.fillRect(x-12, y-12 + 22,                   25, 25);    }    Tile getRelativeTile(GameLevel level, int x1, int y1, int xOff, int yOff) {        for (Tile t : level.getTileMap()) {            if (x1/40 + xOff == t.getSnapX() && y1/40 + yOff == t.getSnapY()) {                return t;            }        }        return null;    }    Tile getTile(GameLevel level) {        for (Tile t : level.getTileMap()) {            if (this.x/40 == t.getSnapX() && this.y/40 == t.getSnapY()) {                return t;            }        }        return null;    }    boolean doesIntersect(Rectangle a, Rectangle b) {        return (a.x + a.width < b.x || a.x > b.x + b.width                || a.y + a.height < b.y || a.y > b.y + b.height);    }    public Rectangle getBounds() {        return new Rectangle(this.x - 15, this.y - 15, 31, 31);    }    void checkCollisionUp(GameLevel level) {        if (getRelativeTile(level, this.x - 14, this.y + 24, 0, -1) != null &&                getRelativeTile(level, this.x - 14, this.y + 24, 0, -1).getType() == 0 ||                getRelativeTile(level, this.x + 15, this.y + 24, 0, -1) != null &&                getRelativeTile(level, this.x + 15, this.y + 24, 0, -1).getType() == 0) {            this.collidingUp = true;            return;        }        this.collidingUp = false;    }    void checkCollisionDown(GameLevel level) {        if (getRelativeTile(level, this.x - 14, this.y - 24, 0, 1) != null &&                getRelativeTile(level, this.x - 14, this.y - 24, 0, 1).getType() == 0 ||                getRelativeTile(level, this.x + 15, this.y - 24, 0, 1) != null &&                getRelativeTile(level, this.x + 15, this.y - 24, 0, 1).getType() == 0) {            this.collidingDown = true;            return;        }        this.collidingDown = false;    }    void checkCollisionLeft(GameLevel level) {        if (getRelativeTile(level, this.x + 24, this.y - 15, -1, 0) != null &&                getRelativeTile(level, this.x + 24, this.y - 15, -1, 0).getType() == 0 ||                getRelativeTile(level, this.x + 24, this.y + 14, -1, 0) != null &&                getRelativeTile(level, this.x + 24, this.y + 14, -1, 0).getType() == 0) {            this.collidingLeft = true;            return;        }        this.collidingLeft = false;    }    void checkCollisionRight(GameLevel level) {        if (getRelativeTile(level, this.x - 24, this.y - 15, 1, 0) != null &&                getRelativeTile(level, this.x - 24, this.y - 15, 1, 0).getType() == 0 ||                getRelativeTile(level, this.x - 24, this.y + 15, 1, 0) != null &&                getRelativeTile(level, this.x - 24, this.y + 15, 1, 0).getType() == 0) {            this.collidingRight = true;            return;        }        this.collidingRight = false;    }    void respawn(GameLevel level) {        this.x = level.getSpawnPoint().x;        this.y = level.getSpawnPoint().y;        if (level.coins != null) {            for (Coin coin : level.coins) coin.collected = false;        }    }    boolean collidesWith(Shape other) {        return this.getBounds().getBounds2D().intersects(other.getBounds2D());    }    public void update(GameLevel level) {        this.snapX = this.x / 40;        this.snapY = this.y / 40;        if (level.coins != null) {            for (Coin coin : level.coins) {                if (this.collidesWith(coin.getBounds()) && !coin.collected) {                    coin.collected = true;                    //Coin sound                    TinySound.init();                    TinySound.loadSound(Player.class.getClassLoader()                            .getResource(net/thedanpage/worldshardestgame/resources/ding.wav)).play();                }            }        }        if (level.getTileMap() != new ArrayList<Tile>()) {            if (level.allCoinsCollected()) {                for (Tile t : level.getTileMap()) {                    if (t.getType() == 3 && this.collidesWith(t.getBounds())) {                        Game.levelNum ++;                        level.init(this, Game.levelNum);                        Game.gameState = Game.LEVEL_TITLE;                        Game.easyLog(Game.logger, Level.INFO, Game state set to LEVEL_TITLE);                        //Wait 1.75 seconds then start the level.                        new Thread() {                            public void run() {                                try {                                    Thread.sleep(1750);                                } catch (InterruptedException e) {                                    Game.easyLog(Game.logger, Level.SEVERE, Game.getStringFromStackTrace(e));                                }                                Game.gameState = Game.LEVEL;                                Game.easyLog(Game.logger, Level.INFO, Game state set to LEVEL);                            }                        }.start();                    }                }            }        }        checkCollisionUp(level);        checkCollisionDown(level);        checkCollisionLeft(level);        checkCollisionRight(level);        if (this.dead) {            this.opacity -= 255/75;            if (this.opacity < 0) this.opacity = 0;            if (this.opacity == 0) {                this.dead = false;                this.opacity = 255;                this.respawn(level);            }        } else {            if (Input.up.isPressed && !this.collidingUp) this.y --;            if (Input.down.isPressed && !this.collidingDown) this.y ++;            if (Input.left.isPressed && !this.collidingLeft) this.x --;            if (Input.right.isPressed && !this.collidingRight) this.x ++;        }        if (this.x > 800) this.x = 0;        if (this.x < 0) this.x = 800;        if (this.y > 600) this.y = 0;        if (this.y < 0) this.y = 600;        if (!this.dead) {            for (Dot dot : level.dots) {                if (this.collidesWith(dot.getBounds())) {                    this.deaths ++;                    this.dead = true;                    if (!Game.muted) {                        //Play the smack sound                        TinySound.init();                        TinySound.loadSound(ClassLoader.getSystemResource(                                net/thedanpage/worldshardestgame/resources/smack.wav)).play();                    }                }            }        }    }    public int getX() {        return this.x;    }    public int getY() {        return this.y;    }    public int getSnapX() {        return this.snapX;    }    public int getSnapY() {        return this.snapY;    }    public int getWidth() {        return (int) this.getBounds().getWidth();    }    public int getHeight() {        return (int) this.getBounds().getHeight();    }    public boolean isCollidingLeft() {        return this.collidingLeft;    }    public boolean isCollidingRight() {        return this.collidingRight;    }    public boolean isCollidingUp() {        return this.collidingUp;    }    public boolean isCollidingDown() {        return this.collidingDown;    }    public int getDeaths() {        return this.deaths;    }    public boolean isDead() {        return this.dead;    }    public void setDead(boolean dead) {        this.dead = dead;    }    public double getOpacity() {        return this.opacity;    }    public void reset() {        this.x = 400;        this.y = 300;        this.snapX = x/40;        this.snapY = y/40;        this.collidingUp = false;        this.collidingDown = false;        this.collidingLeft = false;        this.collidingRight = false;        this.deaths = 0;        this.dead = false;        this.opacity = 255;    }    @Override    public String toString() {        return Player [x= + x + , y= + y + , snapX= + snapX + , snapY=                + snapY + , collidingUp= + collidingUp + , collidingDown=                + collidingDown + , collidingLeft= + collidingLeft                + , collidingRight= + collidingRight + , deaths= + deaths                + , dead= + dead + ];    }}TextFileWriter.javapackage net.thedanpage.worldshardestgame;//  JavaFileAppendFileWriterExample.java//  Created by <a href=http://alvinalexander.com title=http://alvinalexander.com>http://alvinalexander.com</a>import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;public class TextFileWriter {    static BufferedWriter bw = null;    public static void appendToFile(String filepath, String s) {        try {            // APPEND MODE SET HERE            bw = new BufferedWriter(new FileWriter(filepath, true));            bw.write(s);            bw.newLine();            bw.flush();        } catch (IOException e) {            System.out.println(e);        } finally { // always close the file            if (bw != null)                try {                    bw.close();                } catch (IOException ioe2) {                    // just ignore it                }        } // end try/catch/finally    }} // end classLogZipper.javapackage net.thedanpage.worldshardestgame;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.util.Enumeration;import java.util.zip.ZipEntry;import java.util.zip.ZipFile;import java.util.zip.ZipInputStream;import java.util.zip.ZipOutputStream;public class LogZipper {    public static void zipLog() {        byte[] buffer = new byte[1024];        try {            FileOutputStream fos = new FileOutputStream(                    Game.logFilePath + .zip);            ZipOutputStream zos = new ZipOutputStream(fos);            ZipEntry ze = new ZipEntry(Game.logFilePath.replace(                    System.getProperty(user.home)                            + /worldshardestgame/logs/, ));            zos.putNextEntry(ze);            FileInputStream in = new FileInputStream(Game.logFilePath);            int len;            while ((len = in.read(buffer)) > 0) {                zos.write(buffer, 0, len);            }            in.close();            zos.closeEntry();            // remember close it            zos.close();            if (new File(Game.logFilePath).exists()) new File(Game.logFilePath).delete();        } catch (IOException ex) {            ex.printStackTrace();        }    }    public static void unzipLog() {        try {            ZipFile zipFile = new ZipFile(Game.logFilePath + .zip);            Enumeration<?> enu = zipFile.entries();            while (enu.hasMoreElements()) {                ZipEntry zipEntry = (ZipEntry) enu.nextElement();                String name = zipEntry.getName();                File file = new File(name);                if (name.endsWith(/)) {                    file.mkdirs();                    continue;                }                File parent = file.getParentFile();                if (parent != null) {                    parent.mkdirs();                }                InputStream is = zipFile.getInputStream(zipEntry);                FileOutputStream fos = new FileOutputStream(file);                byte[] bytes = new byte[1024];                int length;                while ((length = is.read(bytes)) >= 0) {                    fos.write(bytes, 0, length);                }                is.close();                fos.close();            }            zipFile.close();        } catch (IOException e) {            e.printStackTrace();        }    }    public static void unzip(String destinationFolder, String zipFile) {        File directory = new File(destinationFolder);        // if the output directory doesn't exist, create it        if(!directory.exists())             directory.mkdirs();        // buffer for read and write data to file        byte[] buffer = new byte[2048];        try {            FileInputStream fInput = new FileInputStream(zipFile);            ZipInputStream zipInput = new ZipInputStream(fInput);            ZipEntry entry = zipInput.getNextEntry();            while(entry != null){                String entryName = entry.getName();                File file = new File(destinationFolder + File.separator + entryName);                System.out.println(Unzipping file  + entryName +  to  + file.getAbsolutePath());                // create the directories of the zip directory                if(entry.isDirectory()) {                    File newDir = new File(file.getAbsolutePath());                    if(!newDir.exists()) {                        boolean success = newDir.mkdirs();                        if(success == false) {                            System.out.println(Problem creating Folder);                        }                    }                }                else {                    FileOutputStream fOutput = new FileOutputStream(file);                    int count = 0;                    while ((count = zipInput.read(buffer)) > 0) {                        // write 'count' bytes to the file output stream                        fOutput.write(buffer, 0, count);                    }                    fOutput.close();                }                // close ZipEntry and take the next one                zipInput.closeEntry();                entry = zipInput.getNextEntry();            }            // close the last ZipEntry            zipInput.closeEntry();            zipInput.close();            fInput.close();        } catch (IOException e) {            e.printStackTrace();        }    }}
Compacting code for World's Hardest Game remake
java;game;swing
try-with-resources    static BufferedWriter bw = null;You make this a class field.  So it is common to every object of this class.  Why?      public static void appendToFile(String filepath, String s) {        try {            // APPEND MODE SET HERE            bw = new BufferedWriter(new FileWriter(filepath, true));            bw.write(s);            bw.newLine();            bw.flush();        } catch (IOException e) {            System.out.println(e);        } finally { // always close the file            if (bw != null)                try {                    bw.close();                } catch (IOException ioe2) {                    // just ignore it                }        } // end try/catch/finally    }One would think that this would check if bw is already instantiated before operating on bw.  But it doesn't.  It writes over it and keeps going.  If it doesn't care about previous values of bw, why save them?  This holds a reference to a closed BufferedWriter until the next time it needs one.  Then it creates a new object and sends the old one for garbage collection.  Why wait?  Why not just send the old object for garbage collection as soon as you are done using it?  Why not just replace the whole thing with     public static void appendToFile(String filepath, String s) {        // APPEND MODE SET on the FileWriter        try (BufferedWriter bw = new BufferedWriter(new FileWriter(filepath, true))) {            bw.write(s);            bw.newLine();            bw.flush();        } catch (IOException e) {            System.out.println(e);        }    }Now the try-with-resources handles closing the file.  No manual finally block needed.  That alone cuts almost half the code of the method.  This also limits the scope to just the four places it is used.  Since we always create a new BufferedWriter, this just makes it a local variable.  No class field needed.  Also, you may want to change the comment to explain why append mode is set on the FileWriter rather than simply saying that append mode is set.  We can see that the FileWriter object is created in append mode if we read the JavaDoc on calling it with a Boolean second argument.  What I still don't know though is why that matters.  I'd much prefer to see         // append to FileWriter so that ...With the ... replaced with a real explanation.  As is, I can see that you found this important but I have no idea why.  The current comment tells me nothing that the code doesn't.  I would expect a method called appendToFile to use append mode.  In fact, it leaves me a bit confused, as I don't know which statement is supposed to set append mode until I read the code.  Don't Repeat Yourself (DRY)    void checkCollisionUp(GameLevel level) {        if (getRelativeTile(level, this.x - 14, this.y + 24, 0, -1) != null &&                getRelativeTile(level, this.x - 14, this.y + 24, 0, -1).getType() == 0 ||                getRelativeTile(level, this.x + 15, this.y + 24, 0, -1) != null &&                getRelativeTile(level, this.x + 15, this.y + 24, 0, -1).getType() == 0) {            this.collidingUp = true;            return;        }        this.collidingUp = false;    }You have four of these methods.  All of them have the same basic pattern.  You may want to consider @SimonForsberg's Direction enum pattern for this.  But even without that, you could make this code considerably simpler.      void checkCollisionUp(GameLevel level) {        collidingUp = hasCollision(level, x - 14, y + 24, x + 15, y + 24, 0, -1);    }I prefer not using the this. unless needed to disambiguate.  That of course is up to you.  It works both ways.  I'm just pointing out that I don't use it.  Then of course you need     bool hasCollision(GameLevel level, int x1, int y1, int x2, int y2, int xOff, int yOff) {        Tile relative1 = getRelativeTile(level, x1, y1, 0, -1);        Tile relative2 = getRelativeTile(level, x2, y2, 0, -1);        return relative1 != null && relative1.getType() == 0            || relative2 != null && relative2.getType() == 0;    }Now there are five simple methods rather than four complex and repetitive methods.  I prefer to put operators at the beginning of the lines rather than at the end.  This makes it easier to scan down the left side and get all the critical information.  I don't like the numbered variables, but I don't have better names for them.  Perhaps topLeft and bottomRight or small and big or close and far.  This only calls each method once instead of calling both twice.  I'm not crazy about all the -14, +15, and +24 in that code.  I would rather those were defined as constants in some way.  I note that while it is -14 and +15 three out of four times, the other time it is -15 and +15.  Is that deliberate?  Or a mistake?  I haven't tried to investigate it enough to tell.  If getRelativeTile were a static function, then hasCollision could be one too.  Set before the constructor    /** True if the player is colliding with a tile above them. */    private boolean collidingUp;    /** True if the player is colliding with a tile below them. */    private boolean collidingDown;    /** True if the player is colliding with a tile to their left. */    private boolean collidingLeft;    /** True if the player is colliding with a tile to their right. */    private boolean collidingRight;    /** The smack sound. */    Sound smack;    /** The number of times the player has died. */    private int deaths;    /** True if the player has been hit and is not allowed to move. */    private boolean dead;    /** The opacity of the player. */    private double opacity;    public Player() {        this.x = 400;        this.y = 300;        this.snapX = x/40;        this.snapY = y/40;        this.collidingUp = false;        this.collidingDown = false;        this.collidingLeft = false;        this.collidingRight = false;        this.deaths = 0;        this.dead = false;        this.opacity = 255;    }    public Player(int x, int y) {        this.x = x;        this.y = y;        this.snapX = x/40;        this.snapY = y/40;        this.collidingUp = false;        this.collidingDown = false;        this.collidingLeft = false;        this.collidingRight = false;        this.deaths = 0;        this.dead = false;        this.opacity = 255;    }You can get this down to     /** True if the player is colliding with a tile above them. */    private boolean collidingUp = false;    /** True if the player is colliding with a tile below them. */    private boolean collidingDown = false;    /** True if the player is colliding with a tile to their left. */    private boolean collidingLeft = false;    /** True if the player is colliding with a tile to their right. */    private boolean collidingRight = false;    /** The number of times the player has died. */    private int deaths = 0;    /** True if the player has been hit and is not allowed to move. */    private boolean dead = false;    /** The opacity of the player. */    private double opacity = 255;    public Player(int x, int y) {        this.x = x;        this.y = y;        this.snapX = x/40;        this.snapY = y/40;    }    public Player() {        this(400, 300);    }This does three things to get the code down.  First, I get rid of a smack variable that is unused.  Second, for those fields that get set the same regardless of how the constructor is called, I initialize those at declaration time.  So the constructors don't need to do so.  Third, I call one constructor from the other.  This allows me to set default values for the parameters.  And it means that we don't have to repeat the snap logic in both constructors.  Java doesn't care about method order, so you can do it either way.  I prefer to have called methods before calling methods when possible.  You may prefer a different order.  