Java keylistener grid game - java

So I am working on a game where an actor is moved by the user in a grid, and if the space the user is trying to move the actor to of a certain type, then it can be moved to. The grid is made up of a class that I know works called PGrid, so the grid is made of PGrids. The problem is the keyListener does not function at all, not even print out 'hi.' Below is the code for PGame and moveGrid, where PGame handles moveGrid stuff but moveGrid draws out the grid (as it is the JPanel). I tried moving the keylistener from PGame to moveGrid, but it did not work.
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
//where all the different classes are put together
public class PGame implements KeyListener{ //may want to move the listener to moveGrid
private static moveGrid panel = new moveGrid(8,8); //taking something from moveGrid
private static PActor pguy = new PActor("Bill", 2, 2);
private boolean shallmove = false;
private int newx, newy;
public static void main(String[] args){
//create a new frame
JFrame frame = new JFrame("PGame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//moveGrid panel = new moveGrid(8,8); //taking something from moveGrid
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
panel.requestFocus();
//creating a "path" of places able to move to
moveGrid.pgrids.get(0).changeType(1);
moveGrid.pgrids.get(1).changeType(1);
moveGrid.pgrids.get(2).changeType(1);
moveGrid.pgrids.get(3).changeType(1);
moveGrid.pgrids.get(10).changeType(1);
moveGrid.pgrids.get(11).changeType(1);
moveGrid.pgrids.get(19).changeType(1);
moveGrid.pgrids.get(27).changeType(1);
//moveGrid.pgrids.get(4).changeType(2);
//start our pguy out in a position
PGrid pguystart = new PGrid(2,0,0);
moveGrid.pgrids.set(0,pguystart);
panel.repaint();
}
public void keyPressed(KeyEvent e) {
//here test if the grid can be updated
//Test:
pguy.canMove(3,3);
pguy.Move(4,3,3);
if(e.getKeyCode() == KeyEvent.VK_UP){
newx = pguy.getx();
newy = pguy.gety() - 1;
shallmove = pguy.canMove(newx,newy);
System.out.println("Hi");
}else if(e.getKeyCode() == KeyEvent.VK_DOWN){
newx = pguy.getx();
newy = pguy.gety() + 1;
shallmove = pguy.canMove(newx,newy);
} else if(e.getKeyCode() == KeyEvent.VK_LEFT){
newx = pguy.getx() - 1;
newy = pguy.gety();
shallmove = pguy.canMove(newx,newy);
}else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
newx = pguy.getx() + 1;
newy = pguy.gety();
shallmove = pguy.canMove(newx,newy);
}
}
public void keyReleased(KeyEvent e) {
System.out.println("Hi");
//update the grid if it can here
//somewhere in here add this:
//moveGrid.repaint(); //tell movegrid to repaint
if(shallmove = true){
//change a certain spot to the actor
PGrid temp = new PGrid(2,newx,newy);
moveGrid.pgrids.set(pguy.getplace(),temp);
//need to also change to old space to be back to what it was....
//*here*
pguy.Move(pguy.newPos,newx, newy);
panel.repaint();
}
}
public void keyTyped(KeyEvent e) { }
}
moveGrid:
//a grid in which stuff can move
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class moveGrid extends JPanel {
private int height;
private int width;
private int newx, newy;
private static PActor pguy = new PActor("Bill", 2, 2);
private boolean shallmove = false;
public static ArrayList<PGrid> pgrids = new ArrayList<PGrid>(); //an array full of grid boxes with type PGrid
public moveGrid(int height, int width){
this.height = height;
this.width = width;
setPreferredSize(new Dimension(800, 800));
//make all the values in pgrids equal to "Water" and give them locations
int i = 0;
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
PGrid pnull = new PGrid(0, x, y);
pgrids.add(i, pnull);
i++;
}
}
//drawGrid();
}
/*public void drawGrid(Graphics g){
g.drawRect(x,y,20,20);
} */
public void addNotify() {
super.addNotify();
requestFocus();
}
public void paintComponent(Graphics g){
//PGrid curLoc = new PGrid(height, height, height);
//go through and draw out the grid
int q = 0;
int midx = 0; //need to make these somehow so the squares get drawn at the center
int midy = 0;
for(int qh = 0; qh < height; qh++){
for(int qw = 0; qw < width; qw++){
PGrid pcur = pgrids.get(q); //
int p = pcur.getType();
if(p == 0){
//may want to import a water looking image
g.setColor(Color.BLUE);
g.fillRect((40*qw)+midx,(40*qh)+midy,40,40);
g.setColor(Color.BLACK);
g.drawRect((40*qw)+midx,(40*qh)+midy,40,40);
}else if(p == 1){
//may want to import a better image
g.setColor(Color.GREEN);
g.fillRect((40*qw)+midx,(40*qh)+midy,40,40);
}else if(p == 2){
//draws the "character"
g.setColor(Color.ORANGE);
g.fillRect((40*qw)+midx,(40*qh)+midy,40,40);
}
q++;
}
}
//here draw the character in the proper position
//so like multiply the x and y by 40
}
}
I also may have an error in the PActor class, which is supposedly the Actor that moves.
public class PActor {
private String name;
private int curx, cury;
int newPos;
public PActor(String name, int curx, int cury){
this.name = name;
this.curx = curx;
this.cury = cury;
}
public boolean canMove(int x, int y){
boolean abletomove = false;
//test if the space that the user is trying to moveto can be moved to
//use indexOf(a temp variable with values x and y also with type 1) to test
PGrid togo = new PGrid(1,x,y);
//now scan through pgrids in moveGrid to see the desired spot can be moved to
for(int s = 0; s <= moveGrid.pgrids.size(); s++){
PGrid temp = moveGrid.pgrids.get(s);
//test if the temp space is equal
if((togo.getType() == temp.getType()) && (togo.getx() == temp.getx()) && (togo.gety() == temp.gety())){
abletomove = true;
newPos = s;
break; //stop scanning, as it is now unnecessary
}
else{ //do nothing
}
}
//now test pgrids to see if there is a spot like such that is moveable
return abletomove;
}
public int getplace(){
return newPos;
}
public int getx(){
return curx;
}
public int gety(){
return cury;
}
public void Move(int pos, int x, int y){
PGrid temp = new PGrid(2,x,y);
moveGrid.pgrids.set(pos,temp);
}
public String toString(){
return name + " ";
}
}

Suggestions:
Again, for any Java Swing listener to work, the listener must be added to a component. For instance for a KeyListener to work, you first need to add it to the component that you wish to have listened to by calling addKeyListener(myKeyListener) on that component.
For a KeyListener to work, the component being listened to must have the keyboard focus. This means that if your'e listening to a JPanel, you first have to make it focusable by calling setFocusable(true) on the JPanel and then you need to request focus, such as by calling requestFocusInWindow() on it.
If something later steals the focus, such as a JButton that has been pushed or a text component, then the KeyListener will no longer work.
To get around this, we usually recommend that you use Key Bindings in place of KeyListeners, but note that the set up of these is completely different from that of KeyListeners, and you would need to throw all assumptions to the side and study the tutorial on use of these first.
If still stuck, then you will want to create and post a minimal example program, a small program that has a lot less code than the program you've posted, yet that compiles for us, runs, and that shows us directly your problem.
Links:
KeyListener Tutorial
Key Bindings tutorial

Related

How to add multiple rectangles to a jframe(trying to code 2048 in an easy way)

I made the game already and wanted to make my GUI look better with rectangles not with jlabels and now I´ve come to realize that only the last rectangle that is drawn is shown on the GUI
I already tried it with different layouts
my GUI class:
public class GUI_N
{
private Spiel spiel;
private KeyEvent e;
private String beste;
private int beste1;
private DrawingComponent[][] feld;
GUI_N(){
feld = new DrawingComponent[4][4];
spiel = new Spiel();
beste1 = 0;
beste = "Highscore: "+beste1;
JFrame g=new JFrame("2048 - Main");
g.setSize(500,500);
g.setFocusable(true); //wichtig für KeyListener
g.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int i = 0;
int j = 0;
int h = 0;
int l = 0;
while(i<4)
{
while(j<4)
{
if(i==0){
h = 50;
}else if(i==1){
h = 100;
}else if(i==2){
h = 150;
}else if(i==3){
h = 200;
}
if(j==0){
l = 50;
}else if(j==1){
l = 100;
}else if(j==2){
l = 150;
}else if(j==3){
l = 200;
}
feld[i][j] = new DrawingComponent(l,h,50,50);
feld[i][j].setBounds(l,h,50,50);
j++;
}
j=0;
i++;
}
i = 0;
j = 0;
while(i<4)
{
while(j<4)
{
g.add(feld[i][j]);
j++;
}
j=0;
i++;
}
//g.getContentPane().setBackground(new Color(20, 40, 50));
g.setVisible(true);
}
public static void main(String[] args) {
new GUI_N();
}
}
my rectangle class:
public class DrawingComponent extends JComponent
{
private Graphics2D g2;
private int wert;
private int x;
private int y;
private int w;
private int h;
public DrawingComponent(int px,int py,int pw,int ph)
{
x=px;
y=py;
w=pw;
h=ph;
}
public void paintComponent(Graphics g)
{
g2 = (Graphics2D) g;
Rectangle rect1 = new Rectangle(x,y,w,h);
g2.setColor(Color.RED);
g2.fill(rect1);
}
public void setWert(int x)
{
wert = x;
}
public int getWert()
{
return wert;
}
}
as I said only the last drawn rectangle is shown.
How do I achieve this?
Right now you're adding the rectangles directly to your frame. You should have a JPanel layer in between, to which you can give a LayoutManager (GridLayout would be a good one to look at) to arrange all your rectangles.
So you would have something like this:
JFrame g = new JFrame("2048 - Main");
// GridLayout (on next line) takes number of rows and columns
JPanel panel = new JPanel(new GridLayout(4, 4));
// ... add all the rectangles to the panel here
g.add(panel);
And then you would add your rectangles to the panel, not the frame. As you add them, they will automatically go into place in the grid.
panel.add(feld[i][j]);
Also, if you use GridLayout, it will resize and fit the components to the grid dynamically, so it may save you some code as well, since you wouldn't need to hardcode their sizes in the GUI class.

Setting a label text in Swing undos all button location movements

I have the weirdest bug ever.
I have this puzzle game that moves puzzle pieces (which really are buttons with images attached to them).
Everything worked fine until I tried to change the text of some label (to indicate how many steps the player has done).
Everytime I call someControl.setText("text");, the puzzle pieces that moved are set back to the their first position. I have no idea why, but they just do.
Here's my window:
It consists of two panels, each uses a GridBagLayout.
The main frame uses a gridBagLayout as well, which consists of the two panels.
I know it's weird as hell, but I can't figure out what may cause this GUI bug. Any idea?
The pieces of code:
increaseSteps which is called everytime I click a puzzle button
void increaseSteps() {
_steps++;
_lblSteps.setText("Steps: " + _steps);
}
Creation of the puzzle panel (the left panel)
private JPanel puzzlePanel() {
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
for (int i = 0; i < _splitImage.getSize(); i++)
for (int j = 0; j < _splitImage.getSize(); j++) {
int valueAtPos = _board.getMatrix()[i][j];
if (valueAtPos == 0)
continue;
int imageRow = _board.getImageRowFromValue(valueAtPos);
int imageCol = _board.getImageColFromValue(valueAtPos);
ImageIcon imageIcon = new ImageIcon(_splitImage.getImages()[imageRow][imageCol]);
JButton btn = new JButton(imageIcon);
_tileButtons[i][j] = new TileButton(btn, i, j);
btn.setPreferredSize(new Dimension(_splitImage.getImages()[i][j].getWidth(null),
_splitImage.getImages()[i][j].getHeight(null)));
// add action listener
btn.addActionListener(this);
btn.addKeyListener(this);
gbc.gridx = j;
gbc.gridy = i;
panel.add(_tileButtons[i][j].getButton(), gbc);
}
return panel;
}
actionPerformed:
#Override
public void actionPerformed(ActionEvent e) {
if (!(e.getSource() instanceof JButton))
return;
JButton btn = (JButton) e.getSource();
TileButton tile = getTileButtonFromBtn(btn);
if (tile == null)
return;
// check if we can move the tile
String moveDir = _board.canMoveTile(tile.getRow(), tile.getCol());
if (moveDir.equals("no"))
return;
increaseSteps();
int dirx = 0;
int diry = 0;
if (moveDir.equals("left")) {
dirx = -1;
_board.move("left", true);
tile.setCol(tile.getCol() - 1);
} else if (moveDir.equals("right")) {
dirx = 1;
_board.move("right", true);
tile.setCol(tile.getCol() + 1);
} else if (moveDir.equals("up")) {
diry = -1;
_board.move("up", true);
tile.setRow(tile.getRow() - 1);
} else { // down
diry = 1;
_board.move("down", true);
tile.setRow(tile.getRow() + 1);
}
moveButton(btn, dirx, diry, MOVE_SPEED);
if (_board.hasWon())
win();
}
moveButton: (moves the button in a seperate thread, calling btn.setLocation())
private void moveButton(JButton btn, int dirx, int diry, int speed) {
Point loc = btn.getLocation();
// get start ticks, calculate distance etc...
StopWatch stopper = new StopWatch();
int distance;
if (dirx != 0)
distance = _splitImage.getImages()[0][0].getWidth(null) * dirx;
else
distance = _splitImage.getImages()[0][0].getHeight(null) * diry;
if (speed > 0) {
// run the animation in a new thread
Thread thread = new Thread() {
public void run() {
int currentTicks;
int elapsed;
do {
int newX = loc.x;
int newY = loc.y;
elapsed = stopper.getElapsed();
int moved = (int) ((double) distance * (double) (elapsed / (double) speed));
if (dirx != 0)
newX += moved;
else
newY += moved;
btn.setLocation(newX, newY);
} while (elapsed <= MOVE_SPEED);
// make sure the last location is exact
btn.setLocation(loc.x + (dirx == 0 ? 0 : distance), loc.y + (diry == 0 ? 0 : distance));
}
};
thread.start();
}
else
btn.setLocation(loc.x + (dirx == 0 ? 0 : distance), loc.y + (diry == 0 ? 0 : distance));
}
You're trying to set the absolute position of a component via setLocation(...) or setBounds(...), one that is held by a container that uses a layout manager. This may work temporarily, but will fail if the container's layout manager is triggered to re-do the layout of its contained components. When that happens, the GridBagConstraints will take over and the components will move to their gridbag constraints assigned location.
The solution is to not do this, and instead to place the location of your components in concert with the layout managers used.
Another problem is that your current code is not Swing thread-safe since you're making Swing state changes from within a background thread. This won't always cause problems, but since it's a threading issue, risks causing intermittent hard to debug problems (ones that usually only occur when your boss or instructor are trying to run your code).
Possible solutions:
For a grid of images, you could use a grid of JLabels (or JButtons if you must) held in a container that uses GridLayout. When you need to reposition components, remove all components held by that JPanel, and then re-add, using the order of addition to help you position the components.
Easiest though would be to use a grid of non-moving JLabels, give them MouseListeners, and instead of moving the JLabels, remove and add Icons to them, including a blank Icon.
If you need to do Swing animation, use a Swing Timer to drive the animation. This will allow your code to make repetitive calls with delay between the calls, and with these calls being made on the Swing event thread, the EDT (event dispatch thread).
Demo proof of concept example code that shows swapping icons, but without animation, and without test of solution yet:
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class ImageShuffle extends JPanel {
private static final int SIDES = 3;
public static final String IMG_PATH = "https://upload.wikimedia.org/wikipedia/commons/"
+ "thumb/5/5a/Hurricane_Kiko_Sep_3_1983_1915Z.jpg/"
+ "600px-Hurricane_Kiko_Sep_3_1983_1915Z.jpg";
private List<Icon> iconList = new ArrayList<>(); // shuffled icons
private List<Icon> solutionList = new ArrayList<>(); // in order
private List<JLabel> labelList = new ArrayList<>(); // holds JLabel grid
private Icon blankIcon;
public ImageShuffle(BufferedImage img) {
setLayout(new GridLayout(SIDES, SIDES, 1, 1));
fillIconList(img); // fill array list with icons and one blank one
Collections.shuffle(iconList);
MyMouseListener myMouse = new MyMouseListener();
for (Icon icon : iconList) {
JLabel label = new JLabel(icon);
label.addMouseListener(myMouse);
add(label);
labelList.add(label);
}
}
private class MyMouseListener extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
JLabel selectedLabel = (JLabel) e.getSource();
if (selectedLabel.getIcon() == blankIcon) {
return; // don't want to move the blank icon
}
// index variables to hold selected and blank JLabel's index location
int selectedIndex = -1;
int blankIndex = -1;
for (int i = 0; i < labelList.size(); i++) {
if (selectedLabel == labelList.get(i)) {
selectedIndex = i;
} else if (labelList.get(i).getIcon() == blankIcon) {
blankIndex = i;
}
}
// get row and column of selected JLabel
int row = selectedIndex / SIDES;
int col = selectedIndex % SIDES;
// get row and column of blank JLabel
int blankRow = blankIndex / SIDES;
int blankCol = blankIndex % SIDES;
if (isMoveValid(row, col, blankRow, blankCol)) {
Icon selectedIcon = selectedLabel.getIcon();
labelList.get(selectedIndex).setIcon(blankIcon);
labelList.get(blankIndex).setIcon(selectedIcon);
// test for win here by comparing icons held by labelList
// with the solutionList
}
}
private boolean isMoveValid(int row, int col, int blankRow, int blankCol) {
// has to be on either same row or same column
if (row != blankRow && col != blankCol) {
return false;
}
// if same row
if (row == blankRow) {
// then columns must be off by 1 -- they're next to each other
return Math.abs(col - blankCol) == 1;
} else {
// or else rows off by 1 -- above or below each other
return Math.abs(row - blankRow) == 1;
}
}
public void shuffle() {
Collections.shuffle(iconList);
for (int i = 0; i < labelList.size(); i++) {
labelList.get(i).setIcon(iconList.get(i));
}
}
}
private void fillIconList(BufferedImage img) {
// get the width and height of each individual icon
// which is 1/3 the image width and height
int w = img.getWidth() / SIDES;
int h = img.getHeight() / SIDES;
for (int row = 0; row < SIDES; row++) {
int y = (row * img.getWidth()) / SIDES;
for (int col = 0; col < SIDES; col++) {
int x = (col * img.getHeight()) / SIDES;
// create a sub image
BufferedImage subImg = img.getSubimage(x, y, w, h);
// create icon from the image
Icon icon = new ImageIcon(subImg);
// add to both icon lists
iconList.add(icon);
solutionList.add(icon);
}
}
// create a blank image and corresponding icon as well.
BufferedImage blankImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
blankIcon = new ImageIcon(blankImg);
iconList.remove(iconList.size() - 1); // remove last icon from list
iconList.add(blankIcon); // and swap in the blank one
solutionList.remove(iconList.size() - 1); // same for the solution list
solutionList.add(blankIcon);
}
private static void createAndShowGui(BufferedImage img) {
JFrame frame = new JFrame("ImageShuffle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ImageShuffle(img));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
URL imgUrl = null;
BufferedImage img;
try {
imgUrl = new URL(IMG_PATH);
img = ImageIO.read(imgUrl);
SwingUtilities.invokeLater(() -> createAndShowGui(img));
} catch (IOException e) {
e.printStackTrace();
}
}
}
If I wanted animation, again, I'd raise the icon into the JFrame's glasspane, animate it to the new position using a Swing Timer, and then place the icon into the new JLabel. I'd also disable the MouseListener using a boolean field, a "flag", until the animation had completed its move.

Why are my images not drawing on the JPanel?

I'm trying to make a hex board with hex images (720x835 GIF) on a scroll-able JPanel. I've overridden the paintComponent method to draw the tiles at different specific locations and used a timer to call repaint at each tick.
When repaint() is called, doDrawing is called. When doDrawing is called, choseTile is called to draw the tiles with drawImage.
For some reason, the tiles are not being drawn and I'm left with an empty black panel. Why are my images not being drawn? Is it because the images are too large? The panel is too large?
public class MapPanel extends JPanel {
// Images for the tiles
Image tile1;
Image tile2;
//etc
// measurements for the tiles
int tileX = 720;
int tileY = 835;
int dimensionX = 14760;
int dimensionY = 14613;
//Use this to keep track of which tiles goes where on a 20x20 board
public int[][] hexer;
/**
* Create the panel.
*/
public MapPanel(int[][] hexMap) {
hexer = hexMap;
setPreferredSize(new Dimension(dimensionX, dimensionY));
setBackground(Color.black);
setFocusable(true);
loadImages();
Timer timer = new Timer(140, animatorTask);
timer.start();
}
//getting the images for the tiles
private void loadImages() {
// Setting the images for the tiles
ImageIcon iid1 = new ImageIcon("/Images/tiles/tile1.gif");
tile1 = iid1.getImage();
ImageIcon iid2 = new ImageIcon("/Images/tiles/tile2.gif");
tile2 = iid2.getImage();
//etc
}
// Drawing tiles
private void choseTile(Graphics g, int x, int y, int id) {
switch (id) {
case 1:
g.drawImage(tile1, x, y, this);
break;
case 2:
g.drawImage(tile2, x, y, this);
break;
//etc
}
}
// repainting stuff
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
private void doDrawing(Graphics g) {
int actualX;
int actualY;
//set the painting coordinates and image ID then call the method to draw
for (int x = 0; x < 20; x++) {
for (int y = 0; y < 20; y++) {
if ((y + 1) % 2 == 0) {
actualX = x * tileX + 720;
} else {
actualX = x * tileX + 360;
}
if((x + 1) % 2 == 0){
actualY = (y/2) * 1253 + 418;
}else{
actualY = (y+1)/2 * 1253 + 1044;
}
if(hexer[x][y] != 0)
choseTile(g, actualX, actualY, hexer[x][y]);
}
}
}
private ActionListener animatorTask = new ActionListener() {
public void actionPerformed(ActionEvent e) {
repaint();
}
};
}
Edit: I've already checked to make sure the images aren't null.
Following Andrew Thompson's suggestion; I used ImageIO. I was able to figure out that the way I was accessing the image files was faulty thanks to thrown errors by ImageIO.

Tearing/Artifacting in 2D tile map

first of all thanks for taking the time to read my question!
I'm developing a 2D java game, with a tile based map. When my character moves around,
everthing is fine, although when he moves left, vertical white lines, also know as artifacting/tearing, appear on the screen. Same thing happens when he moves up, though the lines are horizontal and much smaller in width. Oddly enough, this doesn't happen when I move right or down. I have searched around on internet to find a solution, though I haven't encountered anything that fit my problem.
Here's the code, although I've heavily downsized and simplified it for the sake of testing. It can therefore be run without any images. Thank you for any answer you provide!
package adriana;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
/**
*
* #author Christophe
*/
public class Main extends JFrame implements Runnable{
public Image dbImage;
public Graphics dbGraphics;
//Image + Array size
final static int listWidth = 500, listHeight = 500;
//Move Variables
int playerX = 320, playerY = 240, xDirection, yDirection;
//Sprites
BufferedImage spriteSheet;
//Lists for sprite sheet: 1 = STILL; 2 = MOVING_1; 3 = MOVING_2
BufferedImage[] ARCHER_NORTH = new BufferedImage[4];
BufferedImage[] ARCHER_SOUTH = new BufferedImage[4];
BufferedImage[] ARCHER_EAST = new BufferedImage[4];
BufferedImage[] ARCHER_WEST = new BufferedImage[4];
Image[] TILE = new Image[12];
//Animation Variables
int currentFrame = 0, framePeriod = 150;
long frameTicker = 0l;
Boolean still = true;
Boolean MOVING_NORTH = false, MOVING_SOUTH = false, MOVING_EAST = false, MOVING_WEST = false;
BufferedImage player;
//World Tile Variables
//20 X 15 = 300 tiles
Rectangle[][] blocks = new Rectangle[listWidth][listHeight];
int tileX = 250, tileY = 250;
Rectangle playerRect = new Rectangle(playerX + 4,playerY+20,32,20);
//Map Navigation
static final byte PAN_UP = 0, PAN_DOWN = 1, PAN_LEFT = 2, PAN_RIGHT = 3;
final int speed = 8;
public Main(){
this.setTitle("JAVA4K");
this.setSize(640,505);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
addKeyListener(new AL());
for(int y = 0; y < listHeight; y++){
for(int x = 0; x < listWidth; x++){
blocks[x][y] = new Rectangle(x*32-8000, y*32-8000, 32, 32);
}
}
}
//Key Listener
public class AL extends KeyAdapter{
public void keyPressed(KeyEvent e){
int keyInput = e.getKeyCode();
still = false;
if(keyInput == e.VK_LEFT){
navigateMap(PAN_RIGHT);
}if(keyInput == e.VK_RIGHT){
navigateMap(PAN_LEFT);
}if(keyInput == e.VK_UP){
navigateMap(PAN_DOWN);
}if(keyInput == e.VK_DOWN){
navigateMap(PAN_UP);
}
}
public void keyReleased(KeyEvent e){
int keyInput = e.getKeyCode();
setYDirection(0);
setXDirection(0);
if(keyInput == e.VK_LEFT){
}if(keyInput == e.VK_RIGHT){
}if(keyInput == e.VK_UP){
}if(keyInput == e.VK_DOWN){
}
}
}
public void moveMap(){
for(int a = 0; a < 500; a++){
for(int b = 0; b < 500; b++){
if(blocks[a][b] != null){
blocks[a][b].x += xDirection;
blocks[a][b].y += yDirection;
}
}
}
}
public void navigateMap(byte pan){
switch(pan){
default:
System.out.println("Unrecognized pan!");
break;
case PAN_UP:
setYDirection(-1 * speed);
break;
case PAN_DOWN:
setYDirection(+1 * speed);
break;
case PAN_LEFT:
setXDirection(-1 * speed);
break;
case PAN_RIGHT:
setXDirection(+1 * speed);
break;
}
}
public void setXDirection(int xdir){
xDirection = xdir;
if(blocks[0][0] != null) tileX = ((playerRect.x - blocks[0][0].x) / 32)-1;
}
public void setYDirection(int ydir){
yDirection = ydir;
if(blocks[0][0] != null) tileY = ((playerRect.y - blocks[0][0].y) / 32)-1;
}
public void paint(Graphics g){
dbImage = createImage(getWidth(), getHeight());
dbGraphics = dbImage.getGraphics();
paintComponent(dbGraphics);
g.drawImage(dbImage, 0, 25, this);
}
public void paintComponent(Graphics g){
requestFocus();
//Draws tiles and rectangular boundaries for debugging
for(int a = tileX - 18; a < tileX + 20; a++){
for(int b = tileY - 15; b < tileY + 17; b++){
g.setColor(Color.RED);
g.fillRect(blocks[a][b].x, blocks[a][b].y, 32, 32);
g.setColor(Color.BLACK);
g.drawRect(blocks[a][b].x, blocks[a][b].y, 32, 32);
}
}
//Draw player
g.drawRect(playerX, playerY, 40, 40);
repaint();
}
public void run(){
try{
System.out.println("Running");
while(true){
moveMap();
Thread.sleep(13);
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
Main main = new Main();
//Threads
Thread thread1 = new Thread(main);
thread1.start();
}
}
You have no synchronization between the thread updating the map and the drawing. So the map can be in inconsistent state during drawing.
A quick fix would be wrapping the map updating and drawing in a synchronized block:
synchronized(blocks) {
// drawing or modification here
}
(or just making the whole methods synchronized)
Also, the other fields (like those modified in the key listener) are also susceptible being in inconsistent state.
There are other problems too:
Don't override paint() of a frame. Instead override paintComponent() of a JPanel. There's no need to create an image at every redraw, the swing painting mechanism is by default double buffered. See custom painting in swing.
Use KeyBindings instead of a KeyListener
Swing components must be accessed (and created) only in the event dispatch thread.

Creating a Checkers Board in Java

I'm begining a little project to create a simple checkers game. However it's been a long time since I've used the java GUI tools. The goal of the code at this point is to draw the initial board (red pieces at top, black at bottom). However all I get when I run the code is a blank frame. I'm also a little uncertain if my circle drawing code will do what I want (ie create solid red or black circles inside certain squares) Here is the code. Thanks in advance for any help/suggestions
EDIT: I should probably alternate drawing blue and gray squares or else the thing will probably just be a giant blue blob, however I'll settle for a giant blue blob at this point :p
import javax.swing.*;
import java.awt.*;
public class CheckersServer
{
public static class Board
{
private JFrame frame = new JFrame();
private JPanel backBoard = new JPanel();
Board()
{
frame.setSize(905,905);
backBoard.setSize(900,900);
frame.setTitle("Checkers");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
backBoard.setVisible(true);
boardSquare bs;
String type = null;
//Filling in Red Side
for (int i = 0; i <=1; i++)
{
for(int j = 0; j < 9; j++)
{
if(j % 2 == 0)
{
type = "Red";
}
else
{
type = "Blank";
}
bs = new boardSquare(100*j,100*i,type);
backBoard.add(bs);
}
}
//Filling in empty middle
type = "Blank";
for (int i = 2; i < 7; i++)
{
for(int j = 0; j < 9; j++)
{
bs = new boardSquare(100*j,100*i,type);
backBoard.add(bs);
}
}
//Filling in Black side
for (int i = 7; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
if(j % 2 != 0)
{
type = "Black";
}
else
{
type = "Blank";
}
bs = new boardSquare(100*j,100*i,type);
backBoard.add(bs);
}
}
backBoard.repaint();
frame.add(backBoard);
frame.repaint();
}
private class boardSquare extends JComponent
{
private int x; //x position of the rectangle measured from top left corner
private int y; //y position of the rectangle measured from top left corner
private boolean isBlack = false;
private boolean isRed = false;
public boardSquare(int p, int q, String type)
{
x = p;
y = q;
if (type.equals("Black"))
{
isBlack = true;
isRed = false;
}
else if (type.equals("Red"))
{
isRed = true;
isBlack = false;
}
else if (type.equals("Blank"))
{
isBlack = false;
isRed = false;
}
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Rectangle box = new Rectangle(x,y,100,100);
g2.draw(box);
g2.setPaint(Color.BLUE);
g2.fill(box);
if(isBlack)
{
g2.fillOval(x, y,100 ,100 );
g2.setColor(Color.black);
g2.drawOval(x, y, 100, 100);
}
else if(isRed)
{
g2.fillOval(x, y,100 ,100 );
g2.setColor(Color.red);
g2.drawOval(x, y, 100, 100);
}
}
}
}
public static void main(String[] args)
{
Board game = new Board();
}
}
You have several issues.
Java UI is layout-based, which means that when you add a component to a parent, the parent's layout determines where the child component will be placed. You don't have any code to set up the layout, and so your application is using the defaults (FlowLayout is the default, and this may work in your case, as long as your JFrame and children are the appropriate size).
The bigger problems are in your boardSquare class. By default, JPanels have a dimension of 10x10. You aren't specifying the size, and so all your squares are 10x10. You need to tell the squares how big they are. You can do this in the boardSquare constructor:
setPreferredSize(new Dimension(100, 100));
Finally, in your drawing code, you are doing an offset of x,y when drawing the squares and circles. This is an offset from the top-left corner of the component. Your components (after setting the size) will be 100x100 pixels. But if your x,y are greater than these values, you will be drawing outside of the bounds of the component. Instead, these values should be set to 0,0 because that is the top-left corner of the component you are drawing in.
By just setting the preferred size of the squares and setting x,y to 0, I was able to get the squares drawing in the frame, though it wasn't pretty. You will need to work on setting the correct layout before it will be laid out correctly.
Here are some hints:
Your BoardSquares have dimension 0x0. Not a good size for something you want to be visible to the user.
To help visualize what's going on, cause each BoardSquare to be 100x100 pixels in size, and give them a border. Now you can see where they are showing up in your GUI. Your GUI code still needs significant changes, but this will at least let you start seeing what you're dealing with.
public BoardSquare(int p, int q, String type)
{
this.setBorder(new LineBorder(Color.CYAN, 2));
this.setPreferredSize(new Dimension(100, 100));
// ... etc ...
BoardSquare seems to be coded to draw its contents based on coordinates from the absolute topmost leftmost point in the window, but they should be coded to draw themselves from the topmost leftmost point of the BoardSquare itself. That is, components should only draw within their own boundaries, and they should use coordinates that assume 0,0 designates the top,left of the component, not of the window.
If you want to use BoardSquares (JComponents) and add them to the frame, you probably should use a different layout manager, like GridLayout. FlowLayout won't give you the kind of precise positioning you want.
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
public class CheckersServer2
{
public static String type_BLANK = "BLANK";
public static String type_RED = "RED";
public static String type_BLACK = "BLACK";
public static int width = 100;
public static int height = 100;
public static class Board
{
private JFrame frame = new JFrame();
private JPanel backBoard = new JPanel();
Board()
{
int numRows = 8;
int numCols = 8;
frame.setSize(905,905);
backBoard.setSize(900,900);
frame.setTitle("Checkers");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
backBoard.setVisible(true);
String type;
for(int r=0; r<numRows; r++){
for(int c=0; c<numCols; c++){
//
type = type_BLANK;
if(c%2==0){
if(r==0 || r==2) {
type = type_RED;
}else if(r==6){
type = type_BLACK;
}
}else{
if(r==1){
type = type_RED;
} else if(r==5 || r==7) {
type = type_BLACK;
}
}
backBoard.add(new BoardSquare(r,c,type));
}
}
backBoard.repaint();
frame.add(backBoard);
frame.repaint();
}
private class BoardSquare extends JComponent
{
/**
*
*/
private static final long serialVersionUID = 1L;
private int x; //x position of the rectangle measured from top left corner
private int y; //y position of the rectangle measured from top left corner
private boolean isBlack = false;
private boolean isRed = false;
public BoardSquare(int p, int q, String type)
{
//this.setBorder(new LineBorder(Color.CYAN, 2));
this.setPreferredSize(new Dimension(width, height));
x = p;
y = q;
if (type.equals(type_BLACK))
{
isBlack = true;
isRed = false;
}
else if (type.equals(type_RED))
{
isRed = true;
isBlack = false;
}
else if (type.equals(type_BLANK))
{
isBlack = false;
isRed = false;
}
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Rectangle box = new Rectangle(x,y,width,height);
g2.draw(box);
g2.setPaint(Color.BLUE);
g2.fill(box);
int ovalWidth = width - 15;
int ovalHeight = ovalWidth;
if(isBlack)
{
g2.setColor(Color.black);
g2.fillOval(x, y, ovalWidth, ovalHeight);
g2.drawOval(x, y, ovalWidth, ovalHeight);
}
else if(isRed)
{
g2.setColor(Color.red);
g2.fillOval(x, y, ovalWidth, ovalHeight);
g2.drawOval(x, y, ovalWidth, ovalHeight);
}
}
}
}
public static void main(String[] args)
{
Board game = new Board();
}
}

Categories

Resources