Number of methods within a class on a minesweeper example - java

I'm a newbie so please don't scald me too much as I'd just like to follow the good OOP trail from the very beginning :) So I'm coding a minesweeper in Java with Swing and for now, my code looks like this:
a class with main() only which starts the game by creating an object Minesweeper()
the Minesweeper class in which I create a JFRame, JPanel for menu (and ActionListener for it) and create a Grid(x,y) object
the Grid(int width, int height) class which extends JPanel using which I create a grid with the given dimensions, put mines on it and handle all the playing
I have some worries about the Grid(), though. Is it OK to handle everything from drawing the desired number of JButtons, through setting mines on them and listening for clicks (and also addressing those clicks) to the finding_empty_cells algorithm in case user clicks on something other than bomb and we have to show the surrounding empties in one class? Doens't that violate the single responsibility principle? Or is it OK?

I am not familiar with swing, so I can only give you some pseudo-java code.
However, it should serve the demonstrational purpose. When you want to reach
the next level of OOP, I would recommend creating a class for a cell in the
Minesweeper grid.
public class Cell extends JPanel {
private MinesweepController controller;
private int points;
private boolean revealed;
// Index in the grid.
private int x, y;
public Cell(MinesweepController controller_, int x_, int y_, int points_) {
controller = controller_;
points = points_;
x = x_;
y = y_;
}
public void onClick(MouseEvent event) {
controller.reveal(x, y);
}
public void paint(Graphics gfx) {
if (revealed) {
if (points < 0) {
drawBomb(getX(), getY())
}
else {
drawPoints(getX(), getY(), points);
}
}
else {
drawRect(getX(), getY());
}
}
public int getPoints() {
return points;
}
public boolean isRevealed() {
return revealed;
}
public void reveal() {
revealed = true;
}
}
public class MinesweepController {
private Grid grid;
private int score;
// ...
public boid reveal(int x, int y) {
Cell cell = grid.getCell(x, y);
if (cell.getPoints() < 0) {
// End game.
}
else {
score += cell.getPoints();
// Reveal ascending cells.
}
}
}

Related

Correct way for accessing final fields in java from other class

What is the good way to access final fields from other class and why.
A) Isolating it from other class by making it private and giving the functionality in the getter and setter method
public class Game extends JPanel {
private final Racquet racquet;
public Game() {
racquet = new Racquet(this);
}
}
public class Ball {
private Game game;
Ball(final Game game) {
this.game = game;
}
void move(int speed) {
if (collision()) {
y = game.getRacquet().getTopY() - DIAMETER;
}
}
}
public class Racquet {
final Game game;
public Racquet(final Game game) {
this.game = game;
}
public int getTopY() {
return Y;
}
}
B) Keeping it default and use the variable.methodname directly.
public class Game extends JPanel {
final Racquet racquet;
}
public class Ball {
void move(int speed) {
if (collision()) {
y = game.racquet.getTopY() - DIAMETER;
}
}
}
public class Racquet {
final Game game;
public Racquet(final Game game) {
this.game = game;
}
public int getTopY() {
return Y;
}
}
do accessing final fields directly leads to improved performance?
Instead of using the getter to move the Racket, it would be best to have a method it do it for you internally.
void move(int speed) {
if (collision()) {
racket.move(diameter)
}
}
//inside racket
public int move(int diameter){
return this.Y - diameter;
}
Or if you want, split the move into moveUp, moveDown, etc.. and return the value of the calculation after you pass the diameter. This will be dependent on a number of things, for example, the position of the ball. You can check the position of the ball and decided which method to call and move the racket.
It might be best to think about it realistically.
You could have a Player class:
Responsible for determining the position of the ball.
Moving the Racket
In reality your Racket, won't be aware of where the Ball is, or the Ball won't be aware your using a Racket to hit it, the Player is aware of it.
If you want to follow OOP guidelines, then don't access variables directly(i.e public) instead let the methods to do the work for you, and give you the result, it's a method of Tell, Don't Ask. Keep the getters for display purposes if needed.

Why does this attempt to animate my moving sprite work?

I'm trying to animate the sprite in my game when a button is pressed, but when I press the button, it skips the animation. Its supposed to go one pixel, change sprites, and then go one more pixel and change back. Here is the code
//for all
import java.nio.file.*;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.awt.image.*;
import java.net.*;
import java.awt.*;
import javax.swing.*;
import static java.lang.invoke.MethodHandles.*;
import java.awt.event.*;
//my Mario class (cut down a lot)
class Mario {
// all numbers multiplied by 2 from OG game
protected Direction dir;
protected int x, y;
protected BufferedImage sprite;
protected String currentSpriteName;
public Mario() {
this.x = 54;
this.y = 808;
dir = Direction.RIGHT;
setSprite(MVCE.SMALLSTANDFACERIGHT);
currentSpriteName = MVCE.SMALLSTANDFACERIGHT;
}
public void moveRight(){
if(this.dir == Direction.LEFT){
this.dir = Direction.RIGHT;
}
else if(this.dir == Direction.RIGHT){
this.x+=1;
}
}
public void animateMoveRight(){
if (currentSpriteName.equals(MVCE.SMALLSTANDFACERIGHT)){
setSprite(MVCE.SMALLWALKFACERIGHT);
}
else if (currentSpriteName.equals(MVCE.SMALLWALKFACERIGHT)){
setSprite(MVCE.SMALLSTANDFACERIGHT);
}
}
public void jump() {
this.y -= 46;
}
public void setSprite(String spriteName) {
URL spriteAtLoc = MVCE.urlGenerator(spriteName);
this.sprite = MVCE.generateAndFilter(sprite, spriteAtLoc);
}
public void getSprite(){
System.out.println(this.currentSpriteName);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(sprite, 0, 0, null); // DO NOT SET x and y TO ANYTHING,
// this sets 0,0 to top left!!
}
}
// my MarioRender class:
class MarioRender extends JLabel {
protected Mario marioSprite;
public MarioRender() {
marioSprite = new Mario();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
marioSprite.paint(g2);
setBounds(marioSprite.x, marioSprite.y, marioSprite.sprite.getWidth(), marioSprite.sprite.getHeight());
}
public void moveMarioRight(){
marioSprite.moveRight();
marioSprite.animateMoveRight();
setLocation(this.marioSprite.getX(), this.marioSprite.getY());
repaint();
//this is my attempt to make it animate
marioSprite.moveRight();
marioSprite.animateMoveRight();
setLocation(this.marioSprite.getX(), this.marioSprite.getY());
repaint();
}
public void jumpMario() {
marioSprite.jump();
setLocation(this.marioSprite.x, this.marioSprite.y);
repaint();
}
}
// direction class, solely for moving
enum Direction {
LEFT, RIGHT
}
// my calling class, which I called MVCE where I make the frame
public class MVCE extends JFrame {
MarioRender m = new MarioRender();
JLabel bg;
public MVCE() {
bg = new JLabel();
this.setSize(868, 915);
this.setVisible(true);
this.add(bg, BorderLayout.CENTER);
bg.setLayout(null);
bg.add(m);
m.setBounds(m.marioSprite.x, m.marioSprite.y, m.marioSprite.sprite.getWidth(),
m.marioSprite.sprite.getHeight());
KeyListener kl = new MoveListener();
this.addKeyListener(kl);
this.setFocusable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static final String SMALLSTANDFACERIGHT = "SmallStandFaceRight.bmp"; // 30
// x
// 32
public static final String SMALLJUMPFACERIGHT = "SmallJumpFaceRight.bmp"; // 32
// x
// 32
// generate URL
public static URL urlGenerator(String name) {
URL u = lookup().lookupClass().getResource(name);
return u;
}
// return image with filtered color
public static BufferedImage generateAndFilter(BufferedImage b, URL u) {
try {
b = ImageIO.read(u);
int width = b.getWidth();
int height = b.getHeight();
int[] pixels = new int[width * height];
b.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
// System.out.println(pixels[i]);
if (pixels[i] == 0xFFff00fe) {
pixels[i] = 0x00ff00fe;
}
}
BufferedImage newSprite = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
newSprite.setRGB(0, 0, width, height, pixels, 0, width);
b = newSprite;
} catch (IOException e) {
System.out.println("sprite not found");
e.printStackTrace();
}
return b;
}
// key listener
class MoveListener implements KeyListener {
public void keyPressed(KeyEvent k) {
if ((k.getKeyCode() == 39)) {
m.moveMarioRight();
///THIS IS SUPPOSED TO MOVE HIM 1, change sprite, and automatically move him back, it moves 2 pixels but no animation
}
if (k.getKeyCode() == 83) { // S key
m.marioSprite.setSprite(SMALLJUMPFACERIGHT);
m.jumpMario();
}
}
public void keyReleased(KeyEvent k) {
}
public void keyTyped(KeyEvent k) {
}
}
public static void main(String[] args) {
MVCE m = new MVCE();
}
}
I tried putting this between the calls to marioMoveRight():
try {
Thread.sleep(200);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
but it just delays the whole thing. I had also tried using an ActionListener, but I don't know how to make it react only when the key is pushed. as I had it,
I had this class inside of MVCE:
class TickListener implements ActionListener{
public void actionPerformed(ActionEvent a){
m.marioSprite.setSprite(Constants.SMALLWALKFACERIGHT);
repaint();
}
}
and this at the end of the MVCE constructor:
ActionListener ac = new TickListener();
final int DELAY = 1000;
Timer t = new Timer(DELAY, ac);
t.start();
but then, the Mario just moves automatically. I do not want to use a sprite sheet for this project, I am trying to do it as this guy did for SMB1.
Many problems, don't know which one or if any will fix the problem:
Don't use a KeyListener. If a component doesn't have focus the component won't receive the event. Instead use Key Bindings.
Don't use "==" to compare Objects. Instead you should be using the equals(...) method.
Don't override paintComponent. A painting method is for painting only. You should not be changing the bounds of the component in the painting method.
Do basic debugging (problem solving) before asking a question. A simple System.out.println(...) added to various methods will determine if the code is executing as you expect. Then when you ask a question you can ask a specific question telling us which block of code does not execute as you expect.
You never actually call the method animateMoveRight(), and if I understand correcly, that's what's changing the sprite. Also, I doubt that you see the sprite change when calling the same method twice in a row without any delay.
Try putting the animateMoveRight() method into the moveRight() or the moveMarioRight() method and, if neccessary because the animation is too fast, add your delay code back where you had it. Be careful not to let the main thread sleep, as this causes everything to freeze, so start another one or use a timer etc.
EDIT: Good timers
I'm not too familiar with the Timer class, so I end up using the Thread variant. There are many tutorials for that out there, just search for "java threads" or "java multithreading". This is IMO a solid tutorial you can check out.

Getting errors trying to use KeyListeners and ImageIcons?

I am currently trying to make a maze game for school, and I am confused as to why I am getting these errors. Here are the pieces of code that are involved. Also everything that is needed to be imported has been imported. First is the Player class which has the player's location, and then their image.
public class Player {
int playerX;
int playerY;
int moveSpeed = 5; //I can edit move speed here
Image character;
//getters and setters to utilize the player's location and image
public Player(){ //constructor for initial starting points
playerX = 50;
playerY = 50;
ImageIcon player = new ImageIcon("E://Workspace//Maze//images//Player.jpg");
character = player.getImage();
}
public void setPlayerX(int playerX){
this.playerX = playerX;
}
public int getPlayerX(){
return playerX;
}
public void setPlayerY(int playerY){
this.playerY = playerY;
}
public int getPlayerY(){
return playerY;
}
public void setMoveSpeed(int moveSpeed){
this.moveSpeed = moveSpeed;
}
public int getMoveSpeed(){
return moveSpeed;
}
public Image getPlayerImage(){
return character;
}
}
Now is the layout class which creates the JPanel, and is supposed to be what ends up drawing the player image into the game, and is also what is supposed to allow the player to move.
public class Layout extends JPanel implements ActionListener { //GUI with a non null FlowLayout
Player p = new Player();
Maze m = new Maze();
//500 x 500 seemed like a good size for the maze game
int x = 500;
int y = 500;
public Layout(){
setLayout(new FlowLayout());
addKeyListener(new Move());
setFocusable(true);
}
//for use in setting and getting the borders of the game
public void setX(int x){
this.x = x;
}
public int getX(){
return x;
}
public void setY(int y){
this.y = y;
}
public int getY(){
return y;
}
public void paintGame(Graphics g){
super.paint(g);
g.drawImage(p.getPlayerImage(), p.getPlayerX(), p.getPlayerY(), null);
}
#Overrride
public void actionPerformed(ActionEvent ae){
repaint();
}
}
class Move extends Layout implements KeyListener { //Inheritance
public void keyPressed(KeyEvent press) { //for the movement in the game
//I used both keys so that if the player woukld like to use WASD or the arrow keys either will work
if(press.getKeyCode() == KeyEvent.VK_W || press.getKeyCode() == KeyEvent.VK_UP){
//move up
p.setPlayerY(p.getPlayerY() - p.getMoveSpeed());
}
else if(press.getKeyCode() == KeyEvent.VK_S || press.getKeyCode() == KeyEvent.VK_DOWN){
//move down
p.setPlayerY(p.getPlayerY() + p.getMoveSpeed());
}
else if(press.getKeyCode() == KeyEvent.VK_A || press.getKeyCode() == KeyEvent.VK_LEFT){
//move left
p.setPlayerX(p.getPlayerX() - p.getMoveSpeed());
}
else if(press.getKeyCode() == KeyEvent.VK_D || press.getKeyCode() == KeyEvent.VK_RIGHT){
//move right
p.setPlayerX(p.getPlayerX() + p.getMoveSpeed());
}
}
public void keyReleased(KeyEvent release) {
//nothing is needed here
}
public void keyTyped(KeyEvent e) {
//does nothing if a key is type (no need for it)
}
}
Lastly is the Play class which is supposed to run the game (currently I am just trying to get the player to move across a blank space without the map implemented yet).
public class Play {
public static void main(String[]args) {
play();
}
public static void play(){
Layout l = new Layout();
Player p = new Player();
Maze m = new Maze();
l.setSize(l.getX(), l.getY()); //size can be changed in layout
l.setVisible(true);
}
}
Now for some reason I am getting a multitude of errors when I try to run this. I am hoping to get this fixed soon so I can start on implementing my maze map. Here are the errors that I am getting:
Exception in thread "main" java.lang.StackOverflowError
at java.lang.ref.SoftReference.get(Unknown Source)
at sun.misc.SoftCache$ValueCell.strip(Unknown Source)
at sun.misc.SoftCache$ValueCell.access$300(Unknown Source)
at sun.misc.SoftCache.get(Unknown Source)
at sun.awt.SunToolkit.getImageFromHash(Unknown Source)
at sun.awt.SunToolkit.getImage(Unknown Source)
at javax.swing.ImageIcon.<init>(Unknown Source)
at javax.swing.ImageIcon.<init>(Unknown Source)
at mazeGame.Player.<init>(Player.java:16)
at mazeGame.Layout.<init>(Layout.java:11)
at mazeGame.Move.<init>(Layout.java:49)
at mazeGame.Layout.<init>(Layout.java:20)
The: at mazeGame.Move.<init>(Layout.java:49)
at mazeGame.Layout.<init>(Layout.java:20) continues on for atleast 20 lines before Eclipse stops outputing the errors.
Please help me solve this as I am currently stuck on how to get the Player to move, and need to start on creating the map for the maze. Sorry for the really long post, but I wanted to post anything that could possibly be giving me the errors.
The problem is that you have infinite recursion in your constructors. Layout() calls addKeyListener(new Move());, and Move extends Layout, so Move's constructor also calls Layout(), which in turn calls Move().
You need to refactor your code to avoid that. A simple approach would be making Move not extend Layout, and passing player as a constructor parameter to Move.
class Move implements KeyListener {
final Player p;
Move(Player p) {
this.p = p;
}
...
}
and in Layout:
addKeyListener(new Move(p));
Also consider using key bindings instead of a key listener. It's often a better approach.
Try to replace lines:
ImageIcon player = new ImageIcon("E://Workspace//Maze//images//Player.jpg");
character = player.getImage();
with:
character = ImageIO.read(new File("E://Workspace//Maze//images//Player.jpg"));
I believe that problem could be cause by async loading of ImageIcon (The image will be preloaded by using MediaTracker to monitor the loading state of the image.) http://docs.oracle.com/javase/7/docs/api/javax/swing/ImageIcon.html

Drawing a lot of same objects from a class in JFrame

Answer is written by me in the bottom of the question
I want to get a lot of 'Ates' objects in a frame. I tried a lot of examples but always failed.
In this context, I want to see a lot of rectangles which are going to left. However, there is just one and it is going faster and faster...
It does not show more than one object at the same time. Can you tell me what is the problem?
I used this code:
public class GamePanel extends JPanel
{
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.BLACK);
for(Ates a1 : StartGame.alist) // alist is an arraylist for Ates class objects
{
g.fillRect(a1.getX(), a1.getY(), 20, 20);
}
...
Example creating:
public void sentAtes()
{
r = rand.nextInt(471)+60;
Ates a = new Ates(r);
alist.add(a);
}
Ates class:
public Ates(int a)
{
x = 700;
y = a;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
public void setX( int a )
{
x = a;
}
public void setY( int a )
{
y = a;
}
StartGame class:
public class StartGame extends JFrame implements KeyListener, ActionListener
{
protected static ArrayList<Ates> alist = new ArrayList<Ates>();
public static int cen = 0;
...
public StartGame()
{
jp = new GamePanel();
add(jp);
...
int delay = 10;
ActionListener taskPerformed = new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
cen++;
if(cen > 50)
{
cen = 0;
sentAtes();
}
for(Ates a1 : alist)
{
a1.setX(a1.getX()-1);
}
repaint();
}
};
new Timer(delay,taskPerformed).start();
...
Info: If there is only one object, it is going left as expected.
Answer to my question.
In Ates class, you should not use static word for variables and use this. prefix to set them.
This should be work.
public class Ates extends JFrame
{
protected int x;
protected int y;
public Ates(int a)
{
this.x = 700;
this.y = a;
}
Ok the only other thing I can see is that your StartGame() is only initialised once. Therefore your new Timer is only called once. Because it is the timer that is causing the ActionListener to be created it is only ever created and run through once and therefore can never reach the stage where it will create another rectangle. The actionPerformed() method only runs once in the program as there is no action or loop to cause it to run again.

Java Access Main Class Variables

I'm making a small game in which the Main class holds all the objects and variables and calls the methods within the classes themselves that do most of the work. Pretty standard. Unfortunately, that means that many of the variables I need are in the Main class where I can't access them.
For instance, as a test I wanted a ball to bounce around the screen, simple enough, but I need the dimensions of the screen, which I can get easily using the getSize() method in the main class. But when I create the Ball class which will bounce around, I can't access the getSize() method because it is in the Main class. Is there anyway to call it?
I know I can pass the variables to the Ball class in the constructor or for each method I need, but I want to see if there is some way I can take whichever variables I need when I need them, rather than passing it all the information whenever I make a new object.
Main.class
public void Main extends JApplet {
public int width = getSize().width;
public int height = getSize().height;
public void init(){
Ball ball = new Ball();
}
}
Ball.class
public void Ball {
int screenWidth;
int screenHeight;
public Ball(){
//Something to get variables from main class
}
}
Pass the variables you need to your objects. You can even create a singleton class containing all the constants/configurations that your classes need.
Example given:
Constants class
public class Constants {
private static Constants instance;
private int width;
private int height;
private Constants() {
//initialize data,set some parameters...
}
public static Constants getInstance() {
if (instance == null) {
instance = new Constants();
}
return instance;
}
//getters and setters for widht and height...
}
Main class
public class Main extends JApplet {
public int width = getSize().width;
public int height = getSize().height;
public void init(){
Constants.getInstance().setWidth(width);
Constants.getInstance().setHeight(height);
Ball ball = new Ball();
}
}
Ball class
public class Ball {
int screenWidth;
int screenHeight;
public Ball(){
this.screenWidth = Constants.getInstance().getWidth();
this.screenHeight= Constants.getInstance().getHeight();
}
}
Another way can be to start the object instance with the parameters you need. Example given:
Main class
public class Main extends JApplet {
public int width = getSize().width;
public int height = getSize().height;
public void init(){
Ball ball = new Ball(width, height);
}
}
Ball class
public class Ball {
int screenWidth;
int screenHeight;
public Ball(int width, int height){
this.screenWidth = width;
this.screenHeight= height;
}
}
There are more ways to achieve this, just look out yourself and choose the one you think it would be better for your project.
you can access them using simply two arg constructor.
public void init(){
Ball ball = new Ball(width,height);
}
public Ball(width,height){
//access variables here from main class
}
why do not this way:
public void Main extends JApplet {
public int width = getSize().width;
public int height = getSize().height;
public void init(){
Ball ball = new Ball(width, height);
}
public void Ball {
public Ball(int screenWidth, int screenHeight){
//use the variables
}

Categories

Resources