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();
}
}
1 Answer 1
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.
-
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– Rigidity2020年01月08日 23:42:11 +00:00Commented Jan 8, 2020 at 23:42
Explore related questions
See similar questions with these tags.
Point
is a java standard class so you have alreadyequals
method, while your classItem
at the moment hasn't and it can be a source of comparison problems in yourArrayList
. MoreoverPoint
already contains a `move' method that can be used for your player. \$\endgroup\$