Swing based Java game - java

this is a problem that I've been really struggling with
I need to create a game for some University coursework. The game is basically a 4x4 grid. When i click a button, the player should move randomly within 1 square of it's location.
There's enemy spaceships that are generated randomly over the grid, and if the player lands on the spaceship, it destroys it. if there's two enemy ships on the same sqaure and the player lands on that square, the player is destroyed.
I've created a GIU using gridlayout and the button that I need to move the player, but I'm having problems trying to figure out how to actually use the grid. I've created an element class to create objects for the grid (ie, a list of enemy ships), but I don't know how to use this class with my gridlayout. I know I also have to redraw the grid after each move to reflect the changes, but I don't know if I would need to create a custom redraw method for this.
Any push in the correct direction so I can get my program actually doing something would be really helpful.

Some time ago I created a card game that also operated based on a GridLayout. Below is some of the code that was used to drive major events:
CardPanel extends JPanel {
public CardPanel() {
setLayout(gridLayout);
placeCards(N);
chatBoxText = new JTextArea(5, 30);
}
public void placeCards(int numCards) {
for (int i = 0; i < numCards; i++) {
Card card = deck.distributeCard();
String cardImageName = card.getImageName();
JLabel jLabel = makeImage(cardImageName);
cardSet.add(new GraphicCard(card, jLabel));
add(jLabel);
jLabel.addMouseListener(this);
}
if (GameLogic.noSetsOnBoard(cardSet)) {
gridLayout.setRows(gridLayout.getRows() + 1);
placeCards(3);
}
}
public void updateCard(Integer location) {
Card card = deck.distributeCard();
String cardImageName = card.getImageName();
JLabel jLabel = makeImage(cardImageName);
cardSet.set(location, new GraphicCard(card, jLabel));
jLabel.addMouseListener(this);
}
}
The CardPanel here is an object that represents the GridLayout (you see the constructor) as well as some of those helper methods.
Further on, on particular actions, I have the following code:
cardPanel.removeAll();
// Some other logic here
for (GraphicCard card: cardSet) {
cardPanel.add(card.getJLabel());
}
cardPanel.revalidate();
cardPanel.repaint();
In short, yes, it is custom, but it is not difficult. There are better ways to do it than the above such that you do not need to empty the entire panel in order to refresh it, the logic I have omitted above merited this particular solution.
The approach I used kept track of a CardPanel object which was an abstraction that contained not only the information that each box represented (in my case a card with some values), but also the graphic element to be rendered.
This was some time ago, so I don't recall the exact details but hopefully this is helpful.

Related

JComboBox is refering to old Frame while removeAllItems()

I tried to figure this out myself but I can't. I'm stuck at a strange problem.
I have a Java Program with multiple classes and forms (I use Intellij and the build in GUI-Creator). When I switch from one Screen to another I just call frame.setVisible(false); at the leafing window and frame.setVisible(true); at the window I want to show next.
On a Button Click I make this:
In Class 1:
if (e.getSource() == umschaltenButton) {
this.mainW.goToMainWindow();
logger.log(Level.INFO, "Switched Back to MainMenu");
frame.setVisible(false);
}
And here is the weird part.
In Class 2:
public void goToMainWindow() {
frame = tvElectronics.drawMainWindow(); // I get a new Frame with new Images and so on
frame.addMouseListener(al);
frame.add(BotomPanel); // in here is the JComboBox
frame.setSize(LENGTH, HEIGHT);
comboBox1.removeAllItems(); // Here it tryes to refere to the old frame before i made frame = tvElectronics.drawMainWindow();
Vector<String[]> content = tvElectronics.getContent();
for (int i = 0; i < tvElectronics.getAnz(); ++i) {
comboBox1.addItem((i + 1) + ". " + content.get(i)[3]);
}
comboBox1.setSelectedIndex(chanel);
frame.setVisible(true);
}
And so it tries to update the old frame from class2 which no longer exists because of the new one I just created. And so I have 2 frames open: one as I want it and one strange old frame form class2.
My problem is that I want bind my JComboBox to a new Frame and update it but it is still connected to the old one and that causes weird problems like jumping back in the function. I mean it is at the last line of goToMainWindow() and then it starts again at the first line.
First off you should avoid swapping JFrames as your program does since this is a very annoying GUI design. Please read The Use of Multiple JFrames, Good/Bad Practice? for more on this.
Next, it's impossible for us to tell what GUI view your JComboBox is associated with.
But having said that, it really shouldn't matter. Instead of doing what you're doing, I would give the display class that holds a JCombBox a public method that you call on the containing display class that clears the contained JComboBox's model or that places items in the model. This way, there will be no ambiguity as to which JComboBox you're referring to, and this way you avoid directly exposing a view's inner components.
As an aside, I try to gear my display or view classes towards creating JPanels, not JFrames as this will give my code much greater flexibility.
For example
// my display class
class Display1 {
private DefaultComboBoxModel<String> myModel = new DefaultComboBoxModel<>();
private JComboBox<String> myCombo = new JComboBox<>(myModel);
public void removeAllComboElements() {
myModel.removeAllElements();
}
public void addElement(String ele) {
myModel.addElement(ele);
}
}
Same for your Display2 class. Then you can call the correct method on the JComboBox that is held by the correct view/display.
This way, when you swap displays, perhaps by using a CardLayout, you can clear the JComboBox in the display that is being shown by calling its own method to clear its own combobox's model.

Connect Four Java Game Project, need help on basic concepts

I am working on a lab, it's a connect four game. I'm having trouble specifically with basic concepts like how classes communicate with each other, how to use private instance variables, how to use an ArrayList, how to compare JLabels or set them as something comparable...
To give a brief breakdown I have four classes GUI, Game, Player, Name
I can create the GUI by using two four loops, the game is a grid with 7 columns of 6 pieces. The pieces are images,
JLabel Piece = new JLabel("images/blank.png");
for example to denote an empty spot.
The GUI is based on a JFrame, single content pane and four panels, one for a header which indicates who is playing and who won, another for the 7 buttons accompanying the 7 rows, the grid itself of the possible places to be played and then a button panel which gives you the option to replay.
I'm lacking in a lot of concepts. For instance, the replay button shouldn't appear until the game has ended.
I don't understand how to use an ArrayList. I tried to use
ArrayList<ArrayList<JLabel>> myList = new ArrayList<ArrayList<JLabel>>();
So when I create the GUI by running two for loops like so
For ( c = 0 ; c<8 ; c++) {
ArrayList<JLabel> column = new ArrayList<JLabel>();
For ( r = 0 ; r<7 ; r++) {
ArrayList<JLabel> row = new ArrayList<JLabel>();
JLabel empty = new JLabel("images/blank.png");
row.add(empty);
}
column.add(row);
}
Even this small step I've already got confused.
I know the two for loops above are not correct specifically the ArrayList.
I don't know how to create the arraylist and then use them.
using something like
column.get().get();
myList.get().get();
to get a specific piece.
I don't know how to pass that to an argument so that for example if I push on button 7 for column 7, and no pieces have been played yet, I can start from the lowest area column 7 row 6 and update that to a played piece, red or yellow for the appropriate player.
This is vague and I doubt I'll get anywhere but I am desperate for help. There isn't much time available from the TA's / Teacher and I believe I am lacking significantly to be able to finish this project.
I understand how it works/what I have to do in words but in terms of applying Java code...
I'd appreciate any help.
OK first off you should use an array of Enums. ArrayLists are intended for lots of items and that can have rapidly changing numbers. Arrays are intended to house data in a grid of some sorts. Since you are using a static board, use arrays! They are also much less memory-intensive. Example:
//Note you should use [column][row] as that is common practice.
States[][] grid = new States[7][6];
//And initialize it:
for(int i = 0; i < grid.length; i++)
for(int o = 0; o < grid[i].length; o++)
grid[i][o] = EMPTY_JLABEL;
Then declare an enum (this is a new class) (NOTE: replace FULL_PLAYER_ONE_JLABEL and FULL_PLAYER_TWO_JLABEL with the JLabels that have the image for each.):
public enum States {
FULL_PLAYER_ONE(FULL_PLAYER_ONE_JLABEL), FULL_PLAYER_TWO(FULL_PLAYER_TWO_JLABEL), EMPTY(EMPTY_JLABEL);
//The image of the appropriate state.
private JLabel label;
//Enum constructors must be private
private States(JLabel label) {
this.label = label;
}
public JLabel getLabel() {
return label;
}
}
In your GUI, have a JButton that is only added to the frame when the game is over. Also add a button to indicate when each column has been clicked by the player.
JButton button = new JButton();
//Initialize JButton and add to frame...
//Anytime before the frame is set to visible:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
/* Perform tests for for what should happen.
For example test whose turn it is then call a method to add a piece to that column. Then call a checker to see if someone has won. If so, display the replay button, otherwise do nothing (allow other player to move).
*/
}
}

Comparing components via setName() .

I am coding an image puzzle game and one part of the code is to compare the pieces the user has selected to the pieces of the correct image.
Each image piece is already added to a JButton as an ImageIcon.
An identifier is required to differentiate each image piece apart and also for comparision.
I am setting a setName() for each JButton created as the identifier.
The comparison starts when the user releases the mouse after he drags the puzzle pieces from the original 3x3 grid where the shuffled pieces are to the other 3x grid for matching.
I have problems removing the error from the comparison if statement.
I got the comparison idea from this SO thread - link
private JButton[] button = new JButton[9];
private JButton[] waa = new JButton[9];
private String id;
private int cc;
private String id2;
private int cc2;
// setName for each of the 9 buttons in the original 3x3 grid being created
// which stores the shuffled puzzle pieces
for(int a=0; a<9; a++){
button[a] = new JButton(new ImageIcon());
id += Integer.toString(++cc);
button[a].setName(id);
}
// setName for each of the 9 buttons in the other 3x3 grid
// where the images will be dragged to by the user
for(int b=0; b<9; b++){
waa[b] = new JButton();
id2 += Integer.toString(++cc2);
waa[b].setName(id2);
}
// check if puzzle pieces are matched in the correct place
// compare name of original 'button' array button with the name of 'waa' array buttons
button[a].addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent m){
if(m.getbutton().getName().equals (waa.getName())){
}
else{
JOptionPane.showMessageDialog(null,"Wrong! Try Again.");
}
}
}
In your mouseReleased event m.getButton() is returning the mouse button that was clicked. You'll want to do something more like this that will get you closer:
if (m.getComponent().getName().equals(waa.getName())) {
m.getComponent() returns the Component object (your JButton) that the event was fired from. From there you can do the comparison with the getName approach you are using.
There's an additional issue in that your waa variable is an array. I'm not sure how you want to compare them, whether running through the arrays and making sure the index and names match, but that's an additional issue you need to look into.
JButton uses an ActionListener to trigger notifications back to your program to indicate when it's been triggered. This allows the button to respond to different types of event, including the mouse, keyboard and program triggers.
As apart of the action API, you can supply an action command for each button. See JButton#setActionCommand
Basically you would integrate it in a simular way to your mouse listener...
public void actio Performed(ActionEvent evt) {
if (command.equals(evt.getActionCommand()) {...}
}
Depending on your requirements, it might even be easier to use the Action API
The problem you are actually having is waa is an array, therefore, it doesn't have a getName method. I'm also unclear as to why you have two arrays of buttons?

Java Swing: Swing Worker, invokeAndWait and Overlapping JLabels

Hey guys I've come with 2 problems, both being Java Swing related. I am developing a card game in Java. I use arreays lists of type to hold the values of each card and I have a main Play() method that calls updates to the GUI using invokeLater and a Singleton approach to my GUI class.
The first question is fairly simple. I create the cards on the GUI using JLabels like so; attaching relevent listeners and adding them to the appropriate panel, in this case, the 'Human Hand Panel':
for (int i = 0; i < (HumanHand.size()); i++)
{
Card card = HumanHand.get(i);
BufferedImage cardImage = null;
try {
cardImage = ImageIO.read(new File("card/" + card + ".jpg"));
} catch (IOException e) {
e.printStackTrace();
}
JLabel picLabel = new JLabel(new ImageIcon( cardImage ));
picLabel.addMouseListener((MouseListener) me);
HumanHandDisplay.add(picLabel);
}
//HumanHandDisplay.validate();
HumanHandDisplay.updateUI();
The problem I'm having is that when the human hand is more than 7 cards or so, it creates a larger panel area and starts a new row of cards beneath. What I would like to do is, when the hand reaches a certain size, the cards start to overlap eachother (like you would hold cards in your hand). I've messed about with .validate() but gotten nowhere. Any help you can give here would be most welcome.
My second question is about using a Swing Worker to return the Human player's card selection. I have read a little bit about Swing workers but I'm unsure as to the best way to implement one in my own game. At present, using the console I have a scanner than takes the input of an int as the choice (the place of the specific card in the ArrayList). I would like this int to be selected by clicking on the card in the players hand. At the moment I use:
String name = "" + i;
picLabel.setName(name);
to set the names of the Card JLabels to the int in the for loop creating them (as shown^^), and I use:
public void mouseClicked(MouseEvent e) {
String name = ((JLabel)e.getSource()).getName();
System.out.println("Working " + name);
selection = Integer.parseInt(name);
}
To return that int when one of the cards is clicked. Here is what I've been using to call the GUI methods from Play() aswell:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUI.getInstance().UpdateHand();
}
});
My question is, how can I get the Play method to call a method in the GUI class, which sets up the hand with the appropriate listeners (lets call it GUIPlayerSelection() for now) then wait for the player to click a card, which then returns the int to my Play() method in my main class that represents the card selected. I'm unsure as how to use invoke and wait, as in, if I use invoke and wait, will it just wait for the cards to be set up, or will it wait for the mouseClicked() method to finish aswell? Or will I have to do something else to make sure it waits for the mouse to be clicked after the hand set-up? And how then will I return that int? I've been told to use a swing worker but if someone could explain how I can implement that in this case that would be awesome.
Thank-you in advance.
For your first question, are you having an issue getting the frame to repaint or with the layout of the cards?
If the latter, then you may want to have a look at Java Layout Managers, it describes how the content pane inside your JFrame organizes the components added to it.
If none of those will work for you (and I don't think they will with that you describe), you can use the setBounds method to manually align your JLabels.

(GridWorld) World's setGrid() not repainting properly?

I'm trying to use GridWorld (from the AP computer science curriculum) for making a game, and I'm having problems with using multiple grids. World's setGrid method doesn't seem to work. I was under the impression that you could have multiple grid objects co-existing, and that the current one pointed to by the World is the one that gets drawn in the GUI. But that's not what happens... when I call the World's setGrid and pass it a grid, the grid seems to only LOGICALLY be set, and System.out.printing it gives the correct results of its actors and their current positions, but the GUI doesn't update and you can't actually see the grid.
I wrote a simple ActorWorld to illustrate this:
public static void main(String[] args) throws Exception
{
ActorWorld x = new ActorWorld()
{
Grid<Actor> gr1 = new BoundedGrid<Actor>(10,10);
Grid<Actor> gr2 = new BoundedGrid<Actor>(10,10);
public void step()
{
new Actor().putSelfInGrid(gr1, new Location(1,1));
new Actor().putSelfInGrid(gr2, new Location(9,9));
if (getGrid() == gr2)
setGrid(gr1);
else
setGrid(gr2);
System.out.println(getGrid());
}
};
x.show();
}
Every step it's supposed to change to the other grid and display it, so basically what SHOULD be happening is one Actor in the grid changing location from (1,1) to (9,9). But in fact, it just displays an empty grid (because it's using the original grid it made in the default constructor, since I didn't provide one).
What's going on? How do I get it to paint the current grid?
Okay, I found the problem. Upon e-mailing gridworld's creator, he revealed that this is a bug.
I found the source code and added the line
display.setGrid(world.getGrid());
to the beginning of WorldFrame's repaint() method. The problem was that WorldFrame itself updates its current Grid, so logically it's on the right one, but the WorldFrame's GridPanel object, display, which is actually the JPanel that the grid is drawn in, does not get told to update its grid prior to the repaint. With this, the complete method is
public void repaint()
{
display.setGrid(world.getGrid());
String message = getWorld().getMessage();
if (message == null)
message = resources.getString("message.default");
messageArea.setText(message);
messageArea.repaint();
display.repaint(); // for applet
super.repaint();
}
and all is well. :)

Categories

Resources