I'm building a game where the object is to move a knight around a world and be able to fight other knights.
I've got a class Main that starts the game:
public class Main {
public static void main(String[] args) {
new Game();
}
}
A class Game that creates a JFrame:
import java.awt.GridLayout;
import javax.swing.JFrame;
public class Game {
public Game() {
JFrame frame = new JFrame();
frame.setTitle("Knights Tournament");
frame.add(new Board());
frame.setSize(700, 700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
And a class Board which (you guessed it!) creates the GameBoard:
public class Board extends JPanel {
Tile[][] grid = new Tile[15][15];
public Board(){
// Create the grid
CreateGrid();
// Add the player
grid[0][0] = new Player(0, 0);
}
// Method that creates a grid of tiles
private void CreateGrid() {
setLayout(new GridLayout (15, 15));
for(int i = 0; i < 15; i++){
for(int j = 0; j < 15; j++){
grid[i][j] = new Tile(i, j);
add(grid[i][j]);
}
}
}
}
The grid initialy consists of 15x15 tiles.
public class Tile extends JButton implements ActionListener {
public int xCo;
public int yCo;
public Tile(int x, int y) {
setXCo(x);
setYCo(y);
}
public void setXCo(int x) {
this.xCo = x;
}
public void setYCo(int y) {
this.yCo = y;
}
public int getXCo() {
return xCo;
}
public int getYCo() {
return yCo;
}
}
The problem i'm facing is the following: I would like to replace grid[0][0] by another class Player that expands tile. The difference between tile and player would be that the Jbutton gets a text saying 'P' i've tried this:
public class Player extends Tile{
public Player(int x, int y) {
super(x, y);
this.setText("P");
}
}
In the constructor of the class board i tried changing grid[0][0] from tile to player so it would display P, but it doesn't do so for some reason (It does change the type of grid[0][0] to player...) I hope someone can help.
You shouldn't try and maintain the state of the object with the UI, per se. Rather the UI should visualise the state of the underlying logic of the game (or model). This allows you to modify the model, change the rules, add/remove elements (think adding new monsters or loot for example), without need to physically modify the UI or how it visualise this state (to a large degree).
The idea is, the Player attributes should be maintain with an class of it's own, which is managed by the model. The UI would then be used to visualise the state the model.
When clicked the Tile would notify the "controller". The controller would request stateful information from the games "model" and based on this information the "controller" would change the state of the button(s) to meet the requirements of the "model"
This would simply require the "controller" to update the button(s) information (text/icons) without needing to change the physical buttons.
This separates the responsibilities into defined layers and reduces the coupling of the code, so one or more layers can change, but the overall structure doesn't break down or need to be heavily modified to handle these changes.
The model is responsible for maintaining the virtual state of the game, for managing the interactions between the individual game objects (combat etc).
The UI is responsible for providing a visualisation of the model for the user and the controller is used to manage the interaction between the take, taking user input and pass it to the model, monitoring changes to the model's state and telling the UI when it needs to update.
The Model–view–controller paradigm is a common approach used in GUI development across different lanuguages. Take a look at Model–view–controller for more details.
Related
Hello fellow programmers,
I've ran into a little issue in my code that I can't seem to crack. It has to do with the Jframe; Graphics area of Java. The code that I'll post below, is over a drawing method. Which purpose is to draw the "rooms" that are in a ArrayList roomList which is located in another class hence lvl. before. This off-course doesn't happen, hence the post on here.
public class LevelGUI implements Observer {
private Level lv;
private Display d;
public LevelGUI(Level level, String name) {
this.lv = level;
JFrame frame = new JFrame(name);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
d = new Display(lv, 500, 500);
frame.getContentPane().add(d);
frame.pack();
frame.setLocation(0, 0);
frame.setVisible(true);
}
private class Display extends JPanel {
public Display(Level fp, int x, int y) {
addKeyListener(new Listener());
setBackground(Color.GRAY);
setPreferredSize(new Dimension(x + 20, y + 20));
setFocusable(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
private void draw(Graphics g) {
Level lvl = new Level();
for(int i = 0; i < lvl.roomList.size(); i++) {
Room room = lvl.roomList.get(i);
g.setColor(room.floorColor);
g.drawRect(room.posX, room.posY, room.roomWidth, room.roomHeight);
}
}
}
}
To get some background info on the program. roomList is the ArrayList, and it is filled with various different sized and colored rooms. The rooms themselves are objects.
Here comes first Level class:
public class Level extends Observable {
private boolean Switch = true;
public ArrayList<Room> roomList = new ArrayList<Room>();
(...)
}
Here is the Class Room() that is used to create the rooms.
public class Room {
Color floorColor;
int roomWidth;
int roomHeight;
int posX;
int posY;
public Room(int dx, int dy, Color color) {
this.floorColor = color;
this.roomHeight = dy;
this.roomWidth = dx;
this.posY = 0;
this.posX = 0;
}
(...)
}
I've managed to locate where the problem is thought to occur, and it's the code in the for-loop. I tried switching the roomList.size() for an integer to test if it was the loop., But it wasn't. It is possible to draw a figure outside of the for-loop.
and again, the problem isn't an error message, the program simply doesn't draw the rooms that I've instructed it to draw in the method draw().
The display output looks like this:
Thanks beforehand!
Be aware that the paintComponent() method is invoked by Swing whenever the framework thinks the component needs to be rendered on screen. This usually is when the window is getting visible - initially or because some other window no longer hides the component. Such events are out of your control.
So your application should create a state and be ready to draw it anytime. Therefore you do not create state (like a level) inside the paint() or paintComponent() method. Put that elsewhere - if need be into the constructor.
Looking at you code:
As you are creating a new level inside paintComponent()/draw(), is it correct to assume that this level has no rooms associated? In that case the method is right to return without having painted anything.
If your application thinks the screen should be updated call repaint(), knowing that the paint() method will be called by the framework soon.
I'm working on a vertical scrolling game, and I'm using a thread to generate new enemies every 2 seconds. Each enemy is an image in a JPanel. For some reason, The generated enemies are not showing up in the JFrame, but they are present. When the player collides with one of the enemies, all the enemies show up.
Here's the code:
private void checkCollision() {
for(AlienShip as : enemies) {
if(player.getBounds().intersects(as.getBounds()))
player.setVisible(false);
}
}
private void setAlien() {
alien = new AlienShip();
add(alien);
enemies.add(alien);
System.out.println("Enemies: " + enemies.size());
}
public Thread alienGenerator() {
for(int i = 0; i < 3; i++) { // these are being drawn
setAlien();
}
return new Thread(new Runnable() {
#Override
public void run() {
int sleepTime = 2000;
while(true) {
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
System.out.println(e);
}
setAlien(); //these aren't
}
}
});
}
private void gameLoop() {
alienGenerator().start();
mainTimer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
checkCollision();
}
});
mainTimer.start();
}
It always seems that you're Darned If You Do And Darned If You Don't. As far as I'm concerned the code you had placed in your earlier post was adequate, as a matter of fact, it was still lacking (no PlayerShip Class). The code example in this post does even less justice. Never the less......
Before I get started I just want you to know that I personally would have tackled this task somewhat differently and the meager assistance provided here will be solely based on the code you have already provided in this and previous posts.
The reason you are not seeing your Alien Ships displaying onto the Game Board upon creation is because you don't revalidate the board panel. As you currently have your code now this can be done from within the Board.setAlien() method where the Alien Ships are added. Directly under the code lines:
alien = new AlienShip();
add(alien);
enemies.add(alien);
add the code line: revalidate();, so the code would look like this:
alien = new AlienShip();
add(alien);
enemies.add(alien);
revalidate();
Your Alien Ships should now display.
On A Side Note:
What is to happen when any Alien Ship actually makes it to the bottom of the Game Board? As a suggestion, have them re-spawn to the top of the game board (serves ya right fer missin em). This can be done from within the AlienShip.scrollShip() method by checking to see if the Alien Ship has reached the bottom of the board, for example:
public void scrollShip() {
if (getY() + 1 > this.getParent().getHeight()) {
setY(0 - PANEL_HEIGHT);
}
else {
setY(getY() + 1);
}
}
In my opinion, PANEL_HEIGHT is the wrong field name to use. I think it would be more appropriate to use something like ALIEN_SHIP_WIDTH and ALIEN_SHIP_HEIGHT. Same for the variables panelX and panelY, could be alienShipX and alienShipY. Food for thought.
As you can see in the code above the current Game Board height is acquired by polling the Game Board's getHeight() method with: this.getParent().getHeight(). This allows you to change the Game Board size at any time and the Alien Ships will know where that current boundary is when scrolling down. All this then means that the setResizable(false); property setting done in the Main Class for the Game's JFrame window can now be resizable: setResizable(true);.
You will also notice that when the Alien Ship is re-spawned at top of the Game Board it is actually out of site and it flows into view as it moves downward. I think this is a much smoother transition into the gaming area rather than just popping into view. This is accomplished with the setY(0 - PANEL_HEIGHT); code line above. As a matter of fact even when the game initially starts, your Alien Ships should flow into the the gaming area this way and that can be done from within the AlienShip.initAlienShip() method by initializing the panelY variable to panelY = -PANEL_HEIGHT;.
This now takes me to the initialization of the PANEL_WIDTH and PANEL_HEIGHT fields. The values seem enormous (224 and 250 respectively). Of course you may have set to these sizes for collision testing purposes, etc but I think an image size of 64 x 35 would most likely suffice:
This image should be a PNG image with a transparent background which then eliminates the need for the setBackground(Color.BLUE); code line located within the AlienShip.initAlienShip() method.
The AlienShip.getX() and AlienShip.getY() methods should be overridden:
#Override
public int getX() { ... }
#Override
public int getY() { ... }
I think extending the AlienShip Class to JLabel would be better than to JPanel. To JPanel seems like overkill:
public class AlienShip extends JLabel { ... }
Adding a background image to the Game Board can add pizazz to the game. This can be achieved by adding the following code to the Board.paintComponent() method:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
ImageIcon imgIcon = new ImageIcon("images/StarBackground.png");
Image img = imgIcon.getImage();
g.drawImage(img, 0, 0, this.getSize().width, this.getSize().height, this);
}
Images can be acquired here.
This should keep you going for a while. Before to long it'll be Alien mayhem.
I have a class that creates a JFrame on which a simple game of Tetris will be played, I also have a class DrawSquare, which does exactly what you think it does, however when I initialise a new instance of the DrawSquare class and then try to draw that one and all the others to my JFrame things start to go wrong, the code is intended for one square to be drawn in the top left hand corner and then drop down a line at a time until it reaches the bottom of the frame (it does this), then a new square should be drawn in the second column at the top of the frame, as well as our first square in the bottom left hand corner, however once it starts dropping down the second column I get a series of squares drawn in a diagonal towards the top right hand corner. At the moment all I plan for the code to do is have a square drop from the top row of each column and stop when it reaches the bottom of the frame, am I storing the instance of the class at the wrong point in the code? Edit: In fact I'm pretty sure it's that, I'd want to store that instance when it reaches the bottom. Does every instance of the class need its own timer?
public class Tetris extends JFrame {
public static final int height = 20; //height of a square
public static final int width = 20; //width of a square
public int xPos = 0; //column number of the square
public int yPos = 0; //row number of the square
public static void main(String[] args){
Tetris tet = new Tetris();
}
public Tetris() {
DrawSquare square = new DrawSquare(xPos, yPos, width, height, false);
add(square);
DrawSquare.squares.add(square);
setSize(220,440);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
public class DrawSquare extends JPanel {
public static List<DrawSquare> squares = new ArrayList<>();
protected int xPos;
protected int yPos;
protected int width;
protected int height;
protected Timer timer = new Timer(200, new TimerListener());
protected boolean endFall = false;
public DrawSquare(int xPos, int yPos, int width, int height, boolean endFall) {
this.xPos = xPos;
this.yPos = yPos;
this.width = width;
this.height = height;
this.endFall = endFall;
this.timer.start();
}
class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
yPos++;
if (yPos > 19) {
yPos = 19;
endFall = true;
}
if (endFall == true) {
timer.stop();
if (xPos > 8) {
xPos = 8;
}
xPos++;
endFall = false;
yPos = 0;
DrawSquare newSqr = new DrawSquare(xPos, yPos, width, height, true);
squares.add(newSqr);
add(newSqr);
}
timer.start();
repaint();
}
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Iterator<DrawSquare> it = squares.iterator();
while (it.hasNext()) {
DrawSquare square = it.next();
g.fillRect(square.xPos * square.width, square.yPos * square.height, square.width, square.height);
}
}
}
You are giving a great example of the fundamental misunderstanding beginners have of how the swing (and many other graphics toolkits) render stuff to the screen. I will give an overview of that, as it pertains to you, then answer your immediate questions and explain how to fix your code.
It took me a (very long) while to figure out how this stuff works my self, so please bear with me. I hope that reading through this answer will help you in a much more general way than answering this one question.
Asynchronous Drawing
Swing draws windows in a totally different sequence (the event dispatching thread) than the ones that modifies the state of your program (the main thread, as well as timer and other threads). You can modify the coordinates of things you want to draw as many times as you like in the main thread, but the changes will not show up until you request them to by calling JComponent.repaint() on one of your components. This will generally trigger a nearly-immediate repaint of the component, displaying your latest state.
If you change the coordinates of a widget like a JPanel in your main thread, it will likely show up immediately. This is because the methods you use to set the position will trigger repaint requests internally.
A repaint request gets queued and eventually processed by the event dispatching thread. This is where the paintComponent method gets called. The paintComponent method should therefore only draw. It should not do any other logic. If it needs to know how to draw some specialized stuff, the information for that should be stashed somewhere accessible by one of the other threads.
In short, you make calculations and update state as you need in the main thread or the timer. Then you access that state in the event dispatching thread via the paintComponent method.
Timers
There are a bunch of ways you can use timers to run your GUI, but you only really need one for the current application. In your case, the timer only needs to do two things:
Check if a block has fallen all the way down and doesn't need to move any more.
Trigger a repaint of your panel.
You do not need to compute the updated position of the blocks in the timer if the block's position is a simple equation with respect to time. If you know the time at which a block appears on the screen and the current time, you know how far the block has moved, so you can paint it in the correct spot based purely on the elapsed time.
If you had a more complicated system with paths that you could not predict purely on the time, I would recommend sticking the movement logic into the timer events as well. In that case, you might consider having multiple timers, or switching to java.util.timer. But again, this does not apply to your current case (even with multiple blocks).
Model and View
The model of your program is the thing that holds the abstract state. In this case, the positions and other meta-data about all your blocks. The view is the part that does the rendering. It is usually a good idea to separate these two things. There is often a third component to GUIs, called the controller, which connects the model and view to the user. We will ignore it here since you are not asking about controlling the blocks yet.
In your current code, you have attempted to represent your blocks with an extension to JPanel and a static list of existing blocks. While a JPanel may be a convenient way to display rectangular blocks with some custom graphics in them (like icons), I would recommend that you start by drawing the blocks directly using the Graphics object passed to paintComponent. At least initially, it will help you to think of the drawing code and the game logic as separate entities.
Final Rant Before Code Dump
I have made rewrites to your code to encapsulate all the ranting I did before into code. Here are some additional minor points about what I did that may help explain my reasoning:
When you call JFrame.add(...) to add a component to a JFrame, you are really calling JFrame.getContentPane().add(...). The content pane is where 90% of normal swing components go in a window. Therefore, we can either set the JPanel that will do the rendering as your content pane or we can add it to the current content pane. I have chosen to do the latter so that you can add other widgets, like a score board, at a later time.
Class names should generally be nouns, while methods are often verbs. This is not an absolute rule (nothing really is), but naming things this way will often help you visualize the interactions between objects in a more meaningful way. I have renamed DrawSquare to GamePiece for this reason.
There is no longer any reason for GamePiece to be a JPanel. It just needs to know its own width, height, and time of appearance.
The other problem with trying to have DrawSquare draw itself is that a component can only really draw within its own bounding box. So you really want to override the paintComponent of whatever holds the rectangles.
The rendering class maintains a reference to two lists of GamePieces. One is for the moving objects and one is for the ones that have fallen. The logic for moving them between the lists is in the timer. This is better than say adding a flag to GamePiece because it facilitates incremental repaint. I will only partially illustrate this here, but there is a version of repaint that only requests a small region to be painted. This would be useful to speed up the movement.
Code
public class Tetris extends JFrame
{
public static final int height = 20; //height of a square
public static final int width = 20; //width of a square
public static final int x = 0;
private GamePanel gamePanel;
public static void main(String[] args)
{
Tetris tet = new Tetris();
// Normally you would tie this to a button or some other user-triggered action.
tet.gamePanel.start();
tet.gamePanel.addPiece(new GamePiece(width, height, x));
}
public Tetris()
{
getContentPane().setLayout(new BorderLayout());
gamePanel = GamePanel();
add(gamePanel, BorderLayout.CENTER);
setSize(220,440);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
}
public class GamePanel extends JPanel
{
private List<GamePiece> moving;
private List<GamePiece> still;
private Timer timer;
public GamePanel()
{
moving = new ArrayList<>();
still = new ArrayList<>();
timer = new Timer(100, new TimerListener());
}
public addPiece(int width, int height, int x)
{
moving.add(new GamePiece(width, height, x));
}
public void start()
{
timer.start();
}
#Override
public void paintComponent(Graphics g)
{
Rectangle clip = g.getClipBounds(null);
Rectangle rectToDraw = new Rectangle();
// I prefer this, but you can make the call every
// time you call `GamePiece.getY()`
long time = System.currentTimeMillis();
for(GamePiece piece : this.moving) {
rectToDraw.setSize(piece.width, piece.height)
rectToDraw.setLocation(piece.x, piece.getY(time))
if(rectangleToDraw.intersects(clip))
((Graphics2D)g).fill(rectToDraw)
}
for(GamePiece piece : this.still) {
rectToDraw.setSize(piece.width, piece.height)
rectToDraw.setLocation(piece.x, piece.getY(time))
if(rectangleToDraw.intersects(clip))
((Graphics2D)g).fill(rectToDraw)
}
}
private class TimerListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
long time = System.currentTimeMillis();
// Using non-iterator loop to move the pieces that
// stopped safely. Iterator would crash on in-loop move.
for(int i = 0; i < moving.size(); i++) {
piece = moving.get(i);
if(piece.getY(time) > 440 - piece.height) {
moving.remove(i);
still.add(piece);
i--;
}
}
repaint();
}
}
}
public class GamePiece
{
public final int width;
public final int height;
public final long startTime;
public int x;
public GamePiece(int width, int height, int x)
{
this.width = width;
this.height = height;
this.startTime = System.currentTimeMillis();
this.x = x;
}
public int getY(long time)
{
// This hard-codes a velocity of 10px/sec. You could
// implement a more complex relationship with time here.
return (int)((time - this.startTime) / 100.0);
}
}
Your main problem in a nutshell: you need to separate the JPanel component class from the square logical class. Right now, they are one and the same, and every time you create a new DrawSqaure, you're creating a new JPanel, starting a new Swing Timer, and thus calling code that doesn't need to be called. This is also forcing you to make the List static else you'd have a stack overflow error. Solution: separate the two out, make your List non-static, and use only one Swing Timer.
Quick question, I have developed 3 A.I's each with a different depth.
Currently to choose what A.I you want to play against you have to go into the java file called Main.java and change it to whichever one you want. The line to change is:
chessGame.setPlayer(Piece.COLOR_BLACK, ai3);//Here A.I is assigned
I want to allow the user to have an option at the start of the game to choose the A.I. I was hoping for some help with the interface, I was thinking something something like a JOptionpane might work.
(I'm just not sure how to do one for the A.I selection)
Current A.I's
ai1
ai2
ai3
package chess;
import chess.ai.SimpleAiPlayerHandler;
import chess.gui.ChessGui;
import chess.logic.ChessGame;
import chess.logic.Piece;
public class Main {
public static void main(String[] args) {
// Creating the Game
ChessGame chessGame = new ChessGame();
// Creating the Human Player
//Human Player is the Object chessGui
ChessGui chessGui = new ChessGui(chessGame);
//Creating the A.I's
SimpleAiPlayerHandler ai1 = new SimpleAiPlayerHandler(chessGame);//Super Dumb
SimpleAiPlayerHandler ai2 = new SimpleAiPlayerHandler(chessGame);//Dumb
SimpleAiPlayerHandler ai3 = new SimpleAiPlayerHandler(chessGame);//Not So Dumb
// Set strength of AI, how far they can see ahead
ai1.maxDepth = 1;
ai1.maxDepth = 2;
ai3.maxDepth = 3;
//Assign the Human to White
chessGame.setPlayer(Piece.COLOR_WHITE, chessGui);
//Assign the not so dumb A.I to black
chessGame.setPlayer(Piece.COLOR_BLACK, ai3);
// in the end we start the game
new Thread(chessGame).start();
}
}
Thanks for any help.
You should probably use a JComboBox to allow the user to select among the 3 options available. If you make a splash JFrame with this JComboBox you can then create your main game frame afterward and pass it the value from the JComboBox.
For example, you could have the JComboBox give options of difficulty setting Easy, Medium, and Hard. Using an action listener on a JButton get the selected value from the JComboBox and convert it to int value appropriate for your minimax algorithm. That is, pass 1 for easy, 2 for medium, and 3 for hard.
Next change your ai class so that maxDepth is in the constructor. Then when you instantiate your ai, just give it the value that was passed forward from the previous frame and you will have created the only ai you need at the right difficulty setting.
EDIT:
It looks like you managed to get something similar working which is great! In case it helps you, I have included a brief example of how I would have done this below. Note that I also set it up such that your SimpleAiPlayerHandler constructor also takes an int value for instantiating the maxDepth variable. You'll need to add this. Since it uses classes I don't have, I can't compile it. However, if anyone else needs to do something similar, just remove everything in the DifficultyListener except the print statement and the line that get's the difficulty from the JComboBox and you'll see it working (and compiling).
import javax.swing.*;
import java.awt.event.*;
public class ChessSplash extends JFrame {
private final JComboBox<Difficulty> difficultySetting;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ChessSplash gui = new ChessSplash();
gui.setVisible(true);
}
});
}
public enum Difficulty {
EASY(1, "Easy"), MEDIUM(2, "Medium"), HARD(3, "Hard");
private final int intValue;
private final String stringValue;
private Difficulty(int intValue, String stringValue) {
this.intValue = intValue;
this.stringValue = stringValue;
}
#Override
public String toString() {
return stringValue;
}
};
public ChessSplash() {
super("Chess Game");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
difficultySetting = new JComboBox<>(Difficulty.values());
JButton startButton = new JButton("Start Game");
startButton.addActionListener(new DifficultyListener());
JPanel mainPanel = new JPanel();
add(mainPanel);
mainPanel.add(difficultySetting);
mainPanel.add(startButton);
pack();
}
private class DifficultyListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
//Declare AI
SimpleAiPlayerHandler ai;
//Declare and Instantiate Chess Game
ChessGame chessGame = new ChessGame();
//Human Player is the Object chessGui
ChessGui chessGui = new ChessGui(chessGame);
//Assign Human Player to White
chessGame.setPlayer(Piece.COLOR_WHITE, chessGui);
//Get the selected difficulty setting
Difficulty difficulty = (Difficulty)difficultySetting.getSelectedItem();
//Instantiate Computer AI pass it the maxDepth for use in the constructor
ai = new SimpleAiPlayerHandler(difficulty.intValue, chessGame);
//Assign Computer Player to Black
chessGame.setPlayer(Piece.COLOR_BLACK, ai);
//Demonstrate the enum combobox works
System.out.println(difficulty.intValue);
//Dispose of the splash JFrame
ChessSplash.this.dispose();
//Start your game thread (I would probably do something to move this
//onto the EDT if you're doing this is swing personally
new Thread(chessGame).start();
}
}
}
ok i have two classes similar like this(the graphics are set up the same way) and another class that is displayed on the bottom. as you can see i have two graphics2ds that i would like to display at the same time with the items class being transparent and on top (the items class has almost nothing in it, and the game class is fully covered with pictures and such)
is there any way to do this?
currently the items class take priority ever the game class because it was called last and totally blocks the game class.
public class game extends Canvas implements Runnable
{
public game()
{
//stuff here
setBackground(Color.white);
setVisible(true);
new Thread(this).start();
addKeyListener(this);
}
public void update(Graphics window)
{
paint(window);
}
public void paint(Graphics window)
{
Graphics2D twoDGraph = (Graphics2D)window;
if(back==null)
back = (BufferedImage)(createImage(getWidth(),getHeight()));
Graphics graphToBack = back.createGraphics();
//draw stuff here
twoDGraph.drawImage(back, null, 0, 0);
}
public void run()
{
try
{
while(true)
{
Thread.currentThread();
Thread.sleep(8);
repaint();
}
}catch(Exception e)
{
}
}
}
class two
public class secondary extends JFrame
{
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
public secondary()
{
super("Test RPG");
setSize(WIDTH,HEIGHT);
game game = new game();
items items = new items();
((Component)game).setFocusable(true);
((Component)items).setFocusable(true);
getContentPane().add(game);
getContentPane().add(items);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main( String args[] )
{
secondary run = new secondary();
}
}
Here are my suggestions:
Extend JComponent rather than Canvas (you probably want a lightweight Swing component rather than a heavyweight AWT one)
Then don't bother with the manual back-buffering for your drawing - Swing does back-buffering for you automatically (and will probably use hardware acceleration while doing so)
Have one component draw both items and the rest of the game background. There is no good reason to do it separately (even if you only change the items layer, the background will need to be redrawn because of the transparency effects)
Capitalise Your ClassNames, it makes my head hurt to see lowercase class names :-)
EDIT
Typically the approach would be to have a class that represents the visible area of the game e.g. GameScreen, with a paintCompoent method as follows:
public class GameScreen extends JComponent {
....
public void paintComponent(Graphics g) {
drawBackground(g);
drawItems(g);
drawOtherStuff(g); // e.g. animated explosions etc. on top of everything else
}
}