I a working on a program for a simple game and we are told to add something like a start menu. I have the game working by itself to where I click run, and the game pulls up in a window, but now I need to add the start menu which I am having quite a hard time doing and don't know how to get it working properly. The game I am doing is Asteroids.
The way I have it now is that the game appears on a JFrame right from the start. Since the game appears from the start, I tried making a JButton that when clicked initializes the value of a boolean variable that determines the status of the game equal to true. Here is the code I got:
public Asteroids()
{
ast = new ArrayList<Asteroid>();
aliveAsteroids = NUM_ASTEROIDS;
bullets = new Bullet[NUM_BULLETS];
ship = new Ship();
g2d = null;
gameButton = new JButton("Start");
gameButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
gameRunning = true;
}
});
JPanel buttonPanel = new JPanel (new FlowLayout());
buttonPanel.add(gameButton);
frame = new JFrame("Asteroids");
identity = new AffineTransform();
rand = new Random();
frame.setSize(FRAMEWIDTH, FRAMEHEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(buttonPanel);
frame.add(this);
frame.setVisible(true);
}
public static void main(String [] args)
{
Asteroids game = new Asteroids();
if(gameRunning == true)
{
game.go();
}
}
public void go()
{
//set up the ship
ship.setX(FRAMEWIDTH / 2);
ship.setY(FRAMEHEIGHT / 2);
//set up the bullets
for (int n = 0; n < NUM_BULLETS; n++)
{
bullets[n] = new Bullet();
}
//set up the asteroids
for (int n = 0; n < NUM_ASTEROIDS; n++)
{
Asteroid a = new Asteroid();
a.setX((double)rand.nextInt(FRAMEWIDTH)+20);
a.setY((double)rand.nextInt(FRAMEHEIGHT)+20);
a.setMoveAngle(rand.nextInt(360));
double ang = a.getMoveAngle() - 90;
a.setVelX(calcAngleMoveX(ang));
a.setVelY(calcAngleMoveY(ang));
ast.add(a);
}
addKeyListener(this);
requestFocusInWindow();
gameloop = new Thread(this);
gameloop.start();
}
For some reason this is not working the way I want it to and I don't understand why. Some things I try give me a NullPointerException. For the record, I know what a NullPointerException is and what it means for something to be null, but I am just not sure why I am getting one in this instance. Other things I try pull up the frame but with nothing on it even and throws a bunch of exceptions even though it should have a button.
EDIT: The exceptions that appear are Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at game.Asteroids.drawBullets(Asteroids.java:176)
at game.Asteroids.paintComponent(Asteroids.java:148)
Another question I have is about the line that says frame.add(this). What exactly is this? I know this is a keyword that can be used to initialize instance variables that have the same name as a parameter, for instance this.width = width;. But when I see this by itself in this context, I have no clue what it is referring to. So that whole line right there, I have no idea what it does. All I know is that it adds something to the frame. What that something is is beyond me.
I was wondering if anyone could help explain to me what is going on and why my program is not working correctly and maybe offer some advice/tips on how to get on the right track? I would greatly appreciate any and all help.
Related
I am trying to make cardLayout work with an arbitrary amount of cards, meaning I will need some kind of a loop through all the objects I have. Now I tried and I made it work with manually created JPanels but once I put a loop in it doesn't work.
#SuppressWarnings("serial")
public class ClassCardLayoutPane extends JPanel{
JPanel cards;
public ClassCardLayoutPane() {
initialiseGUI();
}
private void initialiseGUI() {
String[] listElements = {"A2", "C3"};
cards = new JPanel(new CardLayout());
JLabel label = new JLabel("Update");
add(label);
JList selectionList = new JList(listElements);
selectionList.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent evt) {
if (!evt.getValueIsAdjusting()) {
label.setText(selectionList.getSelectedValue().toString());
CardLayout cl = (CardLayout)(cards.getLayout());
cl.show(cards, label.getText());
}
}
});
// The panels created by this loop don't work, the cards get stuck on the first one
/*
for (int i = 0; i < listElements.length-1; i ++) {
JPanel temp = new JPanel();
temp.add(new JLabel(i+""));
cards.add(temp, listElements[i]);
}*/
JPanel card1 = new JPanel();
card1.add(new JTable(20,20));
JPanel card2 = new JPanel();
card2.add(new JTable(10,20));
cards.add(card1, listElements[0]);
cards.add(card2, listElements[1]);
//the panels here do work. I don't know what I'm doing wrong
add(selectionList);
add(cards);
}
public static void main(String[] args) {
JFrame main = new JFrame("Win");
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setPreferredSize(new Dimension(1366, 768));
main.getContentPane().add(new ClassCardLayoutPane());
main.pack();
main.setVisible(true);
}
}
Okay so the commented out for loop is what doesn't work for me which has me really confused? Can someone explain to me why it doesn't work and how I could make it work? By the way, listElements can be a different size, that's what I'm trying to get working because eventually, listElements will start off as a LinkedList, so when I create the array for the ListItems, it will be a different size every time because I don't know how many items there will be. Can someone please help me make this work? By "It doesn't work", I mean when I use the loop, the JPanel gets stuck on the very first card and doesn't switch to the next card anymore! There is no error message, the program runs fine but it doesn't do what it's meant to do which is switch cards! Note that when I do them individually, the program works perfectly! Thank you.
Okay so the commented out for loop is what doesn't work for me which has me really confused?
Well the first thing you should be doing is adding debug code to the loop to see if unique card names are being used.
If you did that then you would notice the following problem:
for (int i = 0; i < listElements.length-1; i ++) {
Why are you subtracting 1 fro the list length? You only ever add one panel to the CardLayout.
The code should be:
for (int i = 0; i < listElements.length; i ++) {
When code doesn't execute as you think you need to add debug code or use a debugger to step through the code to see if the code executes as you expect.
You can't just always stare at the code.
So I have been having some trouble trying to create a teletype effect for my swing program. I essentially want to update a JFrame at 40ms increments with a new letter, "typing" out a message to the user. However, it flickers a lot when I try to do this. The method is below:
public static void animateTeletype(String input, JTextArea displayArea)
throws InterruptedException {
displayArea.setText("");
String s = "";
for(int i = 0; i<input.length(); i++) {
s += input.substring(i, i+1);
displayArea.setText(textToDisplay);
Thread.sleep(40);
displayArea.update(displayArea.getGraphics());
}
}
I figure the problem stems from updating the text too fast, and it has to update more than it can handle. I am not sure how I would go about this issue, as reducing tick time will make text scroll too slowly. Any advice is appreciated!
** I've solved the problem. This is my new code:
static Timer timer = null;
public static void animateTeletype(final String input, final JTextArea displayArea) throws InterruptedException
{
final String[] s = new String[1];
s[0] = " ";
final int[] i = new int[1];
i[0] = 0;
displayArea.setText("");
timer = new Timer(30, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
s[0] = input.substring(i[0], i[0]+1);
i[0]++;
displayArea.append(s[0]);
if(displayArea.getText().equals(input))
timer.stop();
}
});
timer.start();
}
displayArea.update(displayArea.getGraphics());
Don't use the update() method. There is never any reason to do that. Get rid of that statement.
Swing components will automatically repaint themselves.
displayArea.setText(textToDisplay);
Don't uset setText(...) to add new text.
Instead you should be using:
displayArea.append( "some more text" );
Don't use Thread.sleep(40) for animation. It you want animation then use a Swing Timer to schedule the animation.
I suggest you look at other section of the tutorial for Swing basics. Maybe something like How to Use Text Fields.
I have two more classes that this main class is using, but I think that it can be answered without those classes because I think it is a logic problem.
I'm trying to create a JFrame that prints out a drawing. The way the API is set up, I want public Viewer to create the frame and set the title and then main to instantiate a viewer. But the way I have this set up keeps printing double the amount of frames that I need. Also, when I try to concatenate
v.setTitle(v.getTitle() + String.format(" pi = %.4f", pi));
it doesn't work. It just prints a new JFrame with just
String.format(" pi = %.4f", pi));
and the original JFrame prints its name (two separate frames). So I know it has to be a logic problem somewhere, but I can't figure it out.
import java.util.Scanner;
import javax.swing.JFrame;
public class Viewer extends JFrame{
private ControlPanel cp;
public Viewer(String name, int in){
JFrame frame = new JFrame();
frame.setSize(550, 550);
frame.setTitle("Name: " + name);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cp = new ControlPanel(in);
frame.add(cp);
frame.setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
int n = (int) 1e4;
System.out.println("Enter the number of runs to make <1 to 4>:");
#SuppressWarnings("resource")
Scanner in = new Scanner(System.in);
int runs = in.nextInt();
if (runs > 4){
runs = 4;
}
if (runs < 1){
runs = 4;
}
for (int i = 1; i <= runs; i ++){
n = n * runs;
Viewer v = new Viewer("Trey Wilson", n);
int hits = v.cp.getHits();
double pi = 4.0 * hits / n;
v.setTitle(v.getTitle() + String.format(" pi = %.4f", pi));
v.pack();
v.setVisible(true);
v.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
}
Your code has multiple errors:
It extends JFrame and creates a JFrame object (just remove the extends JFrame from the class, it's recommended to do so)
Here lies your problem:
Viewer v = new Viewer("Trey Wilson", n);
this calls the Viewer's class constructor but the constructor itself sets it's own title, size, etc, and then you're modifying those attributes and making another call to setVisible(true) which makes this last frame visible again, so remove one or the other.
You're not placing your program on the EDT, see SwingUtilities.invokeLater() why is it needed?
See The use of multiple JFrames, Good / Bad Practice? (Bad) you should instead want to try using a single JFrame with multiple JPanels switching them with a Card Layout or using JDialogs to display / retrieve information to / from user
From your comment above:
I'm completely new to Jframe (along with java) and was given an API to follow along with. What does extends mean? I'll give it a google, but didn't know if you had an answer on how to fix it.
I recommend you to learn the OOP basics, Java concepts first before going into Java Swing which will make your learning harder because it will add more complexity to it and make it harder to you to maintain or make a quality software.
THIS SOLVES THE PROBLEM OF DOUBLE FRAME APPEARING:
So you can either take away frame.setVisible(true); in the constructor as in:
public Viewer(String name, int in){
JFrame frame = new JFrame();
frame.setSize(550, 550);
frame.setTitle("Name: " + name);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cp = new ControlPanel(in);
frame.add(cp);
}
OR Rather take away v.setVisible(true); from you main method.
I will advice you to take away frame.setVisible(true);. But depending on you requirement, choose the best for yourself.
I'm currently working on a simple game in java, representative of the DOS game Gorillas.bas. I'm creating an arraylist to store the individual buildings do to collision checking and whatnot, but Eclipse is giving me an error no matter how I go about it. Here is what i've got for the arraylist.
ArrayList<Rectangle> Buildings = new ArrayList<Rectangle>();
Point build1 = new Point(75,30);
Rectangle building1 = new Rectangle(build1, BUILDING_WIDTH, 150);
Buildings.add(Rectangle building1);
The error is on the .add() method, it tells me that the method needs a body instead of a semicolon. What does this mean? Is eclipse not recognizing the .add()?
EDIT: A bit of the code around it as requested; doesn't appear to have any syntax errors.
public double bananaX = 85;
public double bananaY = 292;
public double bananaDX = 1;
public double bananaDY = 1;
public double power = 0;
public double direction = 0;
public double rise;
public double run;
Point start = new Point(0,0);
Point finish = new Point(0,0);`
ArrayList<Rectangle> buildings = new ArrayList<Rectangle>();
Point build1 = new Point(75,350);
Point build2 = new Point(225, 250);
Point build3 = new Point(325, 200);
Point build4 = new Point(425, 200);
Point build5 = new Point(525, 250);
Point build6 = new Point(675, 350);
Rectangle building1 = new Rectangle(build1, BUILDING_WIDTH, 150);
buildings.add(building1);
public void power(Point start, Point finish)
{
int power = 0;
power = (int)start.distanceTo(finish);
}
public void direction(Point start, Point finish)
{
double direction = 0;
rise = (finish.y - start.y)*-1;
run = (finish.x - start.x)*-1;
direction = rise/run;
bananaDX = run/10;
bananaDY = (rise/10);
System.out.printf("rise = %f\nrun = %f\ndirection = %f\n\n ",rise, run, direction);
}
You just need to have:
Buildings.add(building1);
Since building1 is already a Rectangle. You have already created the Rectangle object above it so you only need to use the variable itself because it is of the correct type.
Edit: You should probably also rename Buildings buildings to avoid any confusion. When you name a variable with a capital letter it looks like a type and not a variable.
Edit2: Based on the code you provided, you need to have buildings.add(building1); inside of a method of some sort. You should create an initialize method that gets called at the start if you want to have it added in at the beginning.
Don't double up on Rectangle.
Buildings.add(building1);
This is a continuation from my last post Java: Animated Sprites on GridLayout. Thanks to a reply, it gave me an idea in where I just had to insert a loop in the trigger condition and call pi[i].repaint() in it. So far it works. Though I tried to integrate it to my game which composed of multiple sprites, it had no improvement in it. Without the animation, the sprites show on the grid with no problems. I inserted the animation loop in the GridFile class and it didn't show. I also tried to insert the animation loop in the MainFile, it showed irregular animations, kinda like a glitch. Can someone tell me where did I went wrong? Ideas are welcome.
MainFile class
public class MainFile {
JFrame mainWindow = new JFrame();
public JPanel gridPanel;
public MainFile() {
gridPanel= new GridFile();
mainWindow.add(gridPanel,BorderLayout.CENTER);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setSize(700,700);
mainWindow.setResizable(false);
mainWindow.setVisible(true);
}
public static void main(String[]args){
new MainFile();
}
}
GridFile class
public class GridFile extends JPanel{
ImageIcon gameBackground = new ImageIcon(getClass().getResource("Assets\\GridBackground.png"));
Image gameImage;
int[] pkmArray = new int[12];
int random = 0;
Pokemon[] pkm = new Pokemon[36];
JPanel[] pokeball = new JPanel[36];
int j = 0;
public GridFile(){
setLayout(new GridLayout(6,6,6,6));
setBorder(BorderFactory.createEmptyBorder(12,12,12,12));
gameImage = gameBackground.getImage();
for(int i = 0;i < 36;i++){
do{
random = (int)(Math.random() * 12 + 0);
if(pkmArray[random] <= 3){
pokeball[i] = new Pokemon(random);
pokeball[i].setOpaque(false);
pokeball[i].setLayout(new BorderLayout());
pkmArray[random]++;
}
}while(pkmArray[random] >= 4);
add(pokeball[i],BorderLayout.CENTER);
}
while(true){
for(int i = 0; i < 36; i++){
pokeball[i].repaint();
}
}
}
public void paintComponent(Graphics g){
if(gameImage != null){
g.drawImage(gameImage,0,0,getWidth(),getHeight(),this);
}
}
}
Use a swing Timer for the repainting, and give a bit time between the frames for swing to do the painting work. There's no point trying to draw faster than what could be displayed anyway. If you have the animation loop in main(), the repaint manager will try to drop some of the repaint requests that appear close to each other, which can be the cause of the irregular animation you see.
You should create and access swing components only in the event dispatch thread. You current approach is breaking the threading rules.
Addition: When the animation loop is where you have it now, the GridFile constructor never returns, which explains that you'll see nothing because the code never gets far enough to show the window.