patternjavaMinor
Snake Game with Swing
Viewed 0 times
withswinggamesnake
Problem
I put my code below to ask you for a review and ask you if this can be considered a good and clean code?
The game works just fine, I have encountered one issue though - the Food condition to not to create new food on current snake position is not working, and to be honest with you - I have been trying to solve it, but forgot about it and now when I am posting this I have this in my mind one more time.
I have added some buttons to stop/pause the game (can be done by pressing Space as well) and choose speed. Score is related to speed value (score for food is the same as the number of speed).
What's the most interesting I guess is the fact, that almost all snake tutorials around the internet allow you (if you are fast enough) to bug the game with those simple steps: Assume, that the snake moves to the Right, timer is set to some amount of time, and if you manage to press Up or Down arrow and then very fast Left (when the timer hasn't gone off), the snake will move backwards and finally eat himself. Most of the games doesn't allow you to change from Right to Left (or Up to Down etc.) in one step, but this is allowed. I prevent players from doing that by implementing temporary direction, that reads the current direction at the begining of a time period (or refresh rate - it is the same here).
I would like to hear your comments, because I am not quite sure if I get (and if I can implement that) all the Object-oriented programming right. Thank you!
```
package com.RGuSnake;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
public class Snake extends JPanel implements ActionListener {
private int sizeWidth;
private int sizeHeight;
private int offsetWidth;
private
The game works just fine, I have encountered one issue though - the Food condition to not to create new food on current snake position is not working, and to be honest with you - I have been trying to solve it, but forgot about it and now when I am posting this I have this in my mind one more time.
I have added some buttons to stop/pause the game (can be done by pressing Space as well) and choose speed. Score is related to speed value (score for food is the same as the number of speed).
What's the most interesting I guess is the fact, that almost all snake tutorials around the internet allow you (if you are fast enough) to bug the game with those simple steps: Assume, that the snake moves to the Right, timer is set to some amount of time, and if you manage to press Up or Down arrow and then very fast Left (when the timer hasn't gone off), the snake will move backwards and finally eat himself. Most of the games doesn't allow you to change from Right to Left (or Up to Down etc.) in one step, but this is allowed. I prevent players from doing that by implementing temporary direction, that reads the current direction at the begining of a time period (or refresh rate - it is the same here).
I would like to hear your comments, because I am not quite sure if I get (and if I can implement that) all the Object-oriented programming right. Thank you!
package com.RGuSnake;
public class Main {
public static void main(String[] args) {
Snake.getInstance().createBoard();
}
}```
package com.RGuSnake;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
public class Snake extends JPanel implements ActionListener {
private int sizeWidth;
private int sizeHeight;
private int offsetWidth;
private
Solution
I put my code below to ask you for a review and ask you if this can be considered a good and clean code?
Your program seems to suffer from the god object anti-pattern.
In short: your
An easy starting point to mitigate this is saving the board instance as a field in the
That way you can remove the following fields from your
If we do need one of those variables you just use the getter on the board:
Next look at where those variables are used and see if that functionality could fit in the
For example the
This also means we can move
inside the board as well. The idea is to remove all food related methods from the Snake class. It shouldn't be too hard to finish this part yourself.
I suggest to also add a method to Board to return a random spawning location for a new snake. This way if you later add some new special squares to your board like walls, you can make sure to spawn the snake in a valid location.
A major change I suggest is to split up the Snake class into a class that represents the game (with all the buttons, and initiating game state and coupling the other classes) and another that actually represents a Snake (with a position and movement and other snake related stuff).
This change is a bit too big to post in my answer here though, so you'll have to try yourself first.
Now that I pointed out the first steps to improving the cleanliness of your code, I'll try to address the problem of where to move next.
Your
The
Since the direction can have only one among a specific set of values, an
I suggest to also keep 2 variables for the movement in the snake:
The
Now in
Then also modify the move implementation slightly to first update the
A minor gameplay suggestion:
Increase the speed proportionally with the length of the snake (the amount of food consumed) at a higher difficulty level.
I hope these tips are enough to get the idea on how to improve your code.
Good luck!
Your program seems to suffer from the god object anti-pattern.
In short: your
Snake class does almost everything.An easy starting point to mitigate this is saving the board instance as a field in the
Snake class and move some board-related functionality to the Board class (the Composition pattern).That way you can remove the following fields from your
Snake class:private int sizeWidth;
private int sizeHeight;
private int offsetWidth;
private int offsetHeight;
private int scale;If we do need one of those variables you just use the getter on the board:
board.getWidth()Next look at where those variables are used and see if that functionality could fit in the
Board class instead.For example the
newFood() method. My first thought is to put this method inside the Board class. And from the Snake class (or whoever triggers spawning a new piece of food later on) just do board.spawnNewFood().This also means we can move
private static Point food;inside the board as well. The idea is to remove all food related methods from the Snake class. It shouldn't be too hard to finish this part yourself.
I suggest to also add a method to Board to return a random spawning location for a new snake. This way if you later add some new special squares to your board like walls, you can make sure to spawn the snake in a valid location.
A major change I suggest is to split up the Snake class into a class that represents the game (with all the buttons, and initiating game state and coupling the other classes) and another that actually represents a Snake (with a position and movement and other snake related stuff).
This change is a bit too big to post in my answer here though, so you'll have to try yourself first.
Now that I pointed out the first steps to improving the cleanliness of your code, I'll try to address the problem of where to move next.
Your
tmpDirection was a good first attempt, but I suggest to take a slightly different approach.The
10 in move() is a magic number, declare it as a field or a parameter to move() (recommended), as (a suggestion) double moveDistance. If you're sure that there will never be a need to change that value during the running of the program (think about it, if the user resizes the window, you should handle it gracefully and scale moveDistance to the playing area for the configured speed)Since the direction can have only one among a specific set of values, an
enum is recommended:public enum Direction {
UP, DOWN, LEFT, RIGHT
}I suggest to also keep 2 variables for the movement in the snake:
private Direction currentDirection = Direction.RIGHT;
private Direction nextDirection = Direction.RIGHT;The
currentDirection is the direction the snake is currently moving. The nextDirection is the direction the snake will move on the next call to move().Now in
ArrowAction modify nextDirection instead of currentDirection. This means that if you quickly press ↑ and then →, your ArrowAction for UP will change nextDirection to UP, and your ArrowAction for LEFT will not allow it, since the currentDirection is still going RIGHT.Then also modify the move implementation slightly to first update the
currentDirection:// Assuming that `Direction` has been imported into scope
public void move() {
currentDirection = nextDirection;
switch(currentDirection) {
case UP: snakeLocation.add(0, new Point(snakeLocation.get(0).x, snakeLocation.get(0).y - 10));
break;
case DOWN: snakeLocation.add(0, new Point(snakeLocation.get(0).x, snakeLocation.get(0).y + 10));
break;
case LEFT: snakeLocation.add(0, new Point(snakeLocation.get(0).x - 10, snakeLocation.get(0).y));
break;
case RIGHT: snakeLocation.add(0, new Point(snakeLocation.get(0).x + 10, snakeLocation.get(0).y));
break;
default: throw new IllegalArgumentException("Unknown move direction");
}
}A minor gameplay suggestion:
Increase the speed proportionally with the length of the snake (the amount of food consumed) at a higher difficulty level.
I hope these tips are enough to get the idea on how to improve your code.
Good luck!
Code Snippets
private int sizeWidth;
private int sizeHeight;
private int offsetWidth;
private int offsetHeight;
private int scale;board.getWidth()private static Point food;public enum Direction {
UP, DOWN, LEFT, RIGHT
}private Direction currentDirection = Direction.RIGHT;
private Direction nextDirection = Direction.RIGHT;Context
StackExchange Code Review Q#158544, answer score: 3
Revisions (0)
No revisions yet.