I'm working on making a board game for chess and checkers (and a few variations that I want to make). I've got a Board class that extends JPanel and sets up a doubly dimensioned array of JPanels to act as my board. Here's some of the code for my board class:
public class Board extends JPanel {
private static final int COLS = 8;
private static final int ROWS = 8;
private JPanel[][] board = new JPanel[COLS][ROWS];
private JPanel chessBoard;
public Board() {
super();
super.setLayout(new BorderLayout());
chessBoard = new JPanel();
chessBoard.setLayout(new GridLayout(COLS,ROWS));
// Set up JPanels on bottom and right to display letters and numbers for the board
// JPanels are called south and west
super.add(chessBoard, BorderLayout.CENTER);
super.add(south, BorderLayout.SOUTH);
super.add(west, BorderLayout.WEST);
for (int i=0; i<COLS; i++) {
for (int j=0; j<ROWS; j++) {
// Set up the grid
board[i][j] = new JPanel();
board[i][j].setBackground(getColor(i,j));
chessBoard.add(board[i][j]);
}
}
super.validate();
}
private Color getColor(int x, int y) {
if ((x + y) % 2 == 0) {
return Constants.GOLD;
} else {
return Constants.PURPLE;
}
}
public void addPiece(Piece piece) {
JLabel p = piece.getImage();
board[piece.getX()][piece.getY()].add(p);
chessBoard.validate();
}
}
Piece is an interface that that I'm going to use for all my pieces. I've set up the interface and set up one class that implements the interface (Checker class). I have all of that set up. The pieces are JLabels with ImageIcons in them. The only problem I'm having so far is writing a move method. I can figure out the logic make sure a move is a valid one, I just don't know how to actually make the move happen.
EDIT: I'm not even asking about mouse listeners or anything like that, I just want some pseudocode to explain making a piece move from one spot in the array to another.
EDIT 2: Here's the code for my Checkers class.
public class Checker implements Piece {
private int side,xPos,yPos;
private JLabel img;
public Checker(int team, int x, int y) {
BufferedImage image;
try {
if (team == 0)
image = ImageIO.read(new File("img/RedPiece.png"));
else
image = ImageIO.read(new File("img/BlackPiece.png"));
} catch(IOException e) {
image = null;
System.out.printf("Image file wasn't found!!!");
System.exit(1);
}
img = new JLabel(new ImageIcon(image), SwingConstants.CENTER);
img.setVerticalAlignment(SwingConstants.CENTER);
xPos = x;
yPos = y;
}
// TODO Figure out move method
public void move(int dx, int dy) {
}
// Also typical gets and sets for instance variables
So my thought is I call the move method for a checkers piece and, assuming I'm moving from the bottom of the screen to the top it would be piece.move(-1,1); and I have to remove the piece from it's current position, then it's new position in the array is [x + dx][y + dy]
Can you use something like this:
public void movePiece(Piece piece, int dx, int dy) {
// Save current position, so we can erase the piece.
int oldX = piece.getX();
int oldY = piece.getY();
// Update the location.
piece.setX(oldX + dx);
piece.setY(oldY + dy);
// Remove piece from old position
board[oldX][oldY].clear();
// Add it to the new position.
addPiece(piece);
}
You'd probably want to use a Point instead of individual x and y coordinates; I just thought this would be easier to understand.
By moving a Piece, you are going to change at least 2 elements of the 2-dimensional array board[][], which is inside your Board class. It's better to implement the method in the Board class, since Piece can only make change to it self. If you put move() in Piece class, and a Piece need to change another Piece, it needs to talk to the Board and ask the Board to pass the reference of the other Piece to it. What are you going to change about each Piece is totally depends on your design, such as colour, picture. Here is my suggestion:
Board:
move(Piece from, Piece to){
... // validation
from.remove();
to.put(team);
// over is the one you jump over, optional, some if statements here
Piece over = Board.getOver(Piece from, Piece to);
over.remove();
// check for further move is also optional
if(board.isFurthermoveAvailable(to)){
from = to;
to = board.getFurthermoveLocation(from);
board.move(from, to);
}
}
getOver(Piece from, Piece to){
return board[(from.getX()+to.getX())/2][(from.getY()+to.getY())/2];
}
isFurthermoveAvailable(Piece from){
int team = from.getTeam();
... // your logic
}
// depends on what you return by isFurthermoveAvailable(Piece from)
// if you return boolean, then you also need the method below
// or, you can let the above method return null or a Piece
getFurthermoveLocation(Piece from){
... // your logic
}
Piece:
remove(){
... // changes such as the picture, colour
}
put(int Team){ // this method is obviously depends on team
...
}
// other methods may assist to the process
getTeam(){
}
getX(){
}
getY(){
}
Related
I'm currently recreating a Civilization game in Processing. I'm planning to implement the feature in a which a given unit can see every possible move it can make with a given number of hexes it is allowed to move. All possible endpoints are marked with red circles. However, units cannot move through mountains or bodies of water. I'm trying to approach this by finding out every possible combination of moves I can make without the unit going into a mountain or body of water but I can't figure out how I can determine every combination.
There are 6 directions that any unit can go in, north-east, north, north-west, south-east, south, south-west. The max number of movements I'm assigning to any unit would probably go up to 6. Any higher and I'm afraid processing may become to slow every time I move a unit.
I'm trying to recreate this:
What I'm hoping the result will look like with two possible movements (without the black arrows):
Raw version of that image:
Here is the code I use to draw the hex grid. Immediately after drawing each individual hex, its center's x coords and y coords are stored in xHexes and yHexes respectively. Also, immediately after generating the type of tile (e.g. grass, beach), the type of tile is also stored in an array named hexTypes. Therefore, I can get the x and y coords and type of hex of any hex I want on the map just by referencing its index.
Code used to draw a single hexagon:
beginShape();
for (float a = PI/6; a < TWO_PI; a += TWO_PI/6) {
float vx = x + cos(a) * gs*2;
float vy = y + sin(a) * gs*2;
vertex(vx, vy);
}
x is the x coord for centre of hexagon
y is the y coord for centre of hexagon
gs = radius of hexagon
Code used to tesselate hex over the window creating a hex grid:
void redrawMap() {
float xChange = 1.7;
float yChange = 6;
for (int y = 0; y < ySize/hexSize; y++) {
for (int x = 0; x < xSize/hexSize; x++) {
if (x % 2 == 1) {
// if any part of this hexagon being formed will be visible on the window and not off the window.
if (x*hexSize*xChange <= width+2*hexSize && int(y*hexSize*yChange) <= height+3*hexSize) {
drawHex(x*hexSize*xChange, y*hexSize*yChange, hexSize);
}
// only record and allow player to react with it if the entire tile is visible on the window
if (x*hexSize*xChange < width && int(y*hexSize*yChange) < height) {
xHexes.add(int(x*hexSize*xChange));
yHexes.add(int(y*hexSize*yChange));
}
} else {
if (x*hexSize*xChange <= width+2*hexSize && int(y*hexSize*yChange) <= height+3*hexSize) {
drawHex(x*hexSize*xChange, y*hexSize*yChange+(hexSize*3), hexSize);
}
if (x*hexSize*xChange < width && int(y*hexSize*yChange+(hexSize*3)) < height) {
xHexes.add(int(x*hexSize*xChange));
yHexes.add(int(y*hexSize*yChange+(hexSize*3)));
}
}
}
}
}
hexSize is a user-specified size for each hexagon, determining the number of hexagons that will be on the screen.
This answer will help you get to this (green is plains, red is hills and blue is water, also please don't flame my terrible grid):
Note that there is no pathfinding in this solution, only some very simple "can I get there" math. I'll include the full code of the sketch at the end so you can reproduce what I did and test it yourself. One last thing: this answer doesn't use any advanced design pattern, but it assume that you're confortable with the basics and Object Oriented Programming. If I did something which you're not sure you understand, you can (and should) ask about it.
Also: this is a proof of concept, not a "copy and paste me" solution. I don't have your code, so it cannot be that second thing anyway, but as your question can be solved in a bazillion manners, this is only one which I deliberately made as simple and visual as possible so you can get the idea and run with it.
First, I strongly suggest that you make your tiles into objects. First because they need to carry a lot of information (what's on each tile, how hard they are to cross, maybe things like resources or yield... I don't know, but there will be a lot of stuff).
The Basics
I organized my global variables like this:
// Debug
int unitTravelPoints = 30; // this is the number if "travel points" currently being tested, you can change it
// Golbals
float _tileSize = 60;
int _gridWidth = 10;
int _gridHeight = 20;
ArrayList<Tile> _tiles = new ArrayList<Tile>(); // all the tiles
ArrayList<Tile> _canTravel = new ArrayList<Tile>(); // tiles you can currently travel to
The basics being that I like to be able to change my grid size on the fly, but that's just a detail. What's next is to choose a coordinate system for the grid. I choose the simplest one as I didn't want to bust my brain on something complicated, but you may want to adapt this to another coordinate system. I choose the offset coordinate type of grid: my "every second row" is half a tile offset. So, instead of having this:
I have this:
The rest is just adjusting the spatial coordinates of the tiles so it doesn't look too bad, but their coordinates stays the same:
Notice how I consider that the spatial coordinates and the grid coordinates are two different things. I'll mostly use the spatial coordinates for the proximity checks, but that's because I'm lazy, because you could make a nice algorithm which do the same thing without the spatial coordinates and it would probably be less costly.
What about the travel points? Here's how I decided to work: your unit has a finite amount of "travel points". Here there's no unit, but instead a global variable unitTravelPoints which will do the same thing. I decided to work with this scale: one normal tile is worth 10 travel points. So:
Plains: 10 points
Hills: 15 points
Water: 1000 points (this is impassable terrain but without going into the details)
I'm not going to go into the details of drawing a grid, but that's mostly because your algorithm looks way better than mine on this front. On the other hand, I'll spend some time on explaining how I designed the Tiles.
The Tiles
We're entering OOP: they are Drawable. Drawable is a base class which contains some basic info which every visible thing should have: a position, and an isVisible setting which can be turned off. And a method to draw it, which I call Render() since draw() is already taken by Processing:
class Drawable {
PVector position;
boolean isVisible;
public Drawable() {
position = new PVector(0, 0);
isVisible = true;
}
public void Render() {
// If you forget to overshadow the Render() method you'll see this error message in your console
println("Error: A Drawable just defaulted to the catch-all Render(): '" + this.getClass() + "'.");
}
}
The Tile will be more sophisticated. It'll have more basic informations: row, column, is it currently selected (why not), a type like plains or hills or water, a bunch of neighboring tiles, a method to draw itself and a method to know if the unit can travel through it:
class Tile extends Drawable {
int row, column;
boolean selected = false;
TileType type;
ArrayList<Tile> neighbors = new ArrayList<Tile>();
Tile(int row, int column, TileType type) {
super(); // this calls the parent class' constructor
this.row = row;
this.column = column;
this.type = type;
// the hardcoded numbers are all cosmetics I included to make my grid looks less awful, nothing to see here
position.x = (_tileSize * 1.5) * (column + 1);
position.y = (_tileSize * 0.5) * (row + 1);
// this part checks if this is an offset row to adjust the spatial coordinates
if (row % 2 != 0) {
position.x += _tileSize * 0.75;
}
}
// this method looks recursive, but isn't. It doesn't call itself, but it calls it's twin from neighbors tiles
void FillCanTravelArrayList(int travelPoints, boolean originalTile) {
if (travelPoints >= type.travelCost) {
// if the unit has enough travel points, we add the tile to the "the unit can get there" list
if (!_canTravel.contains(this)) {
// well, only if it's not already in the list
_canTravel.add(this);
}
// then we check if the unit can go further
for (Tile t : neighbors) {
if (originalTile) {
t.FillCanTravelArrayList(travelPoints, false);
} else {
t.FillCanTravelArrayList(travelPoints - type.travelCost, false);
}
}
}
}
void Render() {
if (isVisible) {
// the type knows which colors to use, so we're letting the type draw the tile
type.Render(this);
}
}
}
The Tile Types
The TileType is a strange animal: it's a real class, but it's never used anywhere. That's because it's a common root for all tile types, which will inherit it's basics. The "City" tile may need very different variables than, say, the "Desert" tile. But both need to be able to draw themselves, and both need to be owned by the tiles.
class TileType {
// cosmetics
color fill = color(255, 255, 255);
color stroke = color(0);
float strokeWeight = 2;
// every tile has a "travelCost" variable, how much it cost to travel through it
int travelCost = 10;
// while I put this method here, it could have been contained in many other places
// I just though that it made sense here
void Render(Tile tile) {
fill(fill);
if (tile.selected) {
stroke(255);
} else {
stroke(stroke);
}
strokeWeight(strokeWeight);
DrawPolygon(tile.position.x, tile.position.y, _tileSize/2, 6);
textAlign(CENTER, CENTER);
fill(255);
text(tile.column + ", " + tile.row, tile.position.x, tile.position.y);
}
}
Each tile type can be custom, now, yet each tile is... just a tile, whatever it's content. Here are the TileType I used in this demonstration:
// each different tile type will adjust details like it's travel cost or fill color
class Plains extends TileType {
Plains() {
this.fill = color(0, 125, 0);
this.travelCost = 10;
}
}
class Water extends TileType {
// here I'm adding a random variable just to show that you can custom those types with whatever you need
int numberOfFishes = 10;
Water() {
this.fill = color(0, 0, 125);
this.travelCost = 1000;
}
}
class Hill extends TileType {
Hill() {
this.fill = color(125, 50, 50);
this.travelCost = 15;
}
}
Non-class methods
I added a mouseClicked() method so we can select a hex to check how far from it the unit can travel. In your game, you would have to make it so when you select a unit all these things fall into place, but as this is just a proof of concept the unit is imaginary and it's location is wherever you click.
void mouseClicked() {
// clearing the array which contains tiles where the unit can travel as we're changing those
_canTravel.clear();
for (Tile t : _tiles) {
// select the tile we're clicking on (and nothing else)
t.selected = IsPointInRadius(t.position, new PVector(mouseX, mouseY), _tileSize/2);
if (t.selected) {
// if a tile is selected, check how far the imaginary unit can travel
t.FillCanTravelArrayList(unitTravelPoints, true);
}
}
}
At last, I added 2 "helper methods" to make things easier:
// checks if a point is inside a circle's radius
boolean IsPointInRadius(PVector center, PVector point, float radius) {
// simple math, but with a twist: I'm not using the square root because it's costly
// we don't need to know the distance between the center and the point, so there's nothing lost here
return pow(center.x - point.x, 2) + pow(center.y - point.y, 2) <= pow(radius, 2);
}
// draw a polygon (I'm using it to draw hexagons, but any regular shape could be drawn)
void DrawPolygon(float x, float y, float radius, int npoints) {
float angle = TWO_PI / npoints;
beginShape();
for (float a = 0; a < TWO_PI; a += angle) {
float sx = x + cos(a) * radius;
float sy = y + sin(a) * radius;
vertex(sx, sy);
}
endShape(CLOSE);
}
How Travel is calculated
Behind the scenes, that's how the program knows where the unit can travel: in this example, the unit has 30 travel points. Plains cost 10, hills cost 15. If the unit has enough points left, the tile is marked as "can travel there". Every time a tile is in travel distance, we also check if the unit can get further from this tile, too.
Now, if you're still following me, you may ask: how do the tiles know which other tile is their neighbor? That's a great question. I suppose that an algorithm checking their coordinates would be the best way to handle this, but as this operation will happen only once when we create the map I decided to take the easy route and check which tiles were the close enough spatially:
void setup() {
// create the grid
for (int i=0; i<_gridWidth; i++) {
for (int j=0; j<_gridHeight; j++) {
int rand = (int)random(100);
if (rand < 20) {
_tiles.add(new Tile(j, i, new Water()));
} else if (rand < 50) {
_tiles.add(new Tile(j, i, new Hill()));
} else {
_tiles.add(new Tile(j, i, new Plains()));
}
}
}
// detect and save neighbor tiles for every Tile
for (Tile currentTile : _tiles) {
for (Tile t : _tiles) {
if (t != currentTile) {
if (IsPointInRadius(currentTile.position, t.position, _tileSize)) {
currentTile.neighbors.add(t);
}
}
}
}
}
Complete code for copy-pasting
Here's the whole thing in one place so you can easily copy and paste it into a Processing IDE and play around with it (also, it includes how I draw my awful grid):
// Debug
int unitTravelPoints = 30; // this is the number if "travel points" currently being tested, you can change it
// Golbals
float _tileSize = 60;
int _gridWidth = 10;
int _gridHeight = 20;
ArrayList<Tile> _tiles = new ArrayList<Tile>();
ArrayList<Tile> _canTravel = new ArrayList<Tile>();
void settings() {
// this is how to make a window size's dynamic
size((int)(((_gridWidth+1) * 1.5) * _tileSize), (int)(((_gridHeight+1) * 0.5) * _tileSize));
}
void setup() {
// create the grid
for (int i=0; i<_gridWidth; i++) {
for (int j=0; j<_gridHeight; j++) {
int rand = (int)random(100);
if (rand < 20) {
_tiles.add(new Tile(j, i, new Water()));
} else if (rand < 50) {
_tiles.add(new Tile(j, i, new Hill()));
} else {
_tiles.add(new Tile(j, i, new Plains()));
}
}
}
// detect and save neighbor tiles for every Tile
for (Tile currentTile : _tiles) {
for (Tile t : _tiles) {
if (t != currentTile) {
if (IsPointInRadius(currentTile.position, t.position, _tileSize)) {
currentTile.neighbors.add(t);
}
}
}
}
}
void draw() {
background(0);
// show the tiles
for (Tile t : _tiles) {
t.Render();
}
// show how far you can go
for (Tile t : _canTravel) {
fill(0, 0, 0, 0);
if (t.selected) {
stroke(255);
} else {
stroke(0, 255, 0);
}
strokeWeight(5);
DrawPolygon(t.position.x, t.position.y, _tileSize/2, 6);
}
}
class Drawable {
PVector position;
boolean isVisible;
public Drawable() {
position = new PVector(0, 0);
isVisible = true;
}
public void Render() {
// If you forget to overshadow the Render() method you'll see this error message in your console
println("Error: A Drawable just defaulted to the catch-all Render(): '" + this.getClass() + "'.");
}
}
class Tile extends Drawable {
int row, column;
boolean selected = false;
TileType type;
ArrayList<Tile> neighbors = new ArrayList<Tile>();
Tile(int row, int column, TileType type) {
super(); // this calls the parent class' constructor
this.row = row;
this.column = column;
this.type = type;
// the hardcoded numbers are all cosmetics I included to make my grid looks less awful, nothing to see here
position.x = (_tileSize * 1.5) * (column + 1);
position.y = (_tileSize * 0.5) * (row + 1);
// this part checks if this is an offset row to adjust the spatial coordinates
if (row % 2 != 0) {
position.x += _tileSize * 0.75;
}
}
// this method looks recursive, but isn't. It doesn't call itself, but it calls it's twin from neighbors tiles
void FillCanTravelArrayList(int travelPoints, boolean originalTile) {
if (travelPoints >= type.travelCost) {
// if the unit has enough travel points, we add the tile to the "the unit can get there" list
if (!_canTravel.contains(this)) {
// well, only if it's not already in the list
_canTravel.add(this);
}
// then we check if the unit can go further
for (Tile t : neighbors) {
if (originalTile) {
t.FillCanTravelArrayList(travelPoints, false);
} else {
t.FillCanTravelArrayList(travelPoints - type.travelCost, false);
}
}
}
}
void Render() {
if (isVisible) {
// the type knows which colors to use, so we're letting the type draw the tile
type.Render(this);
}
}
}
class TileType {
// cosmetics
color fill = color(255, 255, 255);
color stroke = color(0);
float strokeWeight = 2;
// every tile has a "travelCost" variable, how much it cost to travel through it
int travelCost = 10;
// while I put this method here, it could have been contained in many other places
// I just though that it made sense here
void Render(Tile tile) {
fill(fill);
if (tile.selected) {
stroke(255);
} else {
stroke(stroke);
}
strokeWeight(strokeWeight);
DrawPolygon(tile.position.x, tile.position.y, _tileSize/2, 6);
textAlign(CENTER, CENTER);
fill(255);
text(tile.column + ", " + tile.row, tile.position.x, tile.position.y);
}
}
// each different tile type will adjust details like it's travel cost or fill color
class Plains extends TileType {
Plains() {
this.fill = color(0, 125, 0);
this.travelCost = 10;
}
}
class Water extends TileType {
// here I'm adding a random variable just to show that you can custom those types with whatever you need
int numberOfFishes = 10;
Water() {
this.fill = color(0, 0, 125);
this.travelCost = 1000;
}
}
class Hill extends TileType {
Hill() {
this.fill = color(125, 50, 50);
this.travelCost = 15;
}
}
void mouseClicked() {
// clearing the array which contains tiles where the unit can travel as we're changing those
_canTravel.clear();
for (Tile t : _tiles) {
// select the tile we're clicking on (and nothing else)
t.selected = IsPointInRadius(t.position, new PVector(mouseX, mouseY), _tileSize/2);
if (t.selected) {
// if a tile is selected, check how far the imaginary unit can travel
t.FillCanTravelArrayList(unitTravelPoints, true);
}
}
}
// checks if a point is inside a circle's radius
boolean IsPointInRadius(PVector center, PVector point, float radius) {
// simple math, but with a twist: I'm not using the square root because it's costly
// we don't need to know the distance between the center and the point, so there's nothing lost here
return pow(center.x - point.x, 2) + pow(center.y - point.y, 2) <= pow(radius, 2);
}
// draw a polygon (I'm using it to draw hexagons, but any regular shape could be drawn)
void DrawPolygon(float x, float y, float radius, int npoints) {
float angle = TWO_PI / npoints;
beginShape();
for (float a = 0; a < TWO_PI; a += angle) {
float sx = x + cos(a) * radius;
float sy = y + sin(a) * radius;
vertex(sx, sy);
}
endShape(CLOSE);
}
Hope it'll help. Have fun!
You will have to use similar algorithms we use on pathfinding. you create a stack or queue that will hold a class storing the position of the hex and the number of moves left from that point, initially you insert your starting position with the number of moves you have and mark that hex as done ( to not re-use a position you have already been on ), then you pop an element, and you insert every neighbor of that hex with a number of moves -1. when you insert the hexes with zero moves, those are your endpoints. And before inserting any hex check if it's not already done.
I hope I was clear, your question was a bit vague but I tried to give you an idea of how these solutions are usually done, also I think your question fits more into algorithms rather then processing
Best of luck
This is code. I'm trying to create mini tennis, but right now I just want to learn how to move the bottom bar left and right.
panelObject Class
import javax.swing.*;
import java.awt.*;
class panelObject extends JPanel
{
int ballx = 50;
int bally = 50;
int barx = 405;
int bary = 405;
public void paintComponent(Graphics g)
{
super.paintComponent(g);
this.setBackground(Color.WHITE);//creates the color of th background
g.setColor(Color.BLUE);// creates the color of the object
g.fillOval(ballx,bally,50,50); // creates the size and shape of the object
g.setColor(Color.RED);
g.fillRect(barx,bary, 50,50);
}
}
TennisGame class
public class TennisGame
{
public static void main (String[] args)
{
JFrame f = new JFrame("Tennis"); //Create a JFrame object, its basically the window
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //This sets the default close operation, you don't really need to understand it, just make sure you have it
panelObject object = new panelObject(); // creates a panel object to draw on
f.add(object); // this adds the panel we created to the window
f.setSize(640,480); // this sets the size of the window
f.setVisible(true); // this shows the window
//this for loop just loops 540 times
//each time through the loop, we increase the x value of our rectangle
for (int i = 0; i < 540; i++)
{
//increase the x value of the rectangle; x was declared in the panelObeject class
object.ballx += 1;
//repaint will redraw the screen with the updated rectangle position
object.repaint();
//this whole try catch will pause the program for a while so it doesn't draw everything so fast
try
{
Thread.sleep(3); // time duration
}
catch(Exception e)
{
}
}
}
//update animation
public void update (long timePassed)
{
}
}
In order to have the Bar object move we must
Capture the Inputed key
Decide what that key is an update the bar's properties
redraw the bar
Note: This isn't the most efficient ways of creating such a program, and you might want to consider creating a tick and paint method that are called a designated number of times per second (Like 60, AKA 60 FPS/TPS).
tick being to update the Tennis games information and
paint to render that 'information' accordingly
here is all of your code, i edited some parts and created comments.
static panelObject object; //Declares the JPanel Object to draw on as a Global Variable
public static void main (String[] args)
{
JFrame f = new JFrame("Tennis"); //Create a JFrame object, its basically
the window
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //This sets the default close operation, you don't really need to understand it, just make sure you have it
object = new panelObject(); // initializing the global panel object to draw on
f.add(object); // this adds the panel we created to the window
f.setSize(640,480); // this sets the size of the window
f.setVisible(true); // this shows the window
f.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) { // This Method is called when any key is pressed on the keyboard and stores the key pressed as 'e'
if(e.getKeyCode() == KeyEvent.VK_A) //if the pressed key is 'a' then move left
{
System.out.println("left");
object.barx -= object.ballSpeed; //We know that the key is a so move the bar to the left
}
if(e.getKeyCode() == KeyEvent.VK_D) //if the pressed key is 'd' then move right
{
System.out.println("right");
object.barx += object.ballSpeed; //We know that the key is d so move the bar to the right
}
object.repaint();//Repaints to the screen(Not the Best place to do this,but will work)
}
});
//this for loop just loops 540 times
//each time through the loop, we increase the x value of our circle
for (int i = 0; i < 540; i++)
{
//increase the x value of the rectangle; x was declared in the panelObeject class
object.ballx += 1;
//repaint will redraw the screen with the updated rectangle position
object.repaint();
//this whole try catch will pause the program for a while so it doesn't draw everything so fast
try
{
Thread.sleep(3); // time duration
}
catch(Exception e)
{
}
}
}
//update animation
public static void update (long timePassed)
{
object.repaint();
}
Here is the panelObject class, i didnt change much
public class panelObject extends JPanel{
int ballSpeed = 3; // The ball has a speed property so when it moves you don't hard code the value in (Bad Practice especially in very large programs)
int ballx = 50;
int bally = 50;
int barx = 405;
int bary = 405;
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.WHITE);//creates the color of th background
g.setColor(Color.BLUE);// creates the color of the object
g.fillOval(ballx,bally,50,50); // creates the size and shape of the object
g.setColor(Color.RED);
g.fillRect(barx,bary, 50,50);
}
}
Im doing an assignment for school. I have to create 30 randomly colured GameObjects in random locations. I have to use 2 classes, a GameObject class which contains the GameObject data, x and y co-ordinates and colour, plus the move and paint method... and a main MovingSquaresApplication which puts the GameObjects into an array and calls the paint() and move() methods... the current program compiles, runs, paints 60 squares (paint() and repaint()) but no animation. I've looked at a lot of different posts but still cant get it right. Any help would be great. Here is the code.....
*edited new code
import java.awt.*;
import javax.swing.*;
public class MovingSquaresApplication extends JFrame implements Runnable {
//member data
private static final Dimension WindowSize = new Dimension(600,600);
private static final int NUMGAMEOBJECTS = 30;
private GameObject[] gameObjectsArray = new GameObject[NUMGAMEOBJECTS];
private int i,j;
//constructor
public MovingSquaresApplication(){
this.setTitle("MovingSquaresApplication");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window, centered on the screen
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width/2 - WindowSize.width/2;
int y = screensize.height/2 - WindowSize.height/2;
setBounds(x, y, WindowSize.width, WindowSize.height);
setVisible(true);
for (i=0; i<gameObjectsArray.length; i++){ //fills array with GameObjects
GameObject NewSquare = new GameObject();
gameObjectsArray[i] = NewSquare;
}
Thread t = new Thread(this); //creates and stars a thread
t.start();
}
//threads entry point
public void run(){
while (true){
try {
Thread.sleep(20);
for (j=0; j<gameObjectsArray.length; j++){
gameObjectsArray[j].move();
repaint();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//applications paint method
public void paint (Graphics g){
super.paintComponents(g);
for (i=0; i<gameObjectsArray.length; i++){
gameObjectsArray[i].paint(g);
}
}
//applications entry point
public static void main(String[] args){
MovingSquaresApplication w = new MovingSquaresApplication();
}
}
and the GameObject class
import java.awt.*;
public class GameObject {
//member data
private int x,y,xvel=2,yvel=2;
private Color c;
public GameObject(){
x = (int) (Math.random( )*600);
y = (int) (Math.random( )*600);
int R = (int) (Math.random( )*256);
int G = (int)(Math.random( )*256);
int B= (int)(Math.random( )*256);
c = new Color (R, G, B);
}
//public interface
public void move(){
x += xvel;
y += yvel;
if(x<10)
{
xvel = 2;
}
else if(y<30)
{
yvel = 2;
}
else if(x>=560)
{
xvel = -2;
}
else if(y>=560)
{
yvel = -2;
}
}
public void paint(Graphics g){
g.setColor(c);
g.fillRect(x, y, 30, 30);
}
}
Thanks for all the help, much appreciated
Thanks for the help, I didnt create a class that extends JPanel, i simply put
super.paintComponent(g);
in the paint method, not sure if thats good practice but it worked....also on a sidenote i haven't seen this before
for (GameObject gameObject : gameObjectArray)
what exactly does this do compared to the loop i've used?
You need to paint within the paintComponent method of a JPanel (as I'm sure that you've read,.... and so I recommend that you do just that. That you
Fill your GameObjectsArray with GameObjects, and do so not within any painting method, but on GUI construction
Create a JPanel and override its paintComponent method just as the Swing painting tutorials tell you to do
Call the super's paintComponent method within your override (again as the tutorials will tell you to do)
iterate through your GameObjectsArray (but rename it gameObjectsArray since its a variable not a class) within the paintComponent method
call each GameObject's paint method within that same for loop.
edit: check out this code of yours:
GameObject MoveSquare = new GameObject();
for (y = 0; y < GameObjectsArray.length; y++) {
MoveSquare.move();
}
What you're doing is creating a completely new GameObject object, MoveSquare, and are trying to move it within the for loop, meanwhile you're not touching any of the GameObjects held within the gameObjectsArray. Do you see your mistake here?
Edit 2
Also you're using the y variable as the array index, the same variable that you're using to figure out the y-axis bounds of your GUI -- don't do this. Use a completely independent variable.
Edit 4
And here:
public void paint(Graphics g) {
for (y = 0; y < GameObjectsArray.length; y++) {
GameObject NewSquare = new GameObject();
if (GameObjectsArray[y] == null) {
GameObjectsArray[y] = NewSquare;
NewSquare.paint(g);
}
}
}
You're creating new GameObject variables with each call to paint, ignoring any that already may be present in the array(??). Painting methods should be for painting and painting only. Again, fill your GameObject array with new GameObject items once and in your class constructor, not in a painting method.
You're doing a lot of guessing here, and throwing code at the wall and seeing what sticks is not a good heuristic for creating a program. Instead plan each step on paper before committing code to IDE.
Edit 5
Your if conditions within the GameObject move method need to be fixed. But once you get the code running, you'll see exactly what I mean, as you'll see all your GameObjects running off of the page.
Edit 6
I'm not going to show you all your code, but again, you'll want to create a class that extends JPanel, override its paintComponent method, and that method will be quite simple and look like this:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // do housekeeping painting
// note that I've renamed the array to gameObjectArray
for (GameObject gameObject : gameObjectArray) {
gameObject.paint(g); // this is all you need to call
}
}
Likewise, the run method would be something like:
#Override
public void run() {
while (true) {
try {
Thread.sleep(SLEEP_TIME); // constant for the sleep time
} catch (InterruptedException e) {
e.printStackTrace();
}
// again note that the array has been renamed
for (GameObject gameObject : gameObjectArray) {
gameObject.move(); // this is all that needs to be called here
}
repaint();
}
}
Edit next :)
You're creating two Thread objects and starting them both -- don't do that. Only one will do.
I'm having an issue showing that one rectangle has collided with another. So my question is, how can I get the intersect method to check for collision? Or are there any other ways to handle collision in this situation?
I'm creating a turn-based combat game (similar to Final Fantasy or Legend of Dragoon) where the player's character is on the right side of the screen and the enemy is on the left side. The player and enemy each take turns attacking. So when the player attacks, the sprite animation moves across the screen from right to left until it stops in front of the enemy, attacks, and returns to it's starting coordinates. Both the player and enemy have a rectangle drawn around them to represent the bounds of each character.
When the player moves forward, he passes through the Enemy's rectangle and stops within it. At this point there should be output to the console saying "INTERSECT!" to show that there was a collision between the two rectangles, but unfortunately there isn't.
Please note that I have omitted the unnecessary pieces of code within my classes and tried to provide the code that pertains to my problem.
This is my entry point, GameClass:
public class GameClass extends BasicGame{
//other variable declarations
public void init(GameContainer container) throws SlickException {
player = new Player();
enemy = new Enemy();
skeleton = new Skeleton();
enemy = skeleton;
playX = player.getStartX(); //700
playY = player.getStartY(); //140
playW = player.rect.getWidth(); //40
playH = player.rect.getHeight(); //70
enemyX = enemy.getStartX(); //100
enemyY = enemy.getStartY(); //140
enemyWidth = enemy.getWidth(); //50
enemyHeight = enemy.getHeight(); //55
SpriteSheet sheet = new SpriteSheet("data/homeranim.png", 36, 65);
anim = new Animation();
for (int i=0;i<8;i++) {
anim.addFrame(sheet.getSprite(i,0), 150);
}
}
public void render(GameContainer container, Graphics g)
throws SlickException {
anim.draw(playX,playY); // draws player animation
skeletonAnim.draw(enemyX, enemyY); // draws enemy
g.draw(player.rect); //draws player bounds
g.draw(enemy.rect); //draws enemy bounds
}
public void update(GameContainer container, int delta)
throws SlickException {
playerUpdate(delta);
if (player.rect.intersects(enemy.rect)) {
System.out.println("INTERSECT!");
System.out.println("Player minX: " + player.rect.getMinX());
System.out.println("Player maxX: " + player.rect.getMaxX());
System.out.println("Enemy minX: " + enemy.rect.getMinX());
System.out.println("Enemy maxX: " + enemy.rect.getMaxX());
}
}
public void playerUpdate(int delta) {
if (playerForward == true){
playX -= delta * 0.4f;
if (playX <= 140) {
playX = 140;
playerForward = false;
playerBackward = true;}
}
if (playerBackward == true) {
playX += delta * 0.4f;
if (playX >= 700) {
playX = 700;
playerBackward = false;
delay = 1250;
}
public void keyReleased(int key, char c) {
if (key == Input.KEY_ENTER){
playerForward = true;}
}
}
This is a glimpse at my Player class:
public class Player {
private int startX = 700;
private int startY = 140;
public Shape rect = new Rectangle(startX, startY, 40, 70);
//plus getters and setters
}
And my entire Enemy class:
public class Enemy {
private int startX, startY, width, height;
public Shape rect = new Rectangle(startX, startY, width, height);
// plus getters and setters
}
My Skeleton class extends Enemy:
public class Skeleton extends Enemy {
public Skeleton() {
// TODO Auto-generated constructor stub
setMaxHealth(120);
setStartX(100);
setStartY(140);
setWidth(50);
setHeight(55);
}
}
Note: Since I've switched g.drawRect() to g.draw(), enemy rectangle isn't being drawn.
Rect bounds at starting point: http://i.imgur.com/QDDk858.png
Rect bound where collision should be: http://i.imgur.com/pOANfvN.png
I hope I've provided enough code to show you what my problem is. I've rummaged through the internet for hours with no luck. If there is any other code I need to provide, please do not hesitate to ask. Thank you very much for your help and support!
You are not updating the hitbox positions themselves.
You are drawing this:
g.drawRect(playX, playY, playW, playH); //draws player bounds
g.drawRect(enemyX, enemyY, enemyW, enemyH); //draws enemy bounds
But this isn't the actual hitbox, it's just the position of the player/enemy and the rectangles drawn here will be on the correct position while the hitboxes themselves aren't.
I suggest you do the following:
public void update(GameContainer container, int delta)
{
playerUpdate(delta);
player.rect.setLocation(playX, playY);
enemy.rect.setLocation(enemyX, enemyY); // update the hitboxes to the new positions
if (player.rect.intersects(enemy.rect))
{
System.out.println("INTERSECT!");
}
}
public void playerUpdate(int delta)
{
if (playerForward == true)
{
playX -= delta * 0.4f;
if (playX <= 140)
{
playX = 140;
playerForward = false;
playerBackward = true;
}
}
if (playerBackward == true)
{
playX += delta * 0.4f;
if (playX >= 700)
{
playX = 700;
playerBackward = false;
delay = 1250;
}
}
}
public void keyReleased(int key, char c)
{
if (key == Input.KEY_ENTER)
{
playerForward = true;
}
}
Furthermore, as you seem to be new to game development in Java, some tips for you:
Format your code properly
Always place full {...} after if, else, switch, while, for, etc.; proper line indentation, .
Think OO (Object-Oriented)
This one is pretty important. Your enemy and player class should both extend some kind of entity class because they both will pretty much want to obtain similar behavior (avoid code duplication!). Sum up similar behavior to a super class, simplify the behavior to be controlled with a few adjustable parameters and so on.
For example, you store the positions of your enemy and player as a static integer in your main class. This is not OO. Move the positions to the entity class where you can implement it in whatever manner you wish.
Don't just throw an exception for no reason
Your update(...) method throws a SlickException even though it's never needed.
Be careful about encapsulation
This is something a lot of beginners do: Just grab some parameters, put them in a class as private (or maybe even public) and generate getters- and setters for them. This is not encapsulation. This is almost as bad as making them public in the first place.
But why don't we just make everything public?
We don't want anyone (or even ourselves) to rely on some parameters that just happen to be there because of some very specific implementation of something we might want to change later. Don't just put all possible values out there to be changed, the sense of encapsulation is to be independent from what kind of implementation we end up using and to protect the usability of our code by guarding what can be set/changed.
Performance does matter
This is one of the aspects you should watch out for, for any kind of software, but you can often most drastically see the consequences in games. Performance is important! And by that, I don't mean that you have to watch out for every single detail, but just keep an overview in mind on how to improve and fasten up your code, especially with frequently called methods such as update(..) and render(..) in Slick2D.
Update
As a solution to another problem discussed in the comments:
public class Enemy {
private int startX, startY, width, height;
public Shape rect = new Rectangle(startX, startY, width, height);
// plus getters and setters
}
width and height can only be 0 as they are never assigned an integers have the value 0 per default, so the enemy rectangle hitbox does have 0 width and will never trigger.
Try something like:
public class Enemy {
private int startX, startY, width = 50, height = 70;
public Shape rect = new Rectangle(startX, startY, width, height);
// plus getters and setters
}
This should work, but you should probably move all these attributes to the enemy class and put them in the constructor.
My plan is to design a simple game of Pool in Java.
OOP makes the most sense here for the balls. All the balls have the same functionality, so because of that it would be a good idea to make a Ball class that would handle the relative position of the ball and other variables such as when it goes in a hole it removes itself and increments your score. So when it hits a hole Ball.dispose (); would fit nicely.
My issue is that I do not know how to manipulate the ball and dispose of it. Also inorder to move it I rely on Thread.sleep instead of java.swing.timer because there is no available Action Performed method I can rely on.
How can I move the ball more easily and get rid of it when needed?
The green thing covering the ball is my way of erasing the last position of the ball by drawing a green oval over it.
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class Main extends JFrame{
Ball redball = new Ball (285, 600, 20, 20, Color.RED);
//variables to control redball
private int rX = redball.getX();
private int rY = redball.getY();
private final int rWidth = redball.getWidth();
private final int rHeight = redball.getHeight();
int Force = 30;
int Bearing = 20; // True Bearing
public Main (){
setSize(600, 800);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Pool");
}
public void paint(Graphics g){
super.paint(g);
// draw the table
g.setColor (Color.GREEN);
g.fillRect(100, 100, 400, 600);
g.setColor (Color.BLACK);
g.drawRect(99, 99, 401, 601);
//draw start ball
g.setColor(redball.getColor());
g.fillOval(rX, rY, rWidth, rHeight);
if (Force == 30){
for (int i = Force; i > 0;i--){
try {
Thread.sleep(100);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
Force--;
if (rY > 98 + rWidth) {
rY = rY - i;
rX = rX + (Bearing/5);
}
g.fillOval(rX, rY, rWidth, rHeight);
g.setColor(Color.GREEN);
repaint ();
g.fillOval(rX - (Bearing/5), rY + i, rWidth, rHeight); // repaint last ball
g.setColor(Color.RED);
repaint ();
}
}
// Ball.dispose (redball);
}
public static void main(String[] args) {
new Main();
}
Here is the class for the ball
public class Ball {
private int x;
private int y;
private int width;
private int height;
private Color color;
public Ball (int x, int y, int width, int height, Color color)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
public void setX (int x){
this.x = x;
}
public int getX (){
return this.x;
}
public void setY (int x){
this.y = y;
}
public int getY (){
return this.y;
}
public void setWidth (int width){
this.width = width;
}
public int getWidth (){
return this.width;
}
public void setHeight (int height){
this.height = height;
}
public int getHeight (){
return this.height;
}
public void setColor (Color color){
this.color = color;
}
public Color getColor (){
return this.color;
}
public static void dispose(Ball ball) {
ball = null; // if I call this method nothing happens
}
}
What I would do is maintain a List<Ball> field of all active balls. Then each iteration, iterate through that list and update and paint the Balls. When a Ball goes away, it's simply a matter of removing it from the list.
A consideration is: Who determines when its time to dispose of a ball? If something outside the Ball does, you have a few options, among others:
As I mentioned above, you could simply remove the Ball from the list.
You could set a "dead" flag in the Ball elsewhere, then on each iteration remove all Balls from the list whose "dead" flag has been set.
If the Ball itself determines when it is time to go away, you have a few options, among others, there as well:
Perhaps a method boolean Ball.think(), that updates the state of the ball and returns true if the Ball is still good, or false if its dead. Then when you iterate through your list of balls, you call think() on all of them to update, and remove ones from the List that returned false.
Related to option 2 above, if a Ball has a think method or something similar, the ball can set its own "dead" flag (or something outside the Ball can set it), then the main loop can remove dead balls from the list.
If you want to keep the Ball instance around and just not draw it, you have a few options there too:
Simply skip processing of Balls marked as "dead" instead of removing them.
Move "dead" balls to a different dead Ball list.
Personally, I like the boolean think() method, because it easy allows you to specify a base interface for objects in the scene. You can also have objects paint themselves in that case. E.g.:
interface Entity {
public boolean think ();
public void paint (Graphics g);
}
class Ball implements Entity {
#Override public boolean think () {
// return true if alive, false if dead
}
#Override public void paint (Graphics g) {
// draw this ball
}
}
// then in your main update loop:
List<Entity> entities = ...;
Iterator<Entity> eit = entities.iterator();
while (eit.hasNext())
if (!eit.next().think())
eit.remove();
// and in paint:
for (Entity e:entities)
e.paint(graphics);
If you want to take the option I mentioned above of just skipping "dead" balls instead of removing them, something like this would be more appropriate (leaving out Ball for brevity), where isActive() returns true if the ball is active or false if it is temporarily "dead":
interface Entity {
public boolean isActive ();
public void think (); // think returns nothing
public void paint (Graphics g);
}
// then in your main update loop:
List<Entity> entities = ...;
for (Entity e:entities)
if (e.isActive())
e.think();
// it is the responsibility of something outside the ball to restore it to an
// active state, since think() isn't called if !isActive(). alternatively, you
// could always call think(), and just don't paint inactive balls.
// and in paint:
for (Entity e:entities)
if (e.isActive())
e.paint(graphics);
Still, you don't have to do it that way, there are plenty of arguments for all of the options listed above, and more. In your application, for example, there's not much need for an Entity interface if you know you are only dealing with Balls; and think() might not be the most convenient way to go if all of your physics logic is happening somewhere else (although of course the code could be written to place the logic in Ball).
As you can see there are many ways to skin a cat, but I hope something here helps.
Here are some suggestions.
Create a Rack class that contains all of the balls for your game.
Move the draw code for a ball into your Ball class. I realize that this suggestion is not pure MVC, but games are a lot easier to code when objects draw themselves.
Don't try to repaint part of the screen. It's a lot easier, and pretty fast, to just redraw the entire screen.
Don't dispose any Ball instances. Move it to some negative position that your draw code recognizes and doesn't draw.
When you're coding an action game, your main loop should look something like this (psudeocode).
while (running) {
calculateRackPosition();
drawRack();
Thread.sleep(100L);
}
You incrementally move the balls, then redraw the screen. This is how you code any animation. This code would run while the balls are moving.
You would write other code for when the person is aiming his shot.