I'm trying to create the classic Breakout game as part of my programming assignment. I have to start moving the ball on a mouse click from the user. So I'm using a mouselistener to achieve that. The code below is just a smaller, simpler version of what I'm trying to do. But it does not move the ball in gradual steps. It just displays the ball at it's final position after the while loop is done executing.
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class BallMoveTest extends GraphicsProgram {
public void run() {
ball = new GOval(20,20);
ball.setFilled(true);
add(ball, 100, 100);
addMouseListeners();
}
public void mouseClicked(MouseEvent e) {
while (counter < 100) {
moveBall();
counter++;
pause(20);
}
}
public void moveBall(){
ball.move(2, 2);
}
// Private instance variables
private GOval ball;
private int counter = 1;
}
However this alternate code works wonderfully well, but does not allow the user to click to start the movement of the ball.
import acm.program.*;
import acm.graphics.*;
public class TestGOval extends GraphicsProgram {
public void run() {
int counter = 1;
GOval ball = new GOval(20,20);
ball.setFilled(true);
add(ball,100,100);
while (counter < 100) {
ball.move(2, 2);
counter++;
pause(20);
}
}
}
Could someone point out what I'm doing wrong here and more importantly, why the first code block not work as intended?
PS: This is my first question, and I'm a novice at programming. Go easy on me if you can. :)
It may just be that you aren't showing all of your code, but you should have a class that implements MouseListener. Just having the mouse clicked method isn't enough for java to recognise that this is your intention; there's a tutorial here that has more detail: http://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html
One conceptual solution could be to add a thread class which can access all the objects positions (at least the ball in your case). This thread must be able to refresh the canvas of your GraphicsProgram class. You can give to this thread a refresh frequency of 30Hz, making it sleep for 33ms after each refresh.
If you need more details about how to refresh your canvas you should provide us more details.
With such solution you also need to put a sleep of 33 ms into your while loop.
use this code:
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class BallMoveTest extends GraphicsProgram {
public void run() {
ball = new GOval(20,20);
ball.setFilled(true);
add(ball, 100, 100);
addMouseListeners();
waitForClick();
animation();
}
public void animation() {
while(counter<100){
moveBall();
pause(DELAY);
}
}
public void moveBall(){
ball.move(2, 2);
}
// Private instance variables
private GOval ball;
private int counter = 1;
private int DELAY=20;
}
Related
I'm kind of new to java.im trying to write a simple game which it has a player and enemies and when you press some keys like "space", the player most shoot a bullet in right direction but i don't know how to render bullet in game. Actually it doesn't appear and doesn't move in Game.
Shooting system
package com.company;
import javax.swing.*;
import java.awt.*;
public class Bullet {
GamePanel panel;//Game panel
Rectangle hitBox;//Bullet
public int x,y,width,height;
boolean Shoot;
public Bullet(int x , int y ,GamePanel panel)
{
this.panel = panel;
this.x=x;
this.y=y;
height=15;
width=15;
hitBox = new Rectangle(x,y,width,height);
}
public void set()
{
if(Shoot) // where I have problem which Bullet doesn't move or doesn't appear
move();
}
public void move()
{
x=x+10;
}
public void draw(Graphics2D gtd)
{
gtd.setColor(Color.RED);
gtd.fillRect(x,y,width,height);
}
}
Game Panel which sets Game Objects
package com.company;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
public class GamePanel extends JPanel implements ActionListener {
Player player;
Timer gameTimer;
Bullet bullet;
public GamePanel(){
player = new Player(400,300,this);
bullet = new Bullet(player.x, player.y, this);
gameTimer = new Timer();
gameTimer.schedule(new TimerTask() {
#Override
public void run() {
player.set();
bullet.move();
repaint();
}
},100,15);
}
public void paint(Graphics g){
super.paint(g);
Graphics2D gtd = (Graphics2D) g;
player.draw(gtd);
bullet.draw(gtd);
}
void keyPressed(KeyEvent e)
{
if(e.getKeyChar()=='w')
player.keyUp=true;
if(e.getKeyChar()=='a')
player.keyLeft=true;
if(e.getKeyChar()=='s')
player.keyDown=true;
if(e.getKeyChar()=='d')
player.keyRight=true;
if(e.getKeyChar()=='t')
bullet.Shoot=true;
}
void keyReleased(KeyEvent e)
{
if(e.getKeyChar()=='w')
player.keyUp=false;
if(e.getKeyChar()=='a')
player.keyLeft=false;
if(e.getKeyChar()=='s')
player.keyDown=false;
if(e.getKeyChar()=='d')
player.keyRight=false;
if(e.getKeyChar()=='t')
bullet.Shoot=false;
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
I think the problem is that you're calling bullet.move(); continuously in the Timer in your GamePanel class. Apart from that I'm not entirely sure how you're fetching keyboard input. Your GamePanel class contains the methods a KeyListener usually would utilize, but the class does not implement a KeyListener, but an ActionListener, which isn't being used in your code.
So what you should be doing is:
Verify that your keyPressed and keyReleased functions are actually being called when a keyboard button is pressed, and if they aren't, you should implement a KeyListener instead of an ActionListener in the GamePanel class
Make sure that bullet.move(); is only being called when it should be, as it's currently being called on every iteration of your timer
the problem is in the line public class keyPressed implements KeyListener{
it tells me to make it abstract but i know that is not needed. Please any advice to why this is so or how to properly make this work thanks
package GUI;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import Data.TileList;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class MainUI implements Runnable {
public JPanel gameGrid = new JPanel();
private JFrame gameBoard;
private int row = 4;
private int col = 1;
int boardSize = row * col;
public MainUI(){
}
#Override
public void run(){
gameBoard = new JFrame("BrickGame");
JButton st = new JButton("Start");
Font fontst0 = st.getFont();
Font fontst1 = fontst0.deriveFont(Font.BOLD, fontst0.getSize()*14);
st.setFont(fontst1);
gameBoard.add(gameGrid);
gameGrid.setLayout(new GridLayout(2,1));
st.addActionListener(new StartGame(this));
gameGrid.add(st);
JLabel txt = new JLabel();
txt.setText("<HTML><body><H1>Instructions</H1><p>Click the 'start' button to start the game" +
"<br>Type the letters in the bricks to make a bric dissapear. After all bricks are gone the game is over.<p></body></HTML>");
gameGrid.add(txt);
gameBoard.setVisible(true);
gameBoard.pack();
}
public void StartGame(){
gameGrid.removeAll();
gameGrid.setLayout(new GridLayout(row,col));
gameBoard.add(gameGrid);
for(int i = 0; i < boardSize; i++){
//String x = TileList.Letters[i] + "";
//JButton tile = new JButton(x);
JButton tile = new JButton(new TileList(TileList.Letters[i]).getLetter() + "");
tile.setBackground(Color.red);
gameGrid.add(tile);
}
gameBoard.setFocusable(true);
//here i am trying to addd the class of keypressed to the jframe.
gameBoard.addKeyListener(new keyPressed());
gameBoard.add(gameGrid);
gameBoard.pack();
}
}
package GUI;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
//the problem is in the implements line below it tells me to make it abstract but that is not needed.
public class keyPressed implements KeyListener{
public keyPressed(){
}
public void keyTyped(KeyEvent e) {
}
}
The compiler is telling you exactly what's wrong. You have a class implementing an interface, and you're not implementing all the interface methods. You need to fix this by adding the third method, keyReleased. The API would tell you which methods you need, and in the future you will want to have a look there when you have similar problems.
public class keyPressed implements KeyListener{
public keyPressed(){
}
public void keyTyped(KeyEvent e) {
}
}
KeyListener has three methods and you have implemented only 2 .Either implement the remaining methods or mark your class as abstract.
So if you want to use only 2 methods the you have to use keyAdapterhere:
public class adapter implements KeyAdapter{
#Override
public keyPressed(KeyEvent key){
//action...
}
#Override
public void keyTyped(KeyEvent key) {
//action
}
}
Note that:
The KeyListener is interface.
If your class claims to implement an interface, all methods defined by that interface must appear in its source code before the class will successfully compile.
I've been taking AP Computer Science this year as a sophomore in high school and we mainly cover material like loops, classes, methods, general CS logic, and some math stuff. I am missing what I really loved about coding in the first place, making games. Now every game I have made had some sort of way to manage it whether it was using timers in visual basic or a XNA plugin for c# that setup a update method for me. The problem is I have not learned how to do this for java in my course. I've read up a little on threads and implements runnable but i'm not really sure where I'm going with it.
Class 1
import java.awt.FlowLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class GFXScreen extends JFrame
{
/**
* #param screenHeigth
* #param screenHeigth
* #param The file name of the image. Make sure to include the extension type also
* #param The title at the top of the running screen
* #param The height of the screen
* #param The width of the screen
*/
public GFXScreen(String fileName, String screenTitle, int screenHeight, int screenWidth)
{
setLayout(new FlowLayout());
image1 = new ImageIcon(getClass().getResource(fileName));
label1 = new JLabel(image1);
this.add(label1);
//Set up JFrame
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
this.setTitle(screenTitle);
this.setSize(screenWidth, screenHeight);
}
/**
* #param desired amount to move picture
*/
public void updatePic(int increment)
{
//update pos
label1.setBounds(label1.bounds().x, label1.bounds().y - increment,
label1.bounds().width, label1.bounds().height);
}
private ImageIcon image1;
private JLabel label1;
}
Class 2
public class MainClass implements Runnable {
public static void main(String[] args)
{
(new Thread(new MainClass())).start();
GFXScreen gfx = new GFXScreen("pixel_man.png", "pixel_Man", 1000, 1000);
}
public void run()
{
gfx.updatePic(1);
}
}
In this instance what I want to happen is, I want a picture that starts in the top to slowly move down smoothly to the bottom. How would i do this?
Suggestions:
Again, a Swing Timer works well for simple Swing animations or simple game loops. It may not be the greatest choice for complex or rigorous tame loops as its timing is not precise.
Most game loops will not be absolutely precise with time slices
And so your game model should take this into consideration and should note absolute time slices and use that information in its physics engine or animation.
If you must use background threading, do take care that most all Swing calls are made on the Swing event thread. To do otherwise will invite pernicious infrequent and difficult to debug program-ending exceptions. For more details on this, please read Concurrency in Swing.
I avoid using null layouts, except when animating components, as this will allow my animation engine to place the component absolutely.
When posting code here for us to test, it's best to avoid code that uses local images. Either have the code use an image easily available to all as a URL or create your own image in your code (see below for a simple example).
Your compiler should be complaining to you about your using deprecated methods, such as bounds(...), and more importantly, you should heed those complaints as they're there for a reason and suggest increased risk and danger if you use them. So don't use those methods, but instead check the Java API for better substitutes.
Just my own personal pet peeve -- please indicate that you've at least read our comments. No one likes putting effort and consideration into trying to help, only to be ignored. I almost didn't post this answer because of this.
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class GfxPanel extends JPanel {
private static final int BI_WIDTH = 26;
private static final int BI_HEIGHT = BI_WIDTH;
private static final int GAP = 6;
private static final Point INITIAL_LOCATION = new Point(0, 0);
private static final int TIMER_DELAY = 40;
public static final int STEP = 1;
private ImageIcon image1;
private JLabel label1;
private Point labelLocation = INITIAL_LOCATION;
private int prefW;
private int prefH;
private Timer timer;
public GfxPanel(int width, int height) {
// the only time I use null layouts is for component animation.
setLayout(null);
this.prefW = width;
this.prefH = height;
// My program creates its image so you can run it without an image file
image1 = new ImageIcon(createMyImage());
label1 = new JLabel(image1);
label1.setSize(label1.getPreferredSize());
label1.setLocation(labelLocation);
this.add(label1);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(prefW, prefH);
}
public void startAnimation() {
if (timer != null && timer.isRunning()) {
timer.stop();
}
labelLocation = INITIAL_LOCATION;
timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
// My program creates its image so you can run it without an image file
private Image createMyImage() {
BufferedImage bi = new BufferedImage(BI_WIDTH, BI_HEIGHT,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bi.createGraphics();
g2.setColor(Color.red);
g2.fillRect(0, 0, BI_WIDTH, BI_HEIGHT);
g2.setColor(Color.blue);
int x = GAP;
int y = x;
int width = BI_WIDTH - 2 * GAP;
int height = BI_HEIGHT - 2 * GAP;
g2.fillRect(x, y, width, height);
g2.dispose();
return bi;
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
int x = labelLocation.x + STEP;
int y = labelLocation.y + STEP;
labelLocation = new Point(x, y);
label1.setLocation(labelLocation);
repaint();
if (x + BI_WIDTH > getWidth() || y + BI_HEIGHT > getHeight()) {
System.out.println("Stopping Timer");
((Timer) e.getSource()).stop();
}
}
}
private static void createAndShowGui() {
final GfxPanel gfxPanel = new GfxPanel(900, 750);
JButton button = new JButton(new AbstractAction("Animate") {
#Override
public void actionPerformed(ActionEvent arg0) {
gfxPanel.startAnimation();
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(button);
JFrame frame = new JFrame("GFXScreen");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(gfxPanel);
frame.getContentPane().add(buttonPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
What I always use is an infinite loop that calls an update method each iteration, in that method, you would do whatever was required to update the state of the game or render a GUI.
Example
public static void main(String[] args){
// Initialise game
while(true){
updateGame();
}
}
public static void updateGame(){
// Update things here.
}
What I also do ,which is a little more complex, is create and interface called IUpdateListener and have certain classes that are specialised for a certain element of the game. I would example have an InputListener, an AIListener, each handling a certain element of game updating.
public interface IUpdateListener{
public void update();
}
public class Main{
public static ArrayList<IUpdateListener> listeners = new ArrayList<IUpdateListener>();
public static void main(String[] args){
listeners.add(new InputListener());
while(true){
for(IUpdateListener listener : listeners){
listener.update();
}
}
}
}
public class InputListener implements IUpdateListener{
public void update(){
// Handle user input here etc
}
}
I'm making a game with multiple asteroids going down. The goal is to dodge as many asteroids for as long as you can. I made it so that as soon as an asteroid reaches the bottom of the screen it is send back up, now i want to count every time it gets to the bottom and add it to the score.
My problem is that all the asteroids are the same class so if I use:
if(y>700){
y=-50; //
x= (int) (Math.random()*670); // To send the asteroid back up
setLocation(x,y); //
score++; // To add up the score
System.out.println(score); // To print the score
Every asteroid adds op his own amount of times it has reached the bottom, but I want to know how many asteroids have reached the bottom in total. So I figured I have to get the score out of the asteroid class and add it up in an other class but I don't know how.
This is the code of the asteroid class:
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Astroid extends JPanel implements ActionListener
{
public int yVelocity = 1;
public int x = (int) (Math.random()*650), y = (int) (Math.random()*-1000);
public Timer timer;
private int score;
public Astroid(int x,int y)
{
this.setLocation(x, y);
this.setSize(25, 25);
this.setBackground(Color.WHITE);
this.setVisible(true);
}
{
this.timer = null;
this.timer = new Timer(10,new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0)
{
setLocation(x,(y+=yVelocity));
timer.setDelay(10);
repaint();
if(y>700){
y=-50;
x= (int) (Math.random()*670);
setLocation(x,y);
score++;
System.out.println(score);
}
}
});
timer.start();
}
}
This is the code of the class that creates the asteroids:
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SterTest extends JFrame implements ActionListener
{
public int a;
public SterTest()
{
this.setSize(700, 700);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
final JPanel p = new JPanel();
p.setBackground(Color.BLACK);
p.setLayout(null);
for(a=0;a<3;a++) {
Astroid astroid = new Astroid(1,1);
p.add(astroid);
} //Creates 3 asteroids to start with
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
Astroid astroid2 = new Astroid(1,1);
p.add(astroid2);
a++;
System.out.println("Het aantal asteroids in game is:"+a);
}
}, 5000, 5000); // creates a new asteroid every 5 seconds or so
this.add(p);
this.setVisible(true);
}
Keeping track of the score probably isn't the job of the Asteroid class, so that variable would be best kept in a more central class. Then you can add an instance of the central class to each asteroid, and call a method to increment the score. Something like this:
public class ScoreKeeper {
private AtomicInteger score;
public void incrementScore (int points) {
score.getAndAdd (points);
}
public int getScore () {
return score.get ();
}
}
In you class SterTest you would create a single instance of ScoreKeeper and pass it to each new Astroid instance you create.
public class Astroid {
private ScoreKeeper scoreKeeper;
public Astroid(int x,int y, ScoreKeeper scoreKeeper) {
//... existing code ...
this.scoreKeeper = scoreKeeper;
}
// ... when you want to increment the score, do this:
scoreKeeper.incrementScore (1);
}
Note: There are better and more OO approaches to this. This answer is that fastest route to working code from your current solution.
Declare score as static.
private static int score
This will give you a single copy of that variable.
However
I see you have some threads. In this case you're going to need a little bit of synchronization to keep score consistent.
I recommend adding
private static final Object scoreLock
to your fields. And replace score++ with
synchronized(scoreLock)
{
Asteroid.score++;
}
If you're not interested in keeping score of an individual asteroid than there is no reason to add a score variable to that class. The score should be maintained at a higher level, perhaps in your SterTest() class. When you detect an asteroid has made it to the bottom, just increment that score. I don't want to tell you how to design your program, but the most obvious way would be to just have actionPerformed return the score.
Create an AsteroidListener interface. Maybe like this:
public interface AsteroidListener {
public void impact(Asteroid asteroid);
}
Make your SterTest class implement this interface. Add a new parameter to your Asteroid constructor, for the listener.
public Asteroid(int x, int y, AsteroidListener listener) { }
When you need to call the listener to notify it that there was an impact:
listener.impact(this);
Tally up the score in your SterTest class.
Use the Java Observer/Observable pattern:
public class Score extends Observable {
private AtomicInteger counter = new AtomicInteger();
public void increment() {
setChanged();
notifyObservers(Integer.valueOf(counter.incrementAndGet()));
}
}
This class is added to Astroid :
public Astroid(Score score, int x,int y) {
// ...
#Override
public void actionPerformed(ActionEvent event) {
// ...
score.increment();
}
}
StarTest implements the Observer interface:
public class StarTest implements Observer {
// ...
#Override
public void update(Observable o, Object arg) {
Integer count = (Inter)arg;
// do something with the score
}
}
You connect Observer and Observable as follows:
StarTest starTest = new StarTest();
Score score = new Score();
score.addObserver(starTest);
Astroid astroid = new Astroid(score, x, y);
Here is my code:
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import acm.graphics.GLabel;
import acm.program.GraphicsProgram;
import javax.imageio.ImageIO;
import acm.util.MediaTools;
public class FlappyBird extends GraphicsProgram {
public Background background; //background image
public UpTube uptube; //one of the pipes
public DownTube downtube; //other pipe
public Bird bird;
//image for the bird
public static final int APPLICATION_WIDTH = 882;
public static final int APPLICATION_HEIGHT = 772;
public void run(){
addKeyListeners();
background = new Background();
add(background);
uptube = new UpTube();
add(uptube);
downtube = new DownTube();
add(downtube);
bird = new Bird();
add(bird);
public void jump(){
for(int i =0;i<5;i++){
bird.move(3,-7);
pause(100);
}
for(int i =0;i<15;i++){
bird.move(5, -4);
pause(100);
}
for(int i =0;i<15;i++){
bird.move(7,0);
pause(100);
}
for(int i =0;i<15;i++){
bird.move(5,7);
pause(100);
}
for(int i =0;i<15;i++){
bird.move(3,-7);
pause(100);
}
}
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_SPACE){
jump();
However, when I run this and I press the Space Bar, it doesn't show the bird's individual movements, it just teleports the bird to the end location after the pause(100) is over for each for statement. How do I make it so that it updates the bird's location each time I move it?
I dont know about the API you are using but I have made many graphics utilities, games and programs in java and there are some basic principles you must know; the one you seem to be having an 'issue' with is you assume the rendering is done one another thread (happening while this code is running) or will completely redraw whenever the bird moves - this is not the case in most graphics renderers, instead they simply redraw after all processing of every frame.
So, what you will need to do is either get rendering on another thread so the for loop can run while the rendering is happening at a different rate, or, implement more state-machine like code where it knows what it should do each frame (e.g move the bird every 100ms, 15 times).