How to make smooth animations in java swing - java

Hi I'm using the Timer class and the repaint method for the JPanel, but whenever I hit DOWN and the space ship moves halfway down the screen, it shrinks and stays in place. It never reaches the bottom of the window. It is the same when I move it to the RIGHT.
Also when I try to place it in a differnt y position, the background isn't black anymore. Can someone help me with this please? Here is a sample of my code:
package javapaint;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class SpaceShipFlight
{
private JFrame windowFrame;
private BufferedImage shipSprite;
private BufferedImage spaceBackground;
/** MAIN METHOD **/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
SpaceShipFlight window = new SpaceShipFlight();
window.windowFrame.setVisible(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
/** CONSTRUCTOR **/
public SpaceShipFlight() throws IOException
{
/*************************************************************************
SPACE SHIP FLIGHT JFRAME
*************************************************************************/
windowFrame = new JFrame("Space Ship Flight");
windowFrame.setBounds(0, 0, 950, 700);
windowFrame.setBackground(Color.BLACK);
/*************************************************************************
SPACE SHIP FLIGHT SPRITES (FOR DRAWING)
*************************************************************************/
spaceBackground = ImageIO.read(new File("Space (Medium).png"));
// 450 x 374
shipSprite = ImageIO.read(new File("Ship Sprite.png"));
// 64 x 64 png of space ship
/*************************************************************************
SPACE SHIP FLIGHT JPANEL (FOR DRAWING)
*************************************************************************/
SpacePanel spacePanel = new SpacePanel();
windowFrame.add(spacePanel);
/*************************************************************************
SPACE SHIP FLIGHT TIMER
*************************************************************************/
Timer timer = new Timer(10, new AnimationListener(spacePanel));
timer.start();
/*************************************************************************
EVENT HANDLER TO MOVE SPACE SHIP
*************************************************************************/
/*
EventHandler <KeyEvent> moveShip = new EventHandler<KeyEvent> ()
{
#Override
public void handle(KeyEvent event)
{
if (event.getCharacter().equalsIgnoreCase("X"))
{
spacePanel.setX( spacePanel.getX() + 1 );
}
}
};
*/
KeyListener moveShip = new KeyListener()
{
#Override
public void keyTyped(KeyEvent e)
{
}
#Override
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_RIGHT && spacePanel.getX() < 375)
{
spacePanel.setX( spacePanel.getX() + 5 );
spacePanel.repaint();
}
if (e.getKeyCode() == KeyEvent.VK_LEFT && spacePanel.getX() > 0)
{
spacePanel.setX( spacePanel.getX() - 5);
spacePanel.repaint();
}
if (e.getKeyCode() == KeyEvent.VK_UP && spacePanel.getY() > 0)
{
spacePanel.setY( spacePanel.getY() - 5);
spacePanel.repaint();
}
if (e.getKeyCode() == KeyEvent.VK_DOWN && spacePanel.getY() < 700)
{
spacePanel.setY( spacePanel.getY() + 5 );
spacePanel.repaint();
}
if (e.getKeyCode() == KeyEvent.VK_X)
{
System.out.printf("X: %d, Y: %d\n", spacePanel.getX(),
spacePanel.getY());
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
};
windowFrame.addKeyListener(moveShip);
}
/** ANIMATION LISTENER CLASS **/
public static class AnimationListener implements ActionListener
{
private SpacePanel panel;
private Graphics graphics;
public AnimationListener(SpacePanel panel)
{
this.panel = panel;
this.graphics = panel.getGraphics();
}
#Override
public void actionPerformed(ActionEvent e)
{
panel.repaint();
}
}
/** SPACE PANEL SUBCLASS **/
public class SpacePanel extends JPanel
{
// Variables for the space ship's position temporarily placed here
private int x;
private int y;
// Constructor
public SpacePanel()
{
super();
x = 0;
y = 0;
}
// Getters
public int getX()
{
return x;
}
public int getY()
{
return y;
}
// Setters
public void setX(int newX)
{
x = newX;
}
public void setY(int newY)
{
y = newY;
}
#Override
public void paint(Graphics g)
{
g.drawImage(spaceBackground, 0, 0, this);
g.drawImage(shipSprite, x, y, this);
}
}
}

Related

KeyListener wont change variable

I have been working on this snake project, and dont really understand why the keylistener isnt actually changing the variable char key. I have some other examples of keylisteners, and they all work properly, but for some reason mine isnt working. Some help would be appreciated. Thanks a lot for the help.
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.event.KeyListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.RepaintManager;
public class Main extends JPanel implements Runnable {
/*
*
* SIZE OF BOARD
*
*/
static GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
static GraphicsDevice[] gs = ge.getScreenDevices();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
private static final int DIM_WIDTH = Toolkit.getDefaultToolkit().getScreenSize().width;
private static final int DIM_HEIGHT = Toolkit.getDefaultToolkit().getScreenSize().height;
static JFrame frame = new JFrame();
static JPanel panel = new JPanel();
static Snake s = new Snake();
static Main main = new Main();
KeyListener listener = new Snake();
boolean black = true;
public Main() {
addKeyListener(listener);
}
#SuppressWarnings("deprecation")
public static void main(String[] args) {
//gs[0].setFullScreenWindow(frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setCursor(Cursor.CROSSHAIR_CURSOR);
frame.setSize(DIM_WIDTH, DIM_HEIGHT);
frame.add(main);
frame.setVisible(true);
(new Thread(new Main())).start();
}
// paints the panel
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
/*
* Snake
*/
//Redraws Background
g2d.setColor(Color.black);
g2d.fillRect(0, 0, (int) screenSize.getWidth(), (int) screenSize.getHeight());
//Draws Border
g2d.setColor(Color.white);
g2d.fillRect(0,0, (int)screenSize.getWidth(), 1);
g2d.fillRect(0,0, 1, (int)screenSize.getHeight());
g2d.fillRect((int)screenSize.getWidth()-1, 1, 1, (int)screenSize.getHeight());
g2d.fillRect(0, (int)screenSize.getHeight()-86, (int)screenSize.getWidth(), 10);
//Draws Snake head
g2d.setColor(s.getColor());
g2d.fillRect(s.getX(), s.getY(), 30, 30);
}
// Creates Frame, and starts the game
#Override
public void run() {
while (!s.getIsDead()) {
move();
}
}
public void move() {
s.move();
s.death();
main.repaint();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Thread.interrupted()) {
return;
}
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Snake implements KeyListener {
Color c = Color.green;
//Starting position of Snake
int x = 50;
int y = 50;
char key;
boolean dead = false;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public void move() {
x++;
}
public void death() {
if (x + 30 >= screenSize.getWidth() || y + 115 >= screenSize.getHeight() || y<=0 || x<=0) {
c = Color.red;
dead = true;
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColor() {
return c;
}
public boolean getIsDead() {
return dead;
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
key = 'w';
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Change your constructor to
public Main() {
addKeyListener(listener);
setFocusable(true);
requestFocus();
}
But take a look at this question, you should not use KeyListeners.
java keylistener not called
As #azurefrog mentioned, your keyPressed method is setting key to 'w' every time. You need to use the KeyEvent passed in as a parameter to that method to get the key that was pressed. Your keyPressed method should look something like this:
#Override
public void keyPressed(KeyEvent e) {
key = e.getKeyChar();
}

Not seeing rectangle Player class in JFrame

I'm new to java and trying yo create a basic game and right now and trying to program the player class. However only the JFRame appears when i run the game. These are my thress classes and they show no errors.
import java.awt.Color;
import javax.swing.JFrame;
public class Game extends JFrame {
public
final static int WIDTH = 700, HEIGHT = 450;
public
GamePanel panel;
public Game() {
setSize(WIDTH, HEIGHT);
setTitle("Game");
setBackground(Color.WHITE);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
panel = new GamePanel(this);
add(panel);
}
public GamePanel getPanel() {
return panel;
}
public static void main(String[] args) {
new Game();
}
}
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements ActionListener, KeyListener {
public
Game game;
public
Player player;
public GamePanel(Game game) {
setBackground(Color.GREEN);
this.game = game;
player = new Player(game, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, game.getWidth() - 36);
addKeyListener(this);
setFocusable(true);
}
public
void update() {
player.update();
}
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
public Player getPlayer(int playerNo) {
return player;
}
public void keyPressed(KeyEvent e) {
player.pressed(e.getKeyCode());
}
public void keyReleased(KeyEvent e) {
player.released(e.getKeyCode());
}
public void keyTyped(KeyEvent e) {
;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
player.paint(g);
}
}
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Color;
public class Player {
public
static final int WIDTH = 50, HEIGHT = 50;
public
Game game;
public
int left, right;
public
int y;
public
int x, xa;
public Player(Game game, int left, int right, int x) {
this.game = game;
this.x = x;
y = game.getHeight() - 20;
this.left = left;
this.right = right;
this.y = y;
x = game.getWidth() - 36;
}
public void update() {
if (x > 0 && x < game.getWidth() - WIDTH - 36)
x += xa;
else if (x == 0)
x++;
else if (x == game.getWidth() - WIDTH - 36)
x--;
}
public void pressed(int keyCode) {
if (keyCode == left)
xa = -1;
else if (keyCode == right)
xa = 1;
}
public void released(int keyCode) {
if (keyCode == left || keyCode == right)
xa = 0;
}
public Rectangle getBounds() {
return new Rectangle(x, y, WIDTH, HEIGHT);
}
public void paint(Graphics g) {
g.fillRect(x, y, WIDTH, HEIGHT);
g.setColor(Color.ORANGE);
}
}
Take setVisible(true); and move it it to the last of the Game constructor. Also you should make sure you're working within the context of the EDT when building your UI's, see Initial Threads for more details
import java.awt.Color;
import java.awt.EventQueue;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Game extends JFrame {
public final static int WIDTH = 700, HEIGHT = 450;
public GamePanel panel;
public Game() {
setTitle("Game");
setBackground(Color.WHITE);
setDefaultCloseOperation(EXIT_ON_CLOSE);
panel = new GamePanel(this);
add(panel);
setResizable(false);
setSize(WIDTH, HEIGHT);
setVisible(true);
}
public GamePanel getPanel() {
return panel;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
new Game();
}
});
}
}
Basically, Swing's layout management is lazy, it won't try to update the container hierarchy until you tell it (revalidate) or it becomes realised or resized, this is a good thing, as the operation can be expensive.
Next, take a look at player = new Player(game, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, game.getWidth() - 36);
You're setting the position of the player to be 36 pixels less than the components width, but when this is called, the width will be 0.
Basically, you need to allow the UI to "settle" before making this types of calls. This is actually not that easy. You could use a ComponentListener and monitor for the componentResized event, but a window can be resized a number of times when it's first initialized, to this end, you need to "wait" until the size is "settled", for example...
public Game() {
addComponentListener(new ComponentAdapter() {
private boolean initalised = false;
private Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
initalised = true;
panel.init();
timer.stop();
}
});
#Override
public void componentResized(ComponentEvent e) {
if (!initalised) {
timer.restart();
}
}
});
Then you would need to update GamePanel to provide an init method...
public class GamePanel extends JPanel implements ActionListener, KeyListener {
public Game game;
public Player player;
public GamePanel(Game game) {
setBackground(Color.GREEN);
this.game = game;
addKeyListener(this);
setFocusable(true);
}
public void init() {
player = new Player(game, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, game.getWidth() - 36);
repaint();
}
//...
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (player != null) {
player.paint(g);
}
}
}
You have a wide range of "magic" numbers, which don't equate to what you're actually trying to do, for example...
player = new Player(game, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, game.getWidth() - 36);
shouldn't that be game.getWidth() - Player.WIDTH?
And then in Player...
public Player(Game game, int left, int right, int x) {
this.game = game;
this.x = x;
y = game.getHeight() - 20;
//...
this.y = y;
x = game.getWidth() - 36;
}
You assign the parameter x to the field x, but then change it at the end of the constructor? Also, this.y = y makes no sense!?
Now, let's talk about what a bad idea setSize(WIDTH, HEIGHT);, which relates to why your player doesn't appear where you want it to...
Windows have decorations, so your viewable area will ALWAYS be smaller than the actual window size, what's worse, the size of the decorations are variable. Instead, you should use JFrame#pack to pack the window around it's contents and have the contents provide hints about big it wants to be.
Have a look at How can I set in the midst? for more details.
Instead of setting the size of the frame, you should be using pack...
public class Game extends JFrame {
public GamePanel panel;
public Game() {
addComponentListener(new ComponentAdapter() {
private boolean initalised = false;
private Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
initalised = true;
panel.init();
timer.stop();
}
});
#Override
public void componentResized(ComponentEvent e) {
if (!initalised) {
timer.restart();
}
}
});
setTitle("Game");
setBackground(Color.WHITE);
setDefaultCloseOperation(EXIT_ON_CLOSE);
panel = new GamePanel(this);
add(panel);
setResizable(false);
pack();
setVisible(true);
}
And allowing GamePanel to make decisions about how big it wants to be...
public static class GamePanel extends JPanel implements ActionListener, KeyListener {
public final static int WIDTH = 700, HEIGHT = 450;
public Game game;
public Player player;
public GamePanel(Game game) {
setBackground(Color.GREEN);
this.game = game;
addKeyListener(this);
setFocusable(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
This also means that instead of passing Game to your objects, you should be passing GamePanel to them and using its dimensions, for example...
public static class Player {
//...
public Player(GamePanel game, int left, int right, int x) {
this.game = game;
this.left = left;
this.right = right;
this.x = x;
y = game.getHeight() - HEIGHT;
}
You should also not be using KeyListener and instead should be using the Key Bindings API instead, which will solve the focus related issue and make it easier to configure the key strokes
Basically, you're missing some core understandings about how the Swing framework works, which is causing no end of issues, I'd take the time to learn more about how the framework works generally before diving into something as complex as game development
Runnable example...
Now, because I've basically butchered your code to get it to work...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Game extends JFrame {
public GamePanel panel;
public Game() {
addComponentListener(new ComponentAdapter() {
private boolean initalised = false;
private Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
initalised = true;
panel.init();
timer.stop();
}
});
#Override
public void componentResized(ComponentEvent e) {
if (!initalised) {
timer.restart();
}
}
});
setTitle("Game");
setBackground(Color.WHITE);
setDefaultCloseOperation(EXIT_ON_CLOSE);
panel = new GamePanel(this);
add(panel);
setResizable(false);
pack();
setVisible(true);
}
public GamePanel getPanel() {
return panel;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
new Game();
}
});
}
public static class GamePanel extends JPanel implements ActionListener, KeyListener {
public final static int WIDTH = 700, HEIGHT = 450;
public Game game;
public Player player;
public GamePanel(Game game) {
setBackground(Color.GREEN);
this.game = game;
addKeyListener(this);
setFocusable(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
public void init() {
System.out.println("!!");
player = new Player(this, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, getWidth() - 50);
repaint();
}
public void update() {
player.update();
}
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
public Player getPlayer(int playerNo) {
return player;
}
public void keyPressed(KeyEvent e) {
player.pressed(e.getKeyCode());
}
public void keyReleased(KeyEvent e) {
player.released(e.getKeyCode());
}
public void keyTyped(KeyEvent e) {
;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (player != null) {
player.paint(g);
}
}
}
public static class Player {
public static final int WIDTH = 50, HEIGHT = 50;
public GamePanel game;
public int left, right;
public int y;
public int x, xa;
public Player(GamePanel game, int left, int right, int x) {
this.game = game;
this.left = left;
this.right = right;
this.x = x;
y = game.getHeight() - HEIGHT;
}
public void update() {
if (x > 0 && x < game.getWidth() - WIDTH - 36) {
x += xa;
} else if (x == 0) {
x++;
} else if (x == game.getWidth() - WIDTH - 36) {
x--;
}
}
public void pressed(int keyCode) {
if (keyCode == left) {
xa = -1;
} else if (keyCode == right) {
xa = 1;
}
}
public void released(int keyCode) {
if (keyCode == left || keyCode == right) {
xa = 0;
}
}
public Rectangle getBounds() {
return new Rectangle(x, y, WIDTH, HEIGHT);
}
public void paint(Graphics g) {
System.out.println(x + "x" + y);
g.fillRect(x, y, WIDTH, HEIGHT);
g.setColor(Color.ORANGE);
}
}
}

Not letting graphics out of JFrame boundaries

Although there are questions similar, I think mine is slightly different because of how I have my code set up. I have a JFrame within my main method. However, I only have JPanel in my constructor. I tried to make some of my variables static so that I could access them in the main method and say, for instance, if the x-coordinate of this graphic plus its width is greater than frame.getWidth().. but that won't work for some reason. I don't want to bombard anyone with code so I will just try to put the main information and if you need more, I'll update it.
package finalProj;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Ellipse2D;
public class nonaMaingamePractice extends JPanel implements ActionListener, KeyListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private static Ellipse2D ellipse;
static Toolkit tools = Toolkit.getDefaultToolkit();
static int screenWidth = (int)(Math.round(tools.getScreenSize().getWidth()));
static int screenHeight = (int)(Math.round(tools.getScreenSize().getHeight()));
private static Rectangle paddleRect;
JLabel text = new JLabel("cool");
Timer timeMove = new Timer(1, this);
Timer timeBall = new Timer(10, new timeBall());
private static double x = screenWidth/2, y = (screenHeight*0.8), xx = 0, yy = 0, score = 0, Ox = screenWidth/2, Oy = screenHeight/2, Oyy = 0, width = 100, height = 30;
public nonaMaingamePractice(){
setLayout(new BorderLayout());
timeBall.start();
timeMove.start();
addKeyListener(this);
setFocusable(true);
JPanel panelNorth = makePanel();
panelNorth.setBackground(Color.CYAN);
add(panelNorth, BorderLayout.NORTH);
JLabel scoreLabel = new JLabel("Score: " + score);
panelNorth.add(scoreLabel);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLUE);
paddleRect = new Rectangle((int)x, (int)y, (int)width, (int)height);
ellipse = new Ellipse2D.Double(Ox, Oy+Oyy, 50, 50);
Graphics2D graphics = (Graphics2D)g;
graphics.fill(paddleRect);
graphics.fill(ellipse);
}
#Override
public void actionPerformed(ActionEvent e) {
x = x + xx;
y = y + yy;
if(x<0){
x=0;
xx=0;
}
repaint();
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
int c = e.getKeyCode();
if(c==KeyEvent.VK_RIGHT){
xx=1;
}else if(c==KeyEvent.VK_LEFT){
xx=-1;
}
}
#Override
public void keyReleased(KeyEvent e) {
xx=0;
}
protected JPanel makePanel() {
#SuppressWarnings("serial")
JPanel pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 30);
}
};
pane.setBackground(Color.CYAN);
return pane;
}
protected class timeBall implements ActionListener{
Timer timeWhateva = new Timer(100, this);
#Override
public void actionPerformed(ActionEvent e) {
try{
System.out.println(paddleRect.getX());
if(ellipse.intersects(paddleRect)){
timeWhateva.start();
Oy+=-1;
System.out.println(ellipse.getX() + " " + ellipse.getY());
}else if(!ellipse.intersects(paddleRect)){
Oyy+=1;
}
}catch(RuntimeException NullPointerException){
System.out.println(NullPointerException.getMessage());
}
repaint();
}
}
public static void main(String[] args){
nonaMaingamePractice main = new nonaMaingamePractice();
JFrame frame = new JFrame();
frame.add(main);
frame.setVisible(true);
frame.setTitle("Project 4 game");
frame.setSize(screenWidth, screenHeight);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Okay, so there seems to a few things that are wrong.
First, don't rely on static for cross object communication, this is a really bad idea which will come back to bite you hard. Instead, pass information to the classes which need it.
Second, I'd focus on having a single Timer (or "main-loop") which is responsible for updating the current state of the game and scheduling repaints. This is the basic concept of Model-View-Controller paradigm
The first thing I'm going to do is take your code apart completely and rebuild it...
To start with, I want some kind of interface which provides information about the current state of the game and which I can pass instances of to other parts of the game in order for them to make decisions and update the state of the game...
public interface GameView {
public boolean isKeyRightPressed();
public boolean isKeyLeftPressed();
public Dimension getSize();
public void updateState();
}
This provides information about the state of the right and left keys, the size of the view and provides some basic functionality to request that the view update it's current state
Next, we need some way to model the state of the game...
import java.awt.Rectangle;
import java.awt.geom.Ellipse2D;
public interface GameModel {
public Rectangle getPaddle();
public Ellipse2D getBall();
public void ballWasMissed();
}
So, this basically maintains information about the paddle and ball and provides a means by which the "main game loop" can provide notification about the state of the game back to the model
Next, we need to the actual "main game loop" or controller. This is responsible for updating the state of the model and updating the view...
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
public class MainLoop implements ActionListener {
private GameView gameView;
private GameModel gameModel;
private int ballYDelta = 1;
public MainLoop(GameView gameView, GameModel gameModel) {
this.gameView = gameView;
this.gameModel = gameModel;
}
#Override
public void actionPerformed(ActionEvent e) {
Rectangle paddle = gameModel.getPaddle();
Ellipse2D ball = gameModel.getBall();
// Update the paddle position...
if (gameView.isKeyLeftPressed()) {
paddle.x--;
} else if (gameView.isKeyRightPressed()) {
paddle.x++;
}
// Correct for overflow...
if (paddle.x < 0) {
paddle.x = 0;
} else if (paddle.x + paddle.width > gameView.getSize().width) {
paddle.x = gameView.getSize().width - paddle.width;
}
// Update the ball position...
Rectangle bounds = ball.getBounds();
bounds.y += ballYDelta;
if (bounds.y < 0) {
bounds.y = 0;
ballYDelta *= -1;
} else if (bounds.y > gameView.getSize().height) {
// Ball is out of bounds...
// Notify the gameView so it knows what to do when the ball goes
// out of the game view's viewable, ie update the score...
// Reset ball position to just out side the top of the view...
gameModel.ballWasMissed();
bounds.y = -bounds.height;
} else if (paddle.intersects(bounds)) {
// Put the ball to the top of the paddle
bounds.y = paddle.y - bounds.height;
// Bounce
ballYDelta *= -1;
}
ball.setFrame(bounds);
// Update the view
gameView.updateState();
}
}
This is basically where we are making decisions about the current position of the objects and updating their positions. Here we check for "out-of-bounds" positions and update their states appropriately (for example, the ball can "bounce" and change directions)
The delta values are quite small, so you might want to play around with those
And finally, we need something that pulls it all together...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class NonaMaingamePractice extends JPanel implements KeyListener, GameView {
/**
*
*/
private static final long serialVersionUID = 1L;
JLabel text = new JLabel("cool");
private Timer timeBall;
private GameModel model;
private boolean init = false;
private boolean rightIsPressed;
private boolean leftIsPressed;
public NonaMaingamePractice() {
setLayout(new BorderLayout());
addKeyListener(this);
setFocusable(true);
JPanel panelNorth = makePanel();
panelNorth.setBackground(Color.CYAN);
add(panelNorth, BorderLayout.NORTH);
JLabel scoreLabel = new JLabel("Score: " + 0);
panelNorth.add(scoreLabel);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
if (getWidth() > 0 && getHeight() > 0 && !init) {
init = true;
model = new DefaultGameModel(getSize());
timeBall = new Timer(40, new MainLoop(NonaMaingamePractice.this, model));
timeBall.start();
} else if (model != null) {
model.getPaddle().y = (getHeight() - model.getPaddle().height) - 10;
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
Graphics2D graphics = (Graphics2D) g;
if (model != null) {
graphics.fill(model.getPaddle());
graphics.fill(model.getBall());
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
int c = e.getKeyCode();
if (c == KeyEvent.VK_RIGHT) {
rightIsPressed = true;
} else if (c == KeyEvent.VK_LEFT) {
leftIsPressed = true;
}
}
#Override
public void keyReleased(KeyEvent e) {
int c = e.getKeyCode();
if (c == KeyEvent.VK_RIGHT) {
rightIsPressed = false;
} else if (c == KeyEvent.VK_LEFT) {
leftIsPressed = false;
}
}
protected JPanel makePanel() {
#SuppressWarnings("serial")
JPanel pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 30);
}
};
pane.setBackground(Color.CYAN);
return pane;
}
#Override
public boolean isKeyRightPressed() {
return rightIsPressed;
}
#Override
public boolean isKeyLeftPressed() {
return leftIsPressed;
}
#Override
public void updateState() {
// Maybe update the score??
repaint();
}
public static void main(String[] args) {
NonaMaingamePractice main = new NonaMaingamePractice();
JFrame frame = new JFrame();
frame.add(main);
frame.setVisible(true);
frame.setTitle("Project 4 game");
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Detect overlapping objects in java Swing

I am trying to make a program that has a moving ball and a platform that it will sit on. I am new too java and I cant figure out how to detect when 2 swing objects are overlapping. My code it below and I am wondering what the best way is to detect overlapping objects.
KeyDemo.java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class KeyDemo
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
JPanel panel = new JPanel();
LayoutManager overlay = new OverlayLayout(panel);
panel.setLayout(overlay);
final int FRAME_WIDTH = 800;
final int FRAME_HEIGHT = 600;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("Move the Ball");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final WallComponent wc1 = new WallComponent(400, 400);
final BallComponent bc = new BallComponent(400, 300);
panel.add(wc1);
panel.add(bc);
frame.add(panel);
KeyboardController kc = new KeyboardController(bc);
frame.addKeyListener(kc);
frame.setVisible(true);
class AnimationListener implements ActionListener{
public void actionPerformed(ActionEvent event){
bc.tick();
//wc1.checkOverlap(bc);
}
}
ActionListener aListener = new AnimationListener();
final Timer timer = new Timer(1, aListener);
timer.start();
}
}
KeyboardController.java:
import java.awt.*;
import java.awt.event.*;
public class KeyboardController implements KeyListener
{
BallComponent bComp;
public KeyboardController(BallComponent t)
{
bComp = t;
}
/** Handle the key pressed event from the text field. */
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == 38)
{
System.out.println("Pressed Up!");
bComp.moveUp();
}
if(keyCode == 37)
{
System.out.println("Pressed Left!");
bComp.moveLeft();
}
if(keyCode == 39)
{
System.out.println("Pressed Right!");
bComp.moveRight();
}
if(keyCode == 40)
{
System.out.println("Pressed Down!");
bComp.moveDown();
}
}
/** Handle the key released event from the text field. */
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == 38)
{
System.out.println("Released Up!");
bComp.stopY();
}
if(keyCode == 37)
{
System.out.println("Released Left!");
bComp.stopX();
}
if(keyCode == 39)
{
System.out.println("Released Right!");
bComp.stopX();
}
if(keyCode == 40)
{
System.out.println("Pressed Down!");
bComp.stopY();
}
}
public void keyTyped(KeyEvent e) {
}
}
BallComponent.java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class BallComponent extends JComponent
{
int xSpeed;
int ySpeed;
int x;
int y;
public BallComponent(int x, int y)
{
super();
this.x = x;
this.y = y;
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
Ellipse2D.Double ball = new Ellipse2D.Double(x-10,y-10,10,10);
g2.setColor(Color.RED);
g2.fill(ball);
g2.draw(ball);
}
public void moveLeft()
{
xSpeed=-1;
}
public void moveRight()
{
xSpeed=1;
}
public void moveUp()
{
ySpeed=-1;
}
public void moveDown()
{
ySpeed=1;
}
public void tick()
{
x=x+xSpeed;
y=y+ySpeed;
repaint();
}
public void stopY()
{
ySpeed=0;
}
public void stopX()
{
xSpeed=0;
}
}
WallComponent.java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class WallComponent extends JComponent
{
int x;
int y;
public WallComponent(int x, int y)
{
super();
this.x = x;
this.y = y;
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
Rectangle wall = new Rectangle(x-40,y-40,40,40);
g2.setColor(Color.YELLOW);
g2.fill(wall);
g2.draw(wall);
}
public void checkOverlap(BallComponent bc){
if (this.contains(bc.getLocation())){
bc.stopY();
bc.stopX();
}
}
}
All Swing components have a concept of "bounds". This is a rectangular area within which they are "drawn".
If you are controlling the size and position correctly, you should be able to use the contains method of the Rectangle which is returned from calling Component#getBounds
So you checkOverlap method could look like...
public void checkOverlap(BallComponent bc){
if (getBounds().intersects(bc.getBounds())){
bc.stopY();
bc.stopX();
}
}
You will also want to make sure that you are calling super.paintComponent before performing any custom painting, espeically when using components that extend from JComponent. This will ensure that the Graphics context is prepared for painting correctly...
Updated
There's a cascade of issues. Basically, instead of positing the components within the parent container yourself (which is how I thought you had done it), you've laid each component out to fill the parent container and only "painted" the objects...This makes life more difficult
Instead, if you are going to use components, I would use a null layout (or even possibly use a JLayeredPane as the parent container).
I would then change the physical position of the components, for example...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.OverlayLayout;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestGame {
public static void main(String[] args) {
new TestGame();
}
public TestGame() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setLayout(null);
final int FRAME_WIDTH = 800;
final int FRAME_HEIGHT = 600;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("Move the Ball");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final WallComponent wc1 = new WallComponent(400, 400);
final BallComponent bc = new BallComponent(400, 300);
panel.add(wc1);
panel.add(bc);
frame.add(panel);
KeyboardController kc = new KeyboardController(bc);
frame.addKeyListener(kc);
frame.setVisible(true);
class AnimationListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
bc.tick();
wc1.checkOverlap(bc);
}
}
ActionListener aListener = new AnimationListener();
final Timer timer = new Timer(1, aListener);
timer.start();
}
});
}
public class KeyboardController implements KeyListener {
BallComponent bComp;
public KeyboardController(BallComponent t) {
bComp = t;
}
/**
* Handle the key pressed event from the text field.
*/
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == 38) {
System.out.println("Pressed Up!");
bComp.moveUp();
}
if (keyCode == 37) {
System.out.println("Pressed Left!");
bComp.moveLeft();
}
if (keyCode == 39) {
System.out.println("Pressed Right!");
bComp.moveRight();
}
if (keyCode == 40) {
System.out.println("Pressed Down!");
bComp.moveDown();
}
}
/**
* Handle the key released event from the text field.
*/
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == 38) {
System.out.println("Released Up!");
bComp.stopY();
}
if (keyCode == 37) {
System.out.println("Released Left!");
bComp.stopX();
}
if (keyCode == 39) {
System.out.println("Released Right!");
bComp.stopX();
}
if (keyCode == 40) {
System.out.println("Pressed Down!");
bComp.stopY();
}
}
public void keyTyped(KeyEvent e) {
}
}
public class BallComponent extends JComponent {
int xSpeed;
int ySpeed;
public BallComponent(int x, int y) {
super();
setBounds(x, y, 10, 10);
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Ellipse2D.Double ball = new Ellipse2D.Double(0, 0, 9, 9);
g2.setColor(Color.RED);
g2.fill(ball);
g2.draw(ball);
}
public void moveLeft() {
xSpeed = -1;
}
public void moveRight() {
xSpeed = 1;
}
public void moveUp() {
ySpeed = -1;
}
public void moveDown() {
ySpeed = 1;
}
public void tick() {
int x = getX() + xSpeed;
int y = getY() + ySpeed;
setLocation(x, y);
repaint();
}
public void stopY() {
ySpeed = 0;
}
public void stopX() {
xSpeed = 0;
}
}
public class WallComponent extends JComponent {
public WallComponent(int x, int y) {
super();
setBounds(x, y, 40, 40);
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Rectangle wall = new Rectangle(0, 0, 40, 40);
g2.setColor(Color.YELLOW);
g2.fill(wall);
g2.draw(wall);
}
public void checkOverlap(BallComponent bc) {
System.out.println(" me: " + getBounds());
System.out.println("you: " + bc.getBounds());
if (getBounds().intersects(bc.getBounds())) {
bc.stopY();
bc.stopX();
}
}
}
}
Now, you could use "painted" objects, but in that case I would have a virtual concept of a Ball and Wall which you could paint within a single component. These objects would need to provide information about there position and size, which you could, again, check using Rectangle#intersects...
Generally, just try to make a "bounding box" for your objects. This will be the invisible rectangle that goes with the object. Then just do
if(rectangle1.intersects(rectangle2)) ...
The intersects method only works with rectangles, and that's why you need a bounding box.

How do I get my image to follow my mouse?

How do I get my image to follow my mouse anywhere on the screen?
The below code makes the image move along the x axis.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
public class PlayerTwo implements KeyListener, MouseListener, MouseMotionListener{
public static int PLAYER_HEIGHT = 15;
public static int PLAYER_WIDTH = 15;
private Image p2Image = null;
private static int x = 0;
private static int y = 0;
private int heightPosition = 0;
Main main = null;
public PlayerTwo(Image pi, Main m ){
main = m;
p2Image = pi;
y = (int)((Main.WIDTH*2)+(PLAYER_WIDTH*2));
heightPosition = Main.HEIGHT-PLAYER_HEIGHT-20;
}
public void drawPlayer(Graphics g){
g.drawImage(p2Image, y, heightPosition, main);
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent me) {
int newX = me.getX();
int newY = me.getY();
if(newY > (Main.HEIGHT+PLAYER_HEIGHT+10)){
y = Main.HEIGHT+PLAYER_HEIGHT+10;
}else{
y = newY;
}
// if (newX > (Main.WIDTH-PLAYER_WIDTH-10)){
// x = Main.WIDTH-PLAYER_WIDTH-10;
// }else{
// x = newX;
// }
}
}
Updated with Main...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class Main extends JFrame implements Runnable {
public static int WIDTH = 600;
public static int HEIGHT = 600;
private int gameSpeed = 100;
PlayerOne playOne = null;
PlayerTwo playTwo = null;
Image p1Image = null;
Image p2Image = null;
Image backImage = null;
Graphics offscreen_high;
BufferedImage offscreen;
public Main(String frameTitle) {
super(frameTitle);
p1Image = new javax.swing.ImageIcon("src/resources/player1.gif").getImage();
p2Image = new javax.swing.ImageIcon("src/resources/player2.gif").getImage();
backImage = new javax.swing.ImageIcon("src/resources/back.png").getImage();
offscreen = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
offscreen_high = offscreen.createGraphics();
playOne = new PlayerOne(p1Image, this);
playTwo = new PlayerTwo(p2Image, this);
addKeyListener(playOne);
addKeyListener(playTwo);
addMouseListener(playTwo);
addMouseMotionListener(playTwo);
setSize(WIDTH, HEIGHT);
setVisible(true);
startGame();
}
public void startGame() {
Thread thread = new Thread(this);
thread.start();
}
public void paint(Graphics g) {
offscreen_high.setColor(Color.black);
offscreen_high.fillRect(0, 0, WIDTH, HEIGHT);
offscreen_high.drawImage(backImage, 0, 0, this);
playOne.drawPlayer(offscreen_high);
playTwo.drawPlayer(offscreen_high);
g.drawImage(offscreen, 0, 0, this);
}
// public void update(Graphics g){
// paint(g);
// }
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(gameSpeed);
} catch (InterruptedException ie) {
}
repaint();
count++;
}
}
public static void main(String[] args) {
Main main = new Main("Game On!");
}
}
Generally, you need some way to tell the UI that it should be updated.
Assuming that Main is some kind of component (and it's also responsible for painting the Player), you should be calling its repaint method in the mouseListener
But without more details, this is more of a guess
Updated
After a muck around with the code, the main problem, as I see it, is your trying to draw the image only the horizontal axis (x) using the vertical position (y)...
public void drawPlayer(Graphics g){
//g.drawImage(p2Image, y, heightPosition, main);
g.drawImage(p2Image, x, heightPosition, main);
}
To get it to work, you're going to have to uncomment the code in you mouseMoved method so that the x position updates.
You should also avoid painting to top level containers, the main reason (apart from the fact that you can screw up the paint process) is that top level containers are not double buffered.
Instead, you should move your entire game container over to something like a JPanel and override it's paintComponent method (and don't for get to call super.paintComponent)

Categories

Resources