4
\$\begingroup\$

I was wondering what I could do to improve the gameplay of this game, as well as the graphics. I would like to use LWJGL, but the rendering library isn't important. I just need to find out improvements for my code, as well as things I can do to make it look better and play better.

The idea is simple. Don't hit red orbs. You move with W and S, and you can wrap sides. You can collect green orbs, which have a chance of making you faster. When you do, they give you more time before the clock runs out. You can also collect blue orbs, which have a chance of making you slower. You get to remove two random orbs when collecting them as well.

package me.Rigidity.Rue;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Rue extends JFrame {

    private static final long serialVersionUID = 2724959749690039251L;
    public JPanel graphics;
    public KeyListener key;
    public int width = 750, height = 450;
    public String title = "Rue";
    public boolean[] keys;
    public ArrayList<Item> items;
    public Item player;
    public long points;
    public boolean gameover;
    public int counter;
    public int difficulty;
    public long delay;
    public Random random;
    public long highscore = 0;

    public static class Item {

        public int x, y, w, h, s;
        public Color c;

        public Item(Color c, int x, int y, int w, int h, int s) {
            this.x = x;
            this.y = y;
            this.w = w;
            this.h = h;
            this.c = c;
            this.s = s;
        }

        public static boolean collision(Item a, Item b) {
            return a.x + a.w > b.x && a.y + a.h > b.y && a.x < b.x + b.w && a.y < b.y + b.h;
        }

    }
    public void init() {

        gameover = false;
        keys = new boolean[KeyEvent.KEY_LAST];
        player = new Item(Color.WHITE, 16, height / 2 - 24, 24, 24, 2);
        points = 0;
        counter = 0;
        difficulty = 90;
        delay = 1000/120;
        random = new Random((long)(Math.random()*Long.MAX_VALUE));
        items = new ArrayList<>();
        for (int i = 0; i < height / 45; i++) {
            place();
        }

    }

    public void place() {
        int type = (int)(random.nextDouble()*100)+1;
        if (type < 84) {
            barrier();
            return;
        }
        if (type < 92) {
            power();
            return;
        }
        boost();
    }
    public void barrier() {
        items.add(new Item(Color.RED, width + (int)(random.nextDouble() * width * 2), (int)(random.nextDouble() * (height+32))-16, 16, 16, 2));
    }
    public void power() {
        items.add(new Item(Color.BLUE, width + (int)(random.nextDouble() * width * 2), (int)(random.nextDouble() * (height+32))-16, 16, 16, 2));
    }
    public void boost() {
        items.add(new Item(Color.GREEN, width + (int)(random.nextDouble() * width * 2), (int)(random.nextDouble() * (height+32))-16, 16, 16, 2));
    }

    public void tick() {
        if (gameover) {
            if (keys[KeyEvent.VK_SPACE]) {
                init();
            }
            return;
        }
        if (keys[KeyEvent.VK_W]) {
            player.y -= player.s;
        }
        if (keys[KeyEvent.VK_S]) {
            player.y += player.s;
        }
        ArrayList<Item> old = new ArrayList<>();
        for (Item item : items) {
            old.add(item);
        }
        for (Item item : old) {
            item.x -= item.s;
            if (item.x + item.w < 0) {
                items.remove(item);
                place();
            } else {
                //if (false)
                if (Item.collision(player, item)) {
                    if (item.c == Color.RED) {
                        gameover = true;
                    } else if (item.c == Color.BLUE) {
                        items.remove(item);
                        items.remove(0);
                        items.remove(0);
                        if (random.nextDouble() > 0.7) delay++;
                    } else if (item.c == Color.GREEN) {
                        items.remove(item);
                        difficulty += 16;
                        if (random.nextDouble() > 0.7) delay--;
                    }
                }
            }
        }
        if (player.y + player.h < 0) {
            player.y = height - player.h;
        }
        if (player.y > height - player.h) {
            player.y = -player.h;
        }
        counter += Math.ceil((double)points / 3000.0);
        if (counter > difficulty*2) {
            place();
            counter = 0;
            difficulty -= 2;
            if (difficulty < 20) {
                difficulty = 60;
                delay--;
            }
        }
        points++;
        if (points > highscore) {
            highscore = points;
        }
    }
    public void render(Graphics ctx) {
        ctx.setColor(Color.BLACK);
        ctx.fillRect(0, 0, width, height);
        for (Item item : items) {
            ctx.setColor(item.c);
            ctx.fillRect(item.x, item.y, item.w, item.h);
        }
        ctx.setColor(player.c);
        ctx.fillRect(player.x, player.y, player.w, player.h);
        ctx.setFont(ctx.getFont().deriveFont(22.0f));
        ctx.setColor(Color.GREEN);
        ctx.drawString(""+points, width - ctx.getFontMetrics().stringWidth(""+points) - 5, 22);
        ctx.setColor(Color.BLUE);
        ctx.drawString(""+highscore, width/2 - ctx.getFontMetrics().stringWidth(""+highscore)/2, 22);
        ctx.setColor(Color.RED);
        ctx.drawString(""+(difficulty/2), 5, 22);
    }
    public void setup() {
        key = new KeyListener() {
            public void keyTyped(KeyEvent e) {

            }
            public void keyPressed(KeyEvent e) {
                keys[e.getKeyCode()] = true;
            }
            public void keyReleased(KeyEvent e) {
                keys[e.getKeyCode()] = false;
            }
        };
        graphics = new JPanel() {
            private static final long serialVersionUID = -2152573216046911514L;
            public long then = System.currentTimeMillis();
            public long amount = -2000;
            public boolean initialized = false;
            public void paint(Graphics ctx) {
                if (!initialized)init();
                initialized = true;
                ctx.clearRect(0, 0, width, height);
                long now = System.currentTimeMillis();
                long dist = now - then;
                then = now;
                amount += dist;
                if (delay > 1000/90) {
                    delay = 1000/90;
                }
                if (delay < 1000/300) {
                    delay = 1000/300;
                }
                while (amount >= delay) {
                    amount -= delay;
                    tick();
                }
                render(ctx);
                repaint();
            }
        };
    }

    public Rue() {
        game = this;
        game.setVisible(false);
        game.setSize(width, height);
        //game.setExtendedState(JFrame.MAXIMIZED_BOTH); 
        //game.setUndecorated(true);
        game.setResizable(false);
        game.setLocationRelativeTo(null);
        game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        game.setTitle(title);
        setup();
        game.addKeyListener(key);
        game.addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent componentEvent) {
                width = game.getWidth();
                height = game.getHeight();
            }
        });
        game.add(graphics);
        game.setVisible(true);
    }

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

}
\$\endgroup\$
  • 2
    \$\begingroup\$ Hello and welcome to Code Review, I was wondering if you have checked the class Point for coordinates. \$\endgroup\$ – dariosicily Sep 12 '19 at 7:56
  • \$\begingroup\$ Thanks for the answer. I could use that, but what would be the reason? \$\endgroup\$ – Rigidity Sep 15 '19 at 19:18
  • \$\begingroup\$ Point is a java standard class so you have already equals method, while your class Item at the moment hasn't and it can be a source of comparison problems in your ArrayList. Moreover Pointalready contains a `move' method that can be used for your player. \$\endgroup\$ – dariosicily Sep 16 '19 at 10:08
3
\$\begingroup\$

Cool game. I enjoy code reviews when all of the code is easy to reproduce. In this case simply copying & pasting into an IDE and hitting the run button :-)

Item class

Item IMO should be renamed to GameItem, MovableGameObject or something similar. It should also be a class in it's own file, with private methods and getters, setters instead of public properties.

It took me a while to realize s was short for speed. I'd advise against shortening names to a single character. The only exception is x and y as those are fairly standard for x, y coordinates.

Avoid magic numbers / magic strings

Lots of your constants should be declared as static final variables, at the top. E.G:

private static final int PLAYER_WIDTH = 24;

I'd suggest reading more on "Magic numbers". There's a lot on this site or online about it.

Method naming

Use names that make sense and are self explanatory. Try to be specific, don't use names that could cover a wide range of functionality.

place doesn't explain what it's doing. I can't tell what's it's doing until we rename the other methods.

power - Should be renamed to createPowerUnitAndAddToList. Using this new method name which accurately describes what the method is doing, we can easily tell from reading the name that the method could be refactored to be more useful & follow the single responsibility principle. I'd suggest instead naming it createPowerUnit and have the method return a new Item:

public Item createPowerUnit() {
    return new Item(Color.BLUE, width + (int)(random.nextDouble() * width * 2), (int)(random.nextDouble() * (height+32))-16, 16, 16, 2);
}
...
items.add(createPowerUnit());

You may want to create a PowerUnit class and extend Item. This allows for more control over the different units. And/or have a separate class for creating the units. This keeps the code more organized.

Use else-if

It looks like you are avoiding using 'else if' and 'else' statements. Use them whenever necessary. Absolutely no need to avoid them and doing so is wrong.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for the answer! It's been a while since I wrote this and I forgot about it, so my bad for not accepting sooner. I agree with what you were saying. I often take programs from a simple view, then realize they are less simple than I thought, and attempt to fit those complexities into the current model (not having a class for each powerup). As far as the if statements go, I sometimes avoid if statements for no reason, even though they are better in most cases. Thanks again! Also, any improvements for the actual gameplay? \$\endgroup\$ – Rigidity Jan 8 at 23:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.