How do I make a sprite move in a custom JPanel?
I have looked at the similar questions and although one question is similar, it isn't addressing my problem. I have a sprite in a JPanel and I am unable to get it to move. One of the requirements I have to meet for the program is that it must begin moving when a JButton is pressed (Mouse Click). I have the code set-up in a way I believe should work, but it will spit out a long list of errors when I press the button. I'm also required to have the panel be a custom panel class.
What I need to know is this:
Methods (ha) of programming sprite movement.
Continuing to move the sprite without a trail.
Making the sprite bounce off the edges of the panel. Done (Unable to test due to no moving ball)
Here's the code I have (MainClient).
package clientPackage;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import logicPack.Logic;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ClientClass
{
Ball mSolo = new Ball();
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientClass window = new ClientClass();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClientClass()
{
initialize();
}
/**
* Initialize the contents of the frame.
*/
Logic Logical;
Graphics g;
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 590, 520);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
SpriteField panel = new SpriteField();
panel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
/* int tX = e.getX();
Logical.MoveBallX();
int tY = e.getY();
Logical.MoveBallY();
panel.repaint();*/
Logical.MoveBallX();
Logical.MoveBallY();
panel.repaint();
}
});
panel.setForeground(Color.WHITE);
panel.setBackground(Color.GRAY);
panel.setBounds(64, 92, 434, 355);
frame.getContentPane().add(panel);
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
Graphics2D g2 = (Graphics2D)g;
mSolo.DrawSprite(g2 , Logical.MoveBallX(), Logical.MoveBallY());
}
});
btnStart.setBounds(64, 13, 174, 60);
frame.getContentPane().add(btnStart);
}
}
And here are my other Classes (Logic)
package logicPack;
import clientPackage.Ball;
public class Logic
{
Ball mSolo;
public int MoveBallX()
{
int NewX = mSolo.xPos + 50;
return NewX;
}
public int MoveBallY()
{
int NewY = mSolo.yPos + 50;
return NewY;
}
//Motion, force, friction and collision GO HERE ONLY
}
SpriteField
package clientPackage;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class SpriteField extends JPanel
{
Ball mSolo;
SpriteField()
{
mSolo = new Ball();
repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
mSolo.DrawSprite(g2 , mSolo.xPos , mSolo.yPos);
}
}
Ball
package clientPackage;
import java.awt.Color;
import java.awt.Graphics2D;
public class Ball
{
Ball()
{
}
public int xPos = 25;
public int yPos = 25;
int diameter = 25;
public void DrawSprite(Graphics2D g2, int xPos, int yPos)
{
g2.setColor(Color.BLACK);
g2.fillOval(xPos - diameter / 2 , yPos - diameter / 2 , diameter , diameter);
}
}
If you do not understand my Java comments, you can just ignore them.
If you need more details to help me, let me know.
EDIT 1:
Andrew, the closest article I could find used arrow keys to move a sprite. The article was "Sprite not moving in JPanel". All the other articles I found either addressed JPanels without sprites, or animating a sprite. However, I need a JButton that is MouseClicked to simply start the movement, and the ball does not change shape or color. I believe I have the collision part working, but I'm unable to test it until the ball starts moving.
EDIT 2:
LuxxMiner, Thanks for the hints. I have refined my collision portion to be a little more accurate using the getHeight and getWidth methods.
EDIT 3:
MadProgrammer, Thanks...? The problem is not the painting of the ball, I cannot get the ball to move in the first place to repaint it. And the example uses arrow keys, not a mouse click or JButton.
First, take a look at Painting in AWT and Swing and Performing Custom Painting to understand how painting works in Swing.
Let's have a look at the code...
You have a Ball class, which has it's own properties, but then your DrawSprite method passes in values which override these properties?
public class Ball {
Ball() {
}
public int xPos = 25;
public int yPos = 25;
int diameter = 25;
public void DrawSprite(Graphics2D g2, int xPos, int yPos) {
g2.setColor(Color.BLACK);
g2.fillOval(xPos - diameter / 2, yPos - diameter / 2, diameter, diameter);
}
}
What's the point of that? The Ball should paint it's own current state. You should get rid of the additional parameters
public class Ball {
Ball() {
}
public int xPos = 25;
public int yPos = 25;
int diameter = 25;
public void DrawSprite(Graphics2D g2) {
g2.setColor(Color.BLACK);
g2.fillOval(xPos - diameter / 2, yPos - diameter / 2, diameter, diameter);
}
}
ClientClass, Logic and SpriteField all have their own Ball references, none of which is shared so if Logic where to update the state of it's Ball, neither ClientClass or SpriteField would actually see those changes.
In reality, only SpriteField needs an instance of Ball, as it's basically the "ball container", it has the information need to determine if the ball moves out of bounds and wants to know when the ball should be repainted, better to isolate the functionality/responsibility for the Ball to SpriteField at this time.
You also need a means to actually move the ball. While you could use other events, I'd be nice if the ball just moved itself, to this end, you can use a Swing Timer, which won't block the Event Dispatching Thread, but which notifies the registered ActionListener within the context of the EDT, making it safe to update the UI from within.
public class SpriteField extends JPanel {
private Ball mSolo;
private Timer timer;
private int xDelta, yDelta;
public SpriteField() {
mSolo = new Ball();
do {
xDelta = (int) ((Math.random() * 8) - 4);
} while (xDelta == 0);
do {
yDelta = (int) ((Math.random() * 8) - 4);
} while (yDelta == 0);
}
public void start() {
if (timer == null || !timer.isRunning()) {
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
mSolo.xPos += xDelta;
mSolo.yPos += yDelta;
if (mSolo.xPos - (mSolo.diameter / 2) < 0) {
mSolo.xPos = mSolo.diameter / 2;
xDelta *= -1;
} else if (mSolo.xPos + (mSolo.diameter / 2) > getWidth()) {
mSolo.xPos = getWidth() - (mSolo.diameter / 2);
xDelta *= -1;
}
if (mSolo.yPos - (mSolo.diameter / 2) < 0) {
mSolo.yPos = (mSolo.diameter / 2);
yDelta *= -1;
} else if (mSolo.yPos + (mSolo.diameter / 2) > getHeight()) {
mSolo.yPos = getHeight() - (mSolo.diameter / 2);
yDelta *= -1;
}
repaint();
}
});
timer.start();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
mSolo.DrawSprite(g2);
}
}
Now, all you need to do, is when the "Start" button is clicked, call the start method
public class ClientClass {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientClass window = new ClientClass();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClientClass() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
// Logic Logical;
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 590, 520);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SpriteField panel = new SpriteField();
panel.setForeground(Color.WHITE);
panel.setBackground(Color.GRAY);
frame.getContentPane().add(panel);
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
panel.start();
}
});
frame.getContentPane().add(btnStart, BorderLayout.SOUTH);
}
}
Related
I am making a game where a user has to draw lines so as to make a ball bounce into a target. I'm having trouble getting both the ball and the line to show up concurrently, and I can get only one or the other to appear. It seems to me that the panels block each other out, even though I made them transparent. I would like for them both to appear on the same frame. As of this post, the line panel covers the ball panel.
import javax.swing.Timer;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class Game
{
public static void main(String args[]) throws Exception
{
JFrame f = new JFrame("Let's Play");
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setSize(1280, 720);
f.setLocation(300, 300);
f.setResizable(false);
//this part draws a ball that bounces around the screen
BallPanel ballPanel = new BallPanel()
{
// draw rectangles and arcs
public void paintComponent(Graphics g)
{
super.paintComponent(g); // call superclass's paintComponent
g.setColor(Color.red);
// check for boundaries
if (x < radius) dx = Math.abs(dx);
if (x > getWidth() - radius) dx = -Math.abs(dx);
if (y < radius) dy = Math.abs(dy);
if (y > getHeight() - radius) dy = -Math.abs(dy);
// adjust ball position
x += dx;
y += dy;
g.fillOval(x - radius, y - radius, radius*2, radius*2);
}
};
ballPanel.setOpaque(false);
f.add(ballPanel);
//this part allows you to draw lines on the frame with your mouse
JPanel lineP = new JPanel()
{
Point pointStart = null;
Point pointEnd = null;
{
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent me)
{
pointStart = me.getPoint();
}
public void mouseReleased(MouseEvent me)
{
pointStart = null;
}
});
addMouseMotionListener(new MouseMotionAdapter()
{
public void mouseMoved(MouseEvent me)
{
pointEnd = me.getPoint();
}
public void mouseDragged(MouseEvent me)
{
pointEnd = me.getPoint();
repaint();
}
});
}
public void paint(Graphics dline)
{
super.paint(dline);
if (pointStart != null)
{
dline.setColor(Color.RED);
dline.drawLine(pointStart.x, pointStart.y, pointEnd.x, pointEnd.y);
}
}
};
lineP.setOpaque(false); //attempted to enable to see ball panel here
f.add(lineP);
f.setVisible(true);
}
}
class BallPanel extends JPanel implements ActionListener
{
private int delay = 10;
protected Timer timer;
public int x = 30; // x position
public int y = 30; // y position
public int radius = 15; // ball radius
public int dx = 10; // increment amount (x coord)
public int dy = 10; // increment amount (y coord)
public BallPanel()
{
timer = new Timer(delay, this);
timer.start(); // start the timer
}
public void actionPerformed(ActionEvent e)
// will run when the timer fires
{
repaint();
}
}
You've got several issues, but the main one is that you're over-using GUI components. You should have just one single component JPanel that does the drawing, a DrawingPanel, and not a ball panel and a line panel. Rather Ball and Line should be logical classes, not GUI classes, and their display should be in the same single DrawingPanel.
Other issues include:
A main method that has way too much code. Most of that code should be off-loaded into the OOP world where it belongs.
GUI component classes that also implement listener interfaces. This is giving the class too much responsibility making debugging and upgrading difficult. Separate these concerns.
One of your classes overrides the paint method, and this should be avoided. Override paintComponent.
The other class that overrides paintComponent has program logic within paintComponent, and this should be avoided since you have limited control over when or if this method gets called. Get the logic out of that class and into either the mouse listener code or the game loop code (Swing Timer).
I am painting vehicle objects that I defined using the paintComponent().
Because the vehicles can move, I implement ActionListener and set a Timer() to trigger.
As a result, my vehicles can move. But it is kind of "shaking". When I keep resizing the window to call the paintComponent(), the movement becomes smooth. When I do not resize the window (not calling paintComponent), it gets skaking again. Why? How to fix it?
public class VehiclesComponent extends JComponent implements ActionListener{
private Vehicle[] vehicles;
private Timer timer;
public VehiclesComponent(int n){
vehicles = Vehicle.generateVehicle(n);
timer = new Timer(5,this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for (int i=0; i<vehicles.length; i++) {
vehicles[i].draw(g2);
}
// may change later
timer.start();
}
#Override
public void actionPerformed(ActionEvent e){
//check collision in here
for (Vehicle v : vehicles) {
if (Vehicle.intersectsOther(v, vehicles)) {
v.collisionSideEffect();
}
}
//move all in here
for (Vehicle v : vehicles ) {
v.move();
}
repaint();
//?? repaint slower than paintComponent
}
}
Start by taking a look at Painting in AWT and Swing. Remember, repaint is only a suggest made to the RepaintManager, the RepaintManager may choose to consolidate multiple repaint calls into a smaller number of actual paint events.
Make sure you are calling super.paintComponent, otherwise you will end up with no end of strange paint artifacts.
Don't, directly or indirectly, modify the state of the component or ant other components from within any paint method, this will result in a new repaint request been made, which could lead to a cycle of paint events which could consume your CPU cycles. This means, don't call timer.start()!
Without a runable example to go by, I hobbled this together. Now this is animating 10, 000 individual Vehicles (rectangles), so it's massively over kill, but it should provide the point...
(the gif is only running at 7fps, not your 200fps)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new VehiclesComponent(10000));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class VehiclesComponent extends JComponent implements ActionListener {
private Vehicle[] vehicles;
private Timer timer;
public VehiclesComponent(int n) {
vehicles = Vehicle.generateVehicle(n, getPreferredSize());
timer = new Timer(5, this);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (int i = 0; i < vehicles.length; i++) {
vehicles[i].draw(g2);
}
}
#Override
public void actionPerformed(ActionEvent e) {
//check collision in here
// for (Vehicle v : vehicles) {
// if (Vehicle.intersectsOther(v, vehicles)) {
// v.collisionSideEffect();
// }
// }
//move all in here
for (Vehicle v : vehicles) {
v.move(this.getSize());
}
repaint();
//?? repaint slower than paintComponent
}
}
public static class Vehicle {
protected static final int SIZE = 5;
protected static final Color[] COLORS = new Color[]{
Color.BLACK,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GREEN,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.RED,
Color.WHITE,
Color.YELLOW
};
private int x = 0;
private int y = 0;
private int xDelta;
private int yDelta;
private Shape car;
private Color color;
public static Vehicle[] generateVehicle(int count, Dimension bounds) {
Vehicle[] vehicles = new Vehicle[count];
for (int index = 0; index < vehicles.length; index++) {
vehicles[index] = new Vehicle(bounds);
}
return vehicles;
}
public Vehicle(Dimension size) {
x = (int)(Math.random() * (size.width - SIZE));
y = (int)(Math.random() * (size.height - SIZE));
xDelta = (int)(Math.random() * 3) + 1;
yDelta = (int)(Math.random() * 3) + 1;
car = new Rectangle(SIZE, SIZE);
color = COLORS[(int)(Math.random() * COLORS.length)];
}
public void move(Dimension size) {
x += xDelta;
y += yDelta;
if (x < 0) {
x = 0;
xDelta *= -1;
} else if (x + SIZE > size.width) {
x = size.width - SIZE;
xDelta *= -1;
}
if (y < 0) {
y = 0;
yDelta *= -1;
} else if (y + SIZE > size.height) {
y = size.height - SIZE;
yDelta *= -1;
}
}
public void draw(Graphics2D g2) {
g2.translate(x, y);
g2.setColor(color);
g2.fill(car);
g2.translate(-x, -y);
}
}
}
You could also take a look at this example which renders upwards of 4500 images in random directions and demonstrates some optimisation techniques.
You can also take a look at this example which is capable of animating both in direction and rotation, upwards of 10, 000 images
I made a simple program in Java which draws a rectangle on a canvas. And then the rectangle starts moving along X-axis, from left to right.
But the timer.schedule() function is not working. Following is the code:-
package firstanimation;
import java.awt.*;
import java.util.Timer;
public class FirstAnimation {
public static void main(String[] args) {
Frame frame = new Frame("SomeRandomName");
frame.setBounds(50, 50, 700, 500);
frame.setBackground(Color.red);
MyCanvas canvas = new MyCanvas();
frame.add(canvas);
frame.setVisible(true);
Graphics graph = frame.getGraphics();
Timer timer = new Timer();
Task task = new Task(canvas, graph);
timer.schedule(task, 1000,1000);
}
}
package firstanimation;
import java.awt.*;
public class MyCanvas extends Canvas{
public int x,y,width,height;
public MyCanvas()
{
x = 0;
y = 0;
width = 50;
height = 50;
}
#Override
public void paint(Graphics g) {
g.setColor(Color.LIGHT_GRAY);
g.fillRect(x, y, width, height);
}
#Override
public void update(Graphics g) {
x+=10;
g.fillRect(x, y, width, height);
}
}
package firstanimation;
import java.util.TimerTask;
import java.awt.Graphics;
public class Task extends TimerTask{
private MyCanvas canvas;
private Graphics graphics;
public Task(MyCanvas can, Graphics g)
{
super();
canvas = can;
graphics = g;
canvas.paint(g);
}
#Override
public void run() {
canvas.update(graphics);
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
But the strange thing is... Every time i'm maximizing and restoring the frame, the box is moving.
Why is this happening?
"But the strange thing is... Every time i'm maximizing and restoring the frame, the box is moving. Why is this happening?"
Because repaint() is called when you resize which update the graphics, which you should be doing, instead of trying to call paint.
But...
Still many things wrong.
Seeing as this is your first animation (package firstanimation;), let me get you started in the right direction.
Don't use Canvas. Use JPanel or JComponent instead. When you do, don't override paint but paintComponent instead. Also make sure you call super.paintComponent so you aren't let with any paint artifact during the animation.
public class MyCanvas extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//painting code
}
}
Never use getGraphics on a component to do any painting.
You shouldn't ever have to explicitly call paint. The paint[Component] method will be implicitly called for you. A simple call to repaint() will repaint the component.
I just realized you're using all AWT components. Don't use them, they're out-dated. Instead use Swing component. The majority of them are just prefixed with a J, like Frame -> JFrame. They are in the javax.swing.* package.
For animation use a javax.swing.Timer. You can see more at How to Use Timers. The basic construct is
Timer ( int delayInMillis, ActionListener listener )
where delayInMillis is the time to delay between ticks(in this case animations) and the ActionListener listens for "ticks". Each tick, the actionPerformed of the ActionListener is called. There, you can put the code to update any variables you use for animation.
I suggest you read the tutorials Performing Custom Painting to see the proper way to paint.
Here's a simple example with all the points above mentioned
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class AnimateBall extends JPanel {
private static final int D_W = 500;
private static final int D_H = 300;
private Ball ball;
public AnimateBall() {
Random rand = new Random();
int randX = rand.nextInt(D_W);
int randY = rand.nextInt(D_H);
ball = new Ball(randX, randY);
Timer timer = new Timer(15, new ActionListener() {
public void actionPerformed(ActionEvent e) {
ball.animate();
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
ball.drawBall(g);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public class Ball {
int x = 0;
int y = 0; // Current ball position
int dx = 4; // Increment on ball's x-coordinate
int dy = 4; // Increment on ball's y-coordinate
int radius = 15; // Ball radius
public Ball(int x, int y) {
this.x = x;
this.y = y;
}
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
public void drawBall(Graphics g) {
g.setColor(color);
g.fillOval(x - radius, y - radius, radius * 2, radius * 2);
}
public void animate() {
if (x < 0 || x > getWidth()) {
dx = -dx;
}
if (y < 0 || y > getHeight()) {
dy = -dy;
}
// Adjust ball position
x += dx;
y += dy;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new AnimateBall());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Hi guys I'm super new to Java; I've looked around and haven't been able to find an answer to this question. Any chance you could help me?
Here is an example of what I'm trying to achieve.
public class FrameWork extends JFrame implements MouseListener {
... //Irrelevant to the question code
public void mouseClicked(MouseEvent e){
int x = e.getX();
int y = e.getY();
if (x==1 && y==1){
// This is where and when I want to draw GFXDice
}
}}
Now the other class, all imports left out for readability.
public class Board extends JPanel{
Image GFXDice1;
public Board() {
ImageIcon Dice1;
Dice1 = new ImageIcon(this.getClass().getResource("GFX/Dice1"));
GFXDice1 = Dice1.getImage();
}
Now the graphics part
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(GFXDice, 100, 100, null);
}
Now for the question - I want to use the method paint from the Class Board in the Class FrameWork - But can't get it to work - any ideas ? I'm offering a bazillion units of good karma to anyone who has an idea.
The general way to do most Swing drawing is via passive graphics. This means:
Do the drawing itself in the paintComponent(Graphics g) method of a JPanel or JComponent.
In your MouseListener change the state of some of the fields of the class. In your mouseClicked method you are setting the state of some local variables, and I recommend that you instead make your x and y fields, not local.
Then when the mouse listener is done making changes, call repaint() on the JPanel.
Then in the paintComponent method, use those fields that were changed in the mouse listener to do your drawing.
Don't forget to call the super's paintComponent method in your paintComponent override.
Don't forget to read tutorials on Swing Graphics to get the fine points.
Edit
For example, please have a look at a small graphics program that I created for an answer to another recent question.
The drawing occurs in the main class, SpaceShip, which extends JPanel. I add an anonymous inner MouseAdapter class for my Mouse Listener, and inside of the MouseAdapter, I call a method called moveIt, passing in the MouseEvent object.
MouseAdapter myMouseAdapter = new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
moveIt(evt);
count = count + 1;
}
#Override
public void mouseDragged(MouseEvent evt) {
moveIt(evt);
}
};
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
All moveIt(MouseEvent evt) does is to change the state of two fields, myX and myY, and then calls repaint() on the current class:
public void moveIt(MouseEvent evt) {
myY = evt.getY() - sprite.getHeight() / 2;
myX = evt.getX() - sprite.getWidth() / 2;
repaint();
}
And then in the class's paintComponent method, I first call the super's paintComponent to allow it to erase any previous old out of date images, then I paint a background image, background, then I draw a sprite that uses the myX and myY variables to tell it where to draw, then I draw some yellow rectangles at locations that are determined by the JPanel's size:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
font1 = new Font("Serif", Font.BOLD, 36);
g.drawImage(background, 0, 0, this);
g.drawImage(sprite, myX, myY, this);
g.setColor(Color.yellow);
int rectCount = 10;
int height = getHeight() / rectCount;
int width = 272;
int x = getWidth() - width;
for (int i = 0; i < rectCount; i++) {
int y = i * height;
g.drawRect(x, y, width, height);
}
g.setFont(font1);
g.drawString(Integer.toString(count), 500, 100);
}
The whole thing looks like this:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.Graphics;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.io.IOException;
import java.net.URL;
import java.lang.String;
import java.awt.Font;
#SuppressWarnings("serial")
public class SpaceShip extends JPanel {
private static final String BACKGROUND_PATH = "http://www.thatsreallypossible.com/"
+ "wp-content/uploads/2012/12/Space-Colonialisation.jpg";
private static final String SPRITE_PATH = "http://www.pd4pic.com/"
+ "images250_/ufo-flying-saucer-spacecraft-spaceship-alien.png";
private Font font1;
int myX = 100;
int myY = 400;
int count = 0;
private BufferedImage background;
private BufferedImage sprite;
public SpaceShip() throws IOException {
URL backgroundUrl = new URL(BACKGROUND_PATH);
URL spriteUrl = new URL(SPRITE_PATH);
background = ImageIO.read(backgroundUrl);
sprite = ImageIO.read(spriteUrl);
MouseAdapter myMouseAdapter = new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
moveIt(evt);
count = count + 1;
}
#Override
public void mouseDragged(MouseEvent evt) {
moveIt(evt);
}
};
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
#Override
public Dimension getPreferredSize() {
if (background != null) {
return new Dimension(background.getWidth(), background.getHeight());
}
return super.getPreferredSize();
}
public void moveIt(MouseEvent evt) {
myY = evt.getY() - sprite.getHeight() / 2;
myX = evt.getX() - sprite.getWidth() / 2;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
font1 = new Font("Serif", Font.BOLD, 36);
g.drawImage(background, 0, 0, this);
g.drawImage(sprite, myX, myY, this);
g.setColor(Color.yellow);
int rectCount = 10;
int height = getHeight() / rectCount;
int width = 272;
int x = getWidth() - width;
for (int i = 0; i < rectCount; i++) {
int y = i * height;
g.drawRect(x, y, width, height);
}
g.setFont(font1);
g.drawString(Integer.toString(count), 500, 100);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Basic Game");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
SpaceShip ex;
try {
ex = new SpaceShip();
frame.getContentPane().add(ex);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
ex.requestFocus();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Circle extends JApplet {
public void paint(Graphics g) {
int x=100;
int y=100;
int diameter=50;
int xResize=500;
int yResize=500;
super.paint(g);
resize(xResize,yResize);
g.drawOval(x, y, diameter, diameter);
}
}
So I am trying to create a ball that bounces up and down and progressively gets smaller. I need to use the following code as a class that will set up my next class that will actually carry out the action. I know that I need to set up the current code that I have into constructors, instance variables and methods to create objects from but I can't seem to figure out how to do that.
Also how would I make the drawn image move up and down across the JApplet?
This is a basic example of the idea
Basically, when you press the spacebar, it generates a vertical movement which slows over time till it runs out of energy, it will then fall and rebound until it has run out of "bounce"
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class JumpingSprite {
public static void main(String[] args) {
new JumpingSprite();
}
public JumpingSprite() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
protected static final int SPRITE_HEIGHT = 10;
protected static final int SPRITE_WIDTH = 10;
private float vDelta; // The vertical detla...
private float rbDelta; // Rebound delta...
private float rbDegDelta; // The amount the rebound is degregated...
private int yPos; // The vertical position...
private float gDelta; // Gravity, how much the vDelta will be reduced by over time...
private Timer engine;
private boolean bounce = false;
public TestPane() {
yPos = getPreferredSize().height - SPRITE_HEIGHT;
vDelta = 0;
gDelta = 0.25f;
// This is how much the re-bound will degrade on each cycle...
rbDegDelta = 2.5f;
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "jump");
am.put("jump", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Can only bound when we're actually on the ground...
// You might want to add fudge factor here so that the
// sprite can be within a given number of pixels in order to
// jump again...
if (yPos + SPRITE_HEIGHT == getHeight()) {
vDelta = -8;
rbDelta = vDelta;
bounce = true;
}
}
});
engine = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int height = getHeight();
// No point if we've not been sized...
if (height > 0) {
// Are we bouncing...
if (bounce) {
// Add the vDelta to the yPos
// vDelta may be postive or negative, allowing
// for both up and down movement...
yPos += vDelta;
// Add the gravity to the vDelta, this will slow down
// the upward movement and speed up the downward movement...
// You may wish to place a max speed to this
vDelta += gDelta;
// If the sprite is not on the ground...
if (yPos + SPRITE_HEIGHT >= height) {
// Seat the sprite on the ground
yPos = height - SPRITE_HEIGHT;
// If the re-bound delta is 0 or more then we've stopped
// bouncing...
if (rbDelta >= 0) {
// Stop bouncing...
bounce = false;
} else {
// Add the re-bound degregation delta to the re-bound delta
rbDelta += rbDegDelta;
// Set the vDelta...
vDelta = rbDelta;
}
}
}
}
repaint();
}
});
engine.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth() - 1;
int xPos = (width - SPRITE_WIDTH) / 2;
g2d.drawOval(xPos, yPos, SPRITE_WIDTH, SPRITE_HEIGHT);
g2d.dispose();
}
}
}
The example introduces concepts of Key Bindings, Swing Timer and basic custom painting