I am trying to make an animation where two rectangles will appear and disappear from frame when you type in: c with Turtle Graphics. But the problem I have is that I do not understand how to incorporate turtle graphics into a loop. For this, I have to use do-while loop. I am also suppose to have the rectangles that I made move horizontally across the screen. I have the general idea set out, but I do not know how to use turtle graphics with a loop. My code is not the most orderly when I tried to set it up here.
/**
* Write a description of class Animation here.
*
* #author (author)
* #version
*/
import java.util.Scanner;
import java.awt.*;
class Animation
{
//set conditions for turtle to start drawing
public void prepareTurtleToDraw(Turtle myrtle, Color color, int x, int y)
{
myrtle.hide();
myrtle.penUp(); //pick up the pen to avoid leaving a trail when moving the turtle
myrtle.setColor(color); //set myrtle's color
myrtle.moveTo(x, y); //move to coordinates
myrtle.penDown(); //put the pen down to start drawing
}//end of prepareTurtleToDraw method
//draw a line
public void drawLine(Turtle myrtle, int x1, int y1, int x2, int y2)//, int width)
{
myrtle.moveTo(x1, y1); //moves to this coordinate first
myrtle.moveTo(x2, y2); //then moves to this coordinate
//myrtle.setPenWidth(width); //this adjusts the size of the lines
}//end of drawLine method
public static void pressC()
{
String userInput = ""; //declare and initialize a String variable
char key = ' '; //declare and initialize a char variable
Scanner in = new Scanner(System.in); //construct a Scanner object
System.out.println("Please press the c key to watch the animation.");
//do-while loop to wait for the user to enter the letter c
do
{
userInput = in.next(); //accept one token from the keyboard
in.nextLine(); //flush the buffer
key = userInput.charAt(0); //picks off the first character from the userInput String variable
}
while(key != 'c'); //do-while condition statement
System.out.println("Thank you. You may continue");
}//end of main method
}
public class AnimationTester
{
public static void main(String[] args)
{
//Picture pictureObj = new Picture(""); //create a Picture object for the maze background image, has name and etc.
World worldObj = new World(); //create a World object to draw in
//worldObj.setPicture(pictureObj); //set the maze background image in the World object
Turtle lertle = new Turtle(300, 150, worldObj); //create a Turtle object to do the drawing
Animation turt = new Animation();
Turtle dyrtle = new Turtle(150, 150, worldObj);
turt.prepareTurtleToDraw(lertle, Color.BLACK, 250, 150);
turt.drawLine(lertle, 250, 150, 400, 150);
turt.drawLine(lertle, 400, 150, 400, 250);
turt.drawLine(lertle, 400, 250, 250, 250);
turt.drawLine(lertle, 250, 250, 250, 150);
turt.prepareTurtleToDraw(dyrtle, Color.RED, 150, 150);
turt.drawLine(dyrtle, 150, 150, 260, 75);
turt.drawLine(dyrtle, 260, 75, 335, 150);
turt.drawLine(dyrtle, 335, 150, 225, 225);
turt.drawLine(dyrtle, 225, 225, 150, 150);
System.out.println(worldObj);
}
}
Well, it seems I can't upload photos I took of my program because I don't have enough reputation. Thanks!
Figure 1. Before pressing C
Figure 2. After pressing C
I wrote this little program for you, it is running on Java Swing to create animations. I have three rectangles, two - red and blue - fading in and out according to how much seconds have elapsed, and the third appearing and disappearing upon pressing C.
I think what may be helpful for you when dealing with animations is a "game loop." You can find my implementation of it in MainFrame.java below. Loosely speaking, a game loop controls the update speed of animations so that the program runs consistently on both slow and fast computers. If a game loop is not implemented, a fast computer may finish an animation faster than a relatively slower computer.
This is a complete program, just compile the three .java files and run Main to bring up the game interface.
Main.java
import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;
public class Main {
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(() -> {
MainFrame mf = MainFrame.getMainFrame();
new Thread(mf).start();
});
} catch (InvocationTargetException | InterruptedException e) {
System.out.println("Could not create GUI");
}
}
}
MainFrame.java
import java.awt.BorderLayout;
import javax.swing.JFrame;
class MainFrame extends JFrame implements Runnable {
private static final long serialVersionUID = 1L;
private DisplayPanel dp;
private boolean isRunning;
private double secondsPerFrame = 1.0 / 60.0;
private static MainFrame mf;
private MainFrame() {
super("Title");
dp = new DisplayPanel();
add(dp, BorderLayout.CENTER); // Add display to the center
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
}
/**
* Static factory
*
* #return A singleton MainFrame
*/
static MainFrame getMainFrame() {
return mf == null ? mf = new MainFrame() : mf;
}
/**
* Game loop
*/
#Override
public void run() {
isRunning = true;
int frames = 0;
double frameCounter = 0;
double lastTime = System.nanoTime() / 1000000000.0;
double unprocessedTime = 0;
while(isRunning) {
boolean render = false;
double startTime = System.nanoTime() / 1000000000.0;
double passedTime = startTime - lastTime;
lastTime = startTime;
unprocessedTime += passedTime;
frameCounter += passedTime;
while(unprocessedTime > secondsPerFrame) {
render = true;
unprocessedTime -= secondsPerFrame;
// Update the state of the rectangles' brightness
dp.update(secondsPerFrame);
if(frameCounter >= 1.0) {
// Show fps count. Updates every second
dp.setFps(frames);
frames = 0;
frameCounter = 0;
}
}
if(render) {
// Render the rectangles
dp.render();
frames++;
} else {
try {
Thread.sleep(1);
} catch (InterruptedException ie) {}
}
}
}
}
DisplayPanel.java
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
// Create a display within the window
class DisplayPanel extends Canvas implements KeyListener {
private static final long serialVersionUID = 2L;
private Graphics2D g2; // Drawing tool
private BufferStrategy strategy; // Drawing tool
private FadingRectangle[] fadingRectangles; // Appears/disappears based on elapsed time
private FadingRectangle userControlledRectangle; // Appears/disappears upon command by user
private int fps; // Used to display the fps on screen
DisplayPanel() {
setPreferredSize(new Dimension(800, 600));
addKeyListener(this);
setFocusable(true);
fadingRectangles = new FadingRectangle[2];
fadingRectangles[0] = new FadingRectangle(150, 250, 100, 100);
fadingRectangles[1] = new FadingRectangle(550, 250, 100, 100);
userControlledRectangle = new FadingRectangle(350, 100, 100, 100);
}
/**
* Updates the brightness of rectangles
*
* #param elapsedSeconds Seconds elapsed since the last call to this method
*/
void update(double elapsedSeconds) {
fadingRectangles[0].update(elapsedSeconds);
fadingRectangles[1].update(elapsedSeconds);
}
/**
* Draw everything
*/
void render() {
// Prepare drawing tools
if (strategy == null || strategy.contentsLost()) {
createBufferStrategy(2);
strategy = getBufferStrategy();
Graphics g = strategy.getDrawGraphics();
this.g2 = (Graphics2D) g;
}
// Anti-aliasing
this.g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Clear screen by drawing background on top
this.g2.setColor(Color.BLACK);
this.g2.fillRect(0, 0, getWidth(), getHeight());
// Draw the rectangles
fadingRectangles[0].draw(new Color(255, 0, 0));
fadingRectangles[1].draw(new Color(0, 0, 255));
userControlledRectangle.draw(Color.WHITE);
// Draw fps count on the upper left corner
g2.setColor(Color.WHITE);
g2.drawString("FPS: " + Integer.toString(fps), 10, 20);
// Set the drawn lines visible
if(!strategy.contentsLost())
strategy.show();
}
/**
* #param fps The fps to be drawn when render() is called
*/
void setFps(int fps) {
this.fps = fps;
}
/**
* Used to draw rectangles in the display
*/
private class FadingRectangle {
private int x, y, width, height; // Location and size of the rectangle
private double secondsPassed; // Arbitrary number that determines the brightness of blue
private FadingRectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
/**
* Called by render()
*
* #param color The color of the rectangle to be drawn
*/
private void draw(Color color) {
// Determine color
double fade = Math.abs(Math.sin(secondsPassed));
int red = (int) (color.getRed() * fade);
int green = (int) (color.getGreen() * fade);
int blue = (int) (color.getBlue() * fade);
g2.setColor(new Color(red, green, blue));
// Draw the rectangle
g2.drawRect(x, y, width, height);
}
private void update(double elapsedSeconds) {
secondsPassed += elapsedSeconds;
}
}
// A quick and dirty implementation. Should be fixed to make it clearer
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_C) {
userControlledRectangle.update(Math.PI / 2);
}
}
#Override
public void keyPressed(KeyEvent e) {}
#Override
public void keyTyped(KeyEvent e) {}
}
Related
I looked through some of the other questions similar to mine, but none of them seemed of help when i tried their fixes, How would i go about fixing this as its the only thing stopping my programme from printing rectangles which is vital.
Below is the code for the entire programme as im not sure in where the problem lies, only what the problem is. The stuff that is commented I'm currently not using, but I'm keeping it in there just in case I find a use for it later on. Many thanks
Currently, my programme does not draw any sort of rectangle as it should, and its because repaint doesn't invoke the paintcomponent. My question is basically asking as to why nothing is printed when I start it.
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.logging.Logger;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
/**
* Write a description of class Game here.
*
* #author Adam Steele
* #version 1.0.0
*/
public class Game extends JPanel implements MouseListener
{
// logger
private static final Logger LOGGER = Logger.getLogger(Game.class.getName());
// variables
private boolean isGolden; // Class wide boolean for whether rectangle is golden
private int score; // Stores the score
private int noOfAttempts; // stores number of rectangles that are created
private int goldTimer; // attempts before fail & new rectangle is created
// graphics
private Rectangle box;
private JFrame frame;
private JPanel panel;
// testing
private ArrayList<Rect> rects = new ArrayList<Rect>();
/**
* Constructor for objects of class Game
*/
public Game()
{
setup();
}
public void setup()
{
LOGGER.info("setup has been called");
// initalise frame
frame = new JFrame();
final int FRAME_WIDTH = 800;
final int FRAME_HEIGHT = 600;
// set frame attributes
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("Gold Picker");
// initalise panel
panel = new JPanel();
panel.addMouseListener(this);
// add things to panel - ie. score, time limit, etc.
// panel.add();
JLabel amLabel = new JLabel("I am a GUI label.");
panel.add(amLabel);
JButton button = new JButton("Button");
button.setBackground(Color.YELLOW);
button.setForeground(Color.GREEN);
panel.add(button);
// add panel to frame
frame.add(panel);
// Make frame visible
showFrame();
LOGGER.info("Game window has been created");
// (re)set variables
score = 0;
noOfAttempts = 0;
goldTimer = 30;
// this is to check variable initalisation
LOGGER.info("Variables have been initalised at: score = " + score + ", noOfAttempts = " + noOfAttempts + ", goldTimer = " + goldTimer);
// start game
decideGolden();
}
public void showFrame()
{
frame.setVisible(true);
}
/**
* A method that decides if the rectangle will be golden based on a random chance
*/
public void decideGolden()
{
double goldProportion = Math.random() * 1;
double goldChance = Math.random() * 1;
//System.out.println(goldChance + " " + goldProportion);
if(goldChance <= goldProportion) {
isGolden = true;
} else {
isGolden = false;
}
LOGGER.info("isGolden has been set to " + isGolden);
timedRect();
}
/**
* Method for generating rectangled on timed intervals
*/
public void timedRect()
{
/*
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run()
{
}
}, 20000, 20000 );
/*
if(isGolden) {
repaint();
} else {
createNormalRectangle();
}
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
}
},
20000); */
LOGGER.info("timedRect has been called");
for(int i = 0; i < goldTimer; i++) {
try {
LOGGER.info("try has been reached");
//repaint();
drawRectangle();
Thread.sleep(20000); // wait 20 seconds..
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
LOGGER.info("paintComponent has been called with " + g);
/*
int x = 400;
int y = 300;
// Using Math.random() or .nextInt() we could make random gold rects
int width = 100;
int height = 100;
LOGGER.info("x = " + x + ", y = " + y + ", width = " + width + ", height = " + height);
if(isGolden) {
//g.drawRect(x, y, width, height);
g.setColor(Color.BLACK);
g.fillRect(x, y, 100, 100); // g.fillRect(x, y, size, size);
} else {
//g.drawRect(x, y, width, height);
g.setColor(Color.BLACK);
g.fillRect(x, y, 100, 100);
}
*/
Graphics2D g2d = (Graphics2D) g;
for(Rect rectangle : rects) { // for each Rect object in rect ArrayList ..
rectangle.paint(g2d);
}
}
/**
*
*/
public void drawRectangle()
{
int x = (int) (Math.random() * getWidth());
int y = (int) (Math.random() * getHeight());
int width = (int) (Math.random() * (getWidth() / 4));
int height = (int) (Math.random() * (getHeight() / 4));
// LOGGER.info("x = " + x + ", y = " + y + ", width = " + width + ", height = " + height);
if (x + width > getWidth()) {
x = getWidth() - width;
}
if (y + height > getHeight()) {
y = getHeight() - height;
}
Color color = new Color(
(int) (Math.random() * 255),
(int) (Math.random() * 255),
(int) (Math.random() * 255));
rects.add(new Rect(x, y, width, height, color));
repaint();
// LOGGER.info("repaint has been called, check if paintComponent has been called..");
}
/**
* these methods are needed to override the MouseListener
* ..and hence needed to implement the MouseListener
* ..which will probably only be used for testing
* ..actionlistener maybe more appropriate
*/
#Override
public void mouseClicked(MouseEvent e) {
LOGGER.info("Mouse has been clicked");
drawRectangle();
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
}
Rect class in case its needed
Rect class
import java.awt.*;
/**
* Deaals with misc properties of rect
* Inherits the Rectangle class from java.awt.Rectangle
public class Rect extends Rectangle
{
private Color color;
/**
* Constructor for objects of class Rect
*/
public Rect(int x, int y, int width, int height, Color color)
{
super(x, y, width, height);
this.color = color;
}
public void paint(Graphics2D g2d)
{
g2d.setColor(color);
g2d.fill(this);
}
}
Basically, you never actually add Game to anything which can display it, therefore it will never be painted.
Before a component can be painted, it must be added to a container which is realised on the screen.
One of the issues you're having is your Game class is taking on to much responsibility, it should be focused on displaying and managing the game state, not also creating the basic UI.
Another issue you're going to have is your timedRect method will block the EDT, preventing anything from getting painted anyway
This is a "basic" example (I've not tested it because I don't have your Rect class), but conceptually it should get you closer to your goal.
Realistically, I'd have a separate "main" class which started the app, setup the initial state, created the UI and gets the ball rolling.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import org.w3c.dom.css.Rect;
public class Game extends JPanel implements MouseListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
LOGGER.info("setup has been called");
// initalise frame
JFrame frame = new JFrame();
final int FRAME_WIDTH = 800;
final int FRAME_HEIGHT = 600;
// set frame attributes
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("Gold Picker");
// initalise panel
JPanel panel = new Game();
// add things to panel - ie. score, time limit, etc.
// panel.add();
JLabel amLabel = new JLabel("I am a GUI label.");
panel.add(amLabel);
JButton button = new JButton("Button");
button.setBackground(Color.YELLOW);
button.setForeground(Color.GREEN);
panel.add(button);
// add panel to frame
frame.add(panel);
frame.setVisible(true);
LOGGER.info("Game window has been created");
}
});
}
// logger
private static final Logger LOGGER = Logger.getLogger(Game.class.getName());
// variables
private boolean isGolden; // Class wide boolean for whether rectangle is golden
private int score; // Stores the score
private int noOfAttempts; // stores number of rectangles that are created
private int goldTimer; // attempts before fail & new rectangle is created
private int gameLoops = 0;
// graphics
private Rectangle box;
// testing
private ArrayList<Rect> rects = new ArrayList<Rect>();
/**
* Constructor for objects of class Game
*/
public Game() {
addMouseListener(this);
// (re)set variables
score = 0;
noOfAttempts = 0;
goldTimer = 30;
// this is to check variable initalisation
LOGGER.info("Variables have been initalised at: score = " + score + ", noOfAttempts = " + noOfAttempts + ", goldTimer = " + goldTimer);
decideGolden();
}
/**
* A method that decides if the rectangle will be golden based on a random
* chance
*/
public void decideGolden() {
double goldProportion = Math.random() * 1;
double goldChance = Math.random() * 1;
//System.out.println(goldChance + " " + goldProportion);
if (goldChance <= goldProportion) {
isGolden = true;
} else {
isGolden = false;
}
LOGGER.info("isGolden has been set to " + isGolden);
timedRect();
}
/**
* Method for generating rectangled on timed intervals
*/
public void timedRect() {
Timer timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (gameLoops < goldTimer) {
LOGGER.info("try has been reached");
//repaint();
drawRectangle();
gameLoops++;
} else {
((Timer) (e.getSource())).stop();
}
}
});
timer.start();
LOGGER.info("timedRect has been called");
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
LOGGER.info("paintComponent has been called with " + g);
Graphics2D g2d = (Graphics2D) g;
for (Rect rectangle : rects) { // for each Rect object in rect ArrayList ..
rectangle.paint(g2d);
}
}
/**
*
*/
public void drawRectangle() {
int x = (int) (Math.random() * getWidth());
int y = (int) (Math.random() * getHeight());
int width = (int) (Math.random() * (getWidth() / 4));
int height = (int) (Math.random() * (getHeight() / 4));
// LOGGER.info("x = " + x + ", y = " + y + ", width = " + width + ", height = " + height);
if (x + width > getWidth()) {
x = getWidth() - width;
}
if (y + height > getHeight()) {
y = getHeight() - height;
}
Color color = new Color(
(int) (Math.random() * 255),
(int) (Math.random() * 255),
(int) (Math.random() * 255));
rects.add(new Rect(x, y, width, height, color));
repaint();
LOGGER.info("repaint has been called, check if paintComponent has been called..");
}
/**
* these methods are needed to override the MouseListener ..and hence needed
* to implement the MouseListener ..which will probably only be used for
* testing ..actionlistener maybe more appropriate
*/
#Override
public void mouseClicked(MouseEvent e) {
LOGGER.info("Mouse has been clicked");
drawRectangle();
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public class Rect extends Rectangle {
private Color color;
/**
* Constructor for objects of class Rect
*/
public Rect(int x, int y, int width, int height, Color color) {
super(x, y, width, height);
this.color = color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.fill(this);
}
}
}
Why are you extending JPanel when you have an instance of JPanel? When you extend a class, you inherit all of its functionality.
Instead of using your instance "panel.add" whatever, get rid of your JPanel instance and use this.add.
Which leads to my next statement:
Your class should extend JComponent, you're not overriding paintcomponent because JPanel doesn't have a paintcomponent() method to override.
Read up on how to properly implement JComponent and JFrame and how they go together. Normally you add all your drawings to the JComponent, then you say frame.add(component), where component is of type JComponent.
Try reimplementing your code in this fashion and seperate your classes, now you don't have to follow this exact format but from what you've posted, I think you're lost.
Implement a class that extends JFrame, this will be your frame.
Implement a class that "knows" how to draw your rectangle shapes. You must put it in a method called draw(Graphics2D g2){}.
Implement a class that extends JComponent, this class may have an arraylist of your "rectangle" objects.
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
// initialize your rectangle objects here
for(Rectangles rects: whateveryourarraylistvariable) {
rects.draw(g2);
}
}
good luck
I'm pretty sure that this code should draw an oval on the screen next to the word text. However, the word is all the appears, the rest of the screen is black. This seems to happen with any primitive shape. I'd like to think I know java fairly well, but graphical things have been really confusing to me. I'm at my wit's end with this and any help would be appreciated.
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.util.ArrayList;
public class Game extends JPanel implements Runnable {
int W = 4;
int H = 3;
int windowSize = 300;
boolean running;
static boolean drawHitBoxes = true;
int FPSLimit = 30;
private Thread thread;
private BufferedImage buffer;
private Graphics2D g;
public Game() {
super();
setPreferredSize(new Dimension(W * windowSize, H * windowSize));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
public void run() {
running = true;
buffer = new BufferedImage(W * windowSize, H * windowSize,
BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) buffer.getGraphics();
// citList.add(new Citizen(200, 200, "Joe"));
long startTime;
long waitTime;
long frameTime = 1000 / FPSLimit; // /How long one frame should take
long currentFrameTime;
while (running) {
startTime = System.nanoTime(); // record when loop starts
gameUpdate();
gameRender();
gameDraw();
// Calculate how long the current frame took
currentFrameTime = (System.nanoTime() - startTime) / 1000000;
waitTime = frameTime - currentFrameTime;
try {
Thread.sleep(waitTime);
} catch (Exception e) {
} // Sleep for the remaining time
}
}
private void gameUpdate() {
// for(Citizen i:citList){i.update();} //Update citizens
}
private void gameRender() {
g.setColor(Color.WHITE);
g.drawOval(100, 100, W - 100, H - 100);
g.setColor(Color.WHITE);
g.drawString("Text.", 100, 100);
System.out.println("Drawing white box.");
// for(Citizen i:citList){i.draw(g);} //Draw citizens
}
private void gameDraw() {
Graphics gMain = this.getGraphics();
gMain.drawImage(buffer, 0, 0, null);
}
}
g.drawOval(100, 100, W-100, H-100);
W is 4 and H is 3, and so since W-100 is -96 and H-100 is -97, making your 3rd and 4th parameters negative, which doesn't make sense for the Graphics#drawOval(...) method since how can an oval's width and height be negative. Solution: be sure to use only positive parameters that make sense when calling this method. Probably what you want is:
// but you'll also want to avoid magic numbers such as 100 & 200 as well
g.drawOval(100, 100, W * windowSize - 200, H * windowSize - 200);
As an aside, myself, I prefer using passive graphics, drawing in paintComponent and am fearful whenever I see Swing code that has a Graphics or Graphics2D instance field.. Also your code looks to not obey Swing threading rules as it appears to be making Swing calls off of the Swing event thread.
I think it's better to create the paintComponent method and transfer your gameRender and gameDraw there and in your while loop replace their method calls with repaint(). Here is the code that works.
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.util.ArrayList;
public class Game extends JPanel implements Runnable {
int W = 4;
int H = 3;
int windowSize = 300;
boolean running;
static boolean drawHitBoxes = true;
int FPSLimit = 30;
private Thread thread;
private BufferedImage buffer;
private Graphics2D g;
public Game() {
super();
setPreferredSize(new Dimension(W * windowSize, H * windowSize));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
public void run() {
running = true;
buffer = new BufferedImage(W * windowSize, H * windowSize,
BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) buffer.getGraphics();
// citList.add(new Citizen(200, 200, "Joe"));
long startTime;
long waitTime;
long frameTime = 1000 / FPSLimit; // /How long one frame should take
long currentFrameTime;
while (running) {
startTime = System.nanoTime(); // record when loop starts
gameUpdate();
//gameRender();
//gameDraw();
repaint();
// Calculate how long the current frame took
currentFrameTime = (System.nanoTime() - startTime) / 1000000;
waitTime = frameTime - currentFrameTime;
try {
Thread.sleep(waitTime);
} catch (Exception e) {
} // Sleep for the remaining time
}
}
private void gameUpdate() {
// for(Citizen i:citList){i.update();} //Update citizens
}
private void gameRender() {
g.setColor(Color.WHITE);
//g.drawOval(100, 100, W - 100, H - 100);
g.drawOval(100, 100, 100, 100);
g.setColor(Color.WHITE);
g.drawString("Text.", 100, 100);
//System.out.println("Drawing white box.");
// for(Citizen i:citList){i.draw(g);} //Draw citizens
}
private void gameDraw(Graphics gMain) {
//Graphics gMain = this.getGraphics();
gMain.drawImage(buffer, 0, 0, null);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
gameRender();
gameDraw(g);
}
}
As point out by #Hovercraft in his answer, the W-100 and H-100 in the code `g.drawOval(100, 100, W - 100, H - 100);' will yield negative numbers. I don't know exactly what values do you want to result in there but I just replace them with 100 just to remove the bug.
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);
}
}
I'm trying to display a background image using g.drawImage within Canvas Jframe but i have nothing displayed and the screen still white , and when i try to fill the screen with any color it works using g.fillRect
here is the Main class :
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.ImageIcon;
public class Main extends Canvas implements Runnable, KeyListener {
private static final long serialVersionUID = 1L;
public static final int WIDTH = 600, HEIGHT = WIDTH / 12 * 9;
// rate to convert meters to pixels in the physics engine
public static final float RATE = 30;
// counts how many loops are made in the main thread
private int counter = 49;
// image and graphics used for double buffering
private ImageIcon bgImage = new ImageIcon("res/bgImage.png");
private Image dbImage;
private Graphics dbg;
/* boolean to define when the game will be started and stopped */
private boolean running = false;
// variables for the Box2D world
Vec2 gravity = new Vec2(0.0f, 10.0f);
boolean doSleep = true;
World world = new World(gravity, doSleep);
// new array list to hold Ball references
ArrayList<Ball> balls = new ArrayList<Ball>();
// create a new player
Player character = new Player(world);
public Main() {
addKeyListener(this);
// add a ground floor to our Box2D world
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.position.set(300.0f / RATE, 400.0f / RATE);
Body groundBody = world.createBody(groundBodyDef);
PolygonShape groundBox = new PolygonShape();
groundBox.setAsBox(300.0f / RATE, 0);
groundBody.createFixture(groundBox, 0.0f);
// wall fixture
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = groundBox;
fixtureDef.density = 2.0f;
fixtureDef.filter.groupIndex = -1;
// left wall
groundBodyDef.position.set(0.0f / RATE, 350.0f / RATE);
groundBody = world.createBody(groundBodyDef);
groundBox.setAsBox(0, 50.0f / RATE);
groundBody.createFixture(fixtureDef);
// right wall
groundBodyDef.position.set(600.0f / RATE, 350.0f / RATE);
groundBody = world.createBody(groundBodyDef);
groundBox.setAsBox(0, 50.0f / RATE);
groundBody.createFixture(fixtureDef);
/**
* #WIDHT : width of jpanel screen
* #HEIGHT : height of the jpanel screen
* #param : title of jpanel screen
* #this : refered to our main game instance
*/
new Window(WIDTH, HEIGHT, "Apocalypse 2D v0.0", this);
}
#Override
public int getWidth() { // Width of the CustomPanel
return WIDTH;
}
#Override
public int getHeight() { // Height of the CustomPanel
return HEIGHT;
}
#Override
public Dimension getPreferredSize() { // Dimension of the CustomPanel
return new Dimension(getWidth(), getHeight());
}
public void start() {
// starts a new thread
running = true;
Thread th = new Thread(this);
th.start();
}
public void stop() {
running = false;
}
public void destroy() {
}
public void run() {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (running) {
counter++;
// Simulate the world
float timeStep = 1.0f / 60.0f;
int velocityIterations = 6;
int positionIterations = 2;
world.step(timeStep, velocityIterations, positionIterations);
// add new balls to the world every 50th loop
if (counter % 80 == 0)
balls.add(new Ball(world));
repaint();
// pause for 10 milliseconds
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
// do nothing
}
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
}
}
public void paint(Graphics g) {
/*g.setColor(Color.black);*/
/*g.fillRect(0, 0, getWidth(), getHeight());*/
g.drawImage(bgImage, getWidth(), getHeight(), null);
// loop through each ball and call it's draw method
Iterator<Ball> itr = balls.iterator();
while (itr.hasNext()) {
Ball b = itr.next();
b.DrawBall(g);
// if the ball should be removed then remove it
if (b.shouldDelete())
itr.remove();
}
// draw the main character.
character.draw(g);
}
// sets up double buffering for graphics
public void update(Graphics g) {
if (dbImage == null) {
dbImage = createImage(this.getSize().width, this.getSize().height);
dbg = dbImage.getGraphics();
}
dbg.setColor(getBackground());
dbg.fillRect(0, 0, this.getSize().width, this.getSize().height);
dbg.setColor(getForeground());
paint(dbg);
g.drawImage(dbImage, 0, 0, this);
character.animplayer();
for (Ball ball : balls) {
ball.animBalls();
}
}
public void keyPressed(KeyEvent key) {
character.keyPress(key);
}
public void keyReleased(KeyEvent key) {
character.keyRelease(key);
}
public void keyTyped(KeyEvent key) {
}
public static void main(String[] args) {
new Main();
}
}
and this is the paint method where i'm trying to fill the image to the screen :
public void paint(Graphics g) {
/*g.setColor(Color.black);*/
/*g.fillRect(0, 0, getWidth(), getHeight());*/
g.drawImage(bgImage, getWidth(), getHeight(), null);
// loop through each ball and call it's draw method
Iterator<Ball> itr = balls.iterator();
while (itr.hasNext()) {
Ball b = itr.next();
b.DrawBall(g);
// if the ball should be removed then remove it
if (b.shouldDelete())
itr.remove();
}
// draw the main character.
character.draw(g);
}
The drawImage() method has several overloads, the one you are using is drawImage(BufferedImage img, int x, int y, ImageObserver observer), x and y being the top left coordinates on the canvas. Right now those are the width and the heigh of the canvas, so the image is being drawn outside of the panel. Try calling g.drawImage(dbImage, 0, 0, null).
edit:
Asuming your image is not the same size as your canvas, use g.drawImage(dbImage, 0, 0, getWidth(), getHeight(), null) to fill the canvas with your image.
I'm trying to paint a new image of the same image to the screen after every 10 second delay interval. I've been trying to figure it out myself and I also looked things up but can't find anything that helps.
My specific problem is that I don't know how to paint a new image using the original image. I want to make it so every 10 seconds the new image should appear and start following it's ID array which then follows it's own x y coordinates (IDx[0]=x; IDx[0]=y;)
A plain g.drawImage in Graphics for every new image is ruled out because I need it to draw new images/bots until the condition is met for them to stop. But the aim is the same just in an automatic way.
My window:
package pack;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Window extends JPanel implements Runnable {
public static final int GAME_WIDTH = 512;
public static final int GAME_HEIGHT = GAME_WIDTH * 3 / 4;
public static final int SCALE = 2;
public static Image Player = Controls.Player;
public static Image BB = ImageLoader.BB;
public static Image Grass = ImageLoader.Grass;
public static boolean painting = true;
public static boolean Start = true;
public static boolean Menu;
public static boolean ONhs;
public static boolean OFFhs;
public void paint(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.drawString("UPS: " + Integer.toString(UPS.ups), 100, 120);
g.drawString("FPS: " + Integer.toString(UPS.fps), 100, 130);
// // MENU ////
if (Menu) {
Image Grass = ImageLoader.Grass;
Image onHoverStart = ImageLoader.onHoverStart;
Image offHoverStart = ImageLoader.offHoverStart;
g.drawImage(Grass, GAME_WIDTH * SCALE / 4, GAME_HEIGHT * SCALE / 6, null);
if (ONhs) {
g.drawImage(onHoverStart, 400, 250, null);
} else {
g.drawImage(offHoverStart, 400, 250, null);
}
}
// // STARTED // //
if (Start) {
Image Player = Controls.Player;
Image BB = ImageLoader.BB;
g.drawImage(BB, PlayerData.BBDirectionx, PlayerData.BBDirectiony, null);
g.drawImage(ImageLoader.Bot[0], Mob.BotxSELF - 32, Mob.BotySELF - 25, null);
g.drawImage(ImageLoader.Bot[1], Mob.BotxSELF - 32, Mob.BotySELF - 25, null);
g.drawImage(Player, Controls.Playerx - 45, Controls.Playery + 5, null);
}
}
#Override
public void run() {
while (painting) {
UPS.fpstick = UPS.fpstick + 1;
repaint();
try {
Thread.sleep(12);
} catch (InterruptedException e) {
}
}
}
public static void draw(Graphics g) {
g.drawImage(ImageLoader.Bot[0], 32, 25, null);
}
public Window() {
JFrame jf = new JFrame();
JPanel panel = new JPanel(new BorderLayout());
Controls c = new Controls("Window using Controls for Listener");
panel.add(this);
this.setPreferredSize(new Dimension(GAME_WIDTH * SCALE, GAME_HEIGHT * SCALE));
this.setMinimumSize(new Dimension(GAME_WIDTH * SCALE, GAME_HEIGHT * SCALE));
this.setMaximumSize(new Dimension(GAME_WIDTH * SCALE, GAME_HEIGHT * SCALE));
this.setBackground(Color.DARK_GRAY);
jf.setContentPane(panel);
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(GAME_WIDTH * SCALE, GAME_HEIGHT * SCALE);
jf.setVisible(true);
jf.setResizable(false);
jf.addKeyListener(c);
jf.addMouseListener(c);
jf.addMouseMotionListener(c);
}
}
Help is very much appreciated!
Always invoke super.XXX() of the method you override. You should be overriding paintComponent():
//public void paint(Graphics g) {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Don't use a Thread.sleep() for animation. Instead you should be using a Swing Timer.
A plain g.drawImage in Graphics for every new image is ruled out because I need it to draw new images/bots
You can use the same BufferedImage. When the Timer fires you update the image. Then the paintComponent() method just paints the updated image.
You already have the structures necessary to draw in a separate thred, why don't you use them? This looks too much like homework to me...