patternjavaMinor
Atari Breakout Tilted Board
Viewed 0 times
boardataribreakouttilted
Problem
I made an Atari breakout game from Java from an inspiration from Google's Atari Breakout Doodle.
Here is the code for the main Controller:
This actually works. Try it by putting it all in an
```
package atariBreakout;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class Controller implements ActionListener, ItemListener {
Box box;
Block block;
ArrayList balls = new ArrayList();
Label l = new Label("Game starting in 1");
Label start = new Label("");
Random ran = new Random();
JMenuBar menuBar;
JMenu menu;
long score = 0;
JMenuItem menuItem;
java.awt.Image OSC;
ArrayList images = new ArrayList();
Random r = new Random();
boolean paused = false;
public static void main(String[] args) {
Controller c = new Controller();
c.startGame();
System.out.println("Game Started...");
}
public void startGame() {
box = new Box("Atari Breakout");
box.setJMenuBar(createMenuBar());
box.setVisible(true);
}
public JMenuBar createMenuBar() {
menuBar = new JMenuBar();
menu = new JMenu("Control");
menu.setBackground(Color.BLUE);
menuItem = new JMenuItem("Save Progress", KeyEvent.VK_T);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
ActionEvent.CTRL_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
"This save the progress of the game. ");
menu.add(menuItem);
menuBar.add(menu);
menuItem = new JMenuItem("Pause/Resume", KeyEvent.VK_SPACE);
menuItem.setAccelerator(KeyStroke.getKeyStroke(' '));
menuItem.getAccessibleContext().setAccessibleDescription(
"This save the progress of the game. ");
menuItem.addActionListener(this);
menu.add(menuItem);
menuBar.add(menu);
re
Here is the code for the main Controller:
This actually works. Try it by putting it all in an
atariBreakout folder.```
package atariBreakout;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class Controller implements ActionListener, ItemListener {
Box box;
Block block;
ArrayList balls = new ArrayList();
Label l = new Label("Game starting in 1");
Label start = new Label("");
Random ran = new Random();
JMenuBar menuBar;
JMenu menu;
long score = 0;
JMenuItem menuItem;
java.awt.Image OSC;
ArrayList images = new ArrayList();
Random r = new Random();
boolean paused = false;
public static void main(String[] args) {
Controller c = new Controller();
c.startGame();
System.out.println("Game Started...");
}
public void startGame() {
box = new Box("Atari Breakout");
box.setJMenuBar(createMenuBar());
box.setVisible(true);
}
public JMenuBar createMenuBar() {
menuBar = new JMenuBar();
menu = new JMenu("Control");
menu.setBackground(Color.BLUE);
menuItem = new JMenuItem("Save Progress", KeyEvent.VK_T);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
ActionEvent.CTRL_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
"This save the progress of the game. ");
menu.add(menuItem);
menuBar.add(menu);
menuItem = new JMenuItem("Pause/Resume", KeyEvent.VK_SPACE);
menuItem.setAccelerator(KeyStroke.getKeyStroke(' '));
menuItem.getAccessibleContext().setAccessibleDescription(
"This save the progress of the game. ");
menuItem.addActionListener(this);
menu.add(menuItem);
menuBar.add(menu);
re
Solution
Making classes is a good thing to help encapsulate specific work on an object is a good habbit. Having nested classes is not so good, use in very rare situations. That said It would be better if you would move your nested classes outside
I create a new instance of a box, and pass it in immediatly to BoxChanger. BoxChanger is this very simple classes:
and when I run that test my test passes just fine. You might wonder how that is possible, and the reason is because of being passed in by reference and not by value. So anychanges I make to that specific instance of Box in any class will be "updated" wherever that instance is used. Hence how that test passes. How to use this? Lets extract one of your listeners into its own file.
You'll see that I marked
Next point that I want to point to is having public fields. In your code there are very many places that you set the field to a specific value. such as in your keylistener
when I change degrees to private the compiler will now show me a few places where degrees is used. One in particular that stands out is in
This is the sorta stuff that very desperatly needs to be in block and not anywhere else. So a quick rule change to
Normally I would also mention testing, and one place in particular that I would like to get under some sort of test framework is Baller because there is some logic in there that needs to get tested. specically there is a bug (as far as I can tell) that if my paddle is at a steep angle the collision detection is wrong. I'm not sure specifically where it is wrong, but the ball fell right through my paddle but did move slighly (almost like there was a hole in my paddle that the ball fell into). That is all I have for now.
Controller and into a seperate file. I think I see why you had it the way you do because you want to change some of the fields in your java classes. Classes are passed by reference. To show you in code and with a test consider the following code@Test
public void testUpdatingFieldInADifferentClass() throws Exception {
Box box = new Box();
BoxChanger changer = new BoxChanger(box);
box.X = 1;
box.Y = 3;
changer.changeBox();
assertEquals(box.X, 2, "Box Changer should have added 1 to X");
assertEquals(box.Y, 3, "Box Changer should Not have Touched Y");
}I create a new instance of a box, and pass it in immediatly to BoxChanger. BoxChanger is this very simple classes:
public class BoxChanger {
private final Box box;
public BoxChanger(Box box) {
this.box = box;
}
public void changeBox(){
box.X += 1;
}
}and when I run that test my test passes just fine. You might wonder how that is possible, and the reason is because of being passed in by reference and not by value. So anychanges I make to that specific instance of Box in any class will be "updated" wherever that instance is used. Hence how that test passes. How to use this? Lets extract one of your listeners into its own file.
public class MyKeyListener extends KeyAdapter {
private final Block block;
private final Component box;
MyKeyListener(Component box, Block block) {
this.box = box;
this.block = block;
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
block.degrees += 5;
box.repaint();
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
block.degrees -= 5;
box.repaint();
}
}
}You'll see that I marked
block and box as final which would force me to make sure that I never set block or box to anything different than what it already is. Or in other words don't change the reference to which block and box point to. Now with a slight change in Controller.Box to make the compiler happy. addKeyListener(new MyKeyListener(this, block));. Fire up the game and sure enough the program still works. We would call this encapsulation. It's a form of refactoring that is fairly safe because you're not really changing anything, you're just moving things around to make more sense and to clean up the code some. As an exercise I'll let you try to do the same with MyMouseListener, Box, GamePanel, and Baller.Next point that I want to point to is having public fields. In your code there are very many places that you set the field to a specific value. such as in your keylistener
block.degrees += 5. It would be much better to make Block have a public method called rotateLeft, rotateRight. You could make a rotate(int) but the amount that you want to rotate might change and it is easier to update 1 reference instead of many. Changes to Block:private int degrees;
private static final int ROTATION = 5;
public void rotateRight(){
rotate(-ROTATION);
}
public void rotateLeft(){
rotate(ROTATION);
}
private void rotate(int amount){
degrees += amount;
repaint();
}when I change degrees to private the compiler will now show me a few places where degrees is used. One in particular that stands out is in
Controller.Ballerif (block.degrees == 180)
block.degrees = 0;This is the sorta stuff that very desperatly needs to be in block and not anywhere else. So a quick rule change to
Block.rotate and a getter added to degrees and we are set. Block and the changes to Block now look likeprivate int degrees;
private static final int ROTATION = 5;
public int getDegrees(){
return degrees;
}
public void rotateRight(){
rotate(-ROTATION);
}
public void rotateLeft(){
rotate(ROTATION);
}
private void rotate(int amount){
degrees += amount;
if (degrees == 180)
degrees = 0;
repaint();
}Normally I would also mention testing, and one place in particular that I would like to get under some sort of test framework is Baller because there is some logic in there that needs to get tested. specically there is a bug (as far as I can tell) that if my paddle is at a steep angle the collision detection is wrong. I'm not sure specifically where it is wrong, but the ball fell right through my paddle but did move slighly (almost like there was a hole in my paddle that the ball fell into). That is all I have for now.
Code Snippets
@Test
public void testUpdatingFieldInADifferentClass() throws Exception {
Box box = new Box();
BoxChanger changer = new BoxChanger(box);
box.X = 1;
box.Y = 3;
changer.changeBox();
assertEquals(box.X, 2, "Box Changer should have added 1 to X");
assertEquals(box.Y, 3, "Box Changer should Not have Touched Y");
}public class BoxChanger {
private final Box box;
public BoxChanger(Box box) {
this.box = box;
}
public void changeBox(){
box.X += 1;
}
}public class MyKeyListener extends KeyAdapter {
private final Block block;
private final Component box;
MyKeyListener(Component box, Block block) {
this.box = box;
this.block = block;
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
block.degrees += 5;
box.repaint();
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
block.degrees -= 5;
box.repaint();
}
}
}private int degrees;
private static final int ROTATION = 5;
public void rotateRight(){
rotate(-ROTATION);
}
public void rotateLeft(){
rotate(ROTATION);
}
private void rotate(int amount){
degrees += amount;
repaint();
}if (block.degrees == 180)
block.degrees = 0;Context
StackExchange Code Review Q#113762, answer score: 2
Revisions (0)
No revisions yet.