I have a Problem, I tried to build up a code to visualise Functions.
package main;
import java.awt.*;
import java.awt.Graphics;
import javax.swing.JPanel;
public class MyPanel extends JPanel{
private static final long serialVersionUID = -6649271997955423098L;
#Override
public void paintComponent(Graphics g)
{
//super.paintComponents(g); only show code how it works
//g.setColor(Color.cyan);
//g.fillRect(10, 20, 35, 78);
paintStuff(g);
paintBackgroundComponents(g); // the navigation cross in the background
paintFunctions(g); //Both
leftPaintLineFunction(g); //with test Functions
rightPaintLineFunction(g);
}
/**
* Own Method for drawing lines. Needs Graphics g!
* #param g
*/
private void paintStuff(Graphics g)
{
g.setColor(Color.black);
g.drawString("Integral Veranschaulichung", (Main.length)-(Main.length/3),((Main.width) - (Main.width/12)));
}
private void paintBackgroundComponents(Graphics g)
{
g.drawLine((Main.length)/2, Main.width, (Main.length)/2, 0);
g.drawLine(0, (Main.width)/2, Main.length, (Main.width)/2);
g.setColor(Color.red);
g.drawString("X", (Main.length-(Main.length/8)) , ((Main.width/2) - (Main.width/80)));
g.drawString("Y", ((Main.length/2)+(Main.length/80)) , ((Main.width) - (Main.width/8)));
}
private void paintFunctions(Graphics g)
{
g.setColor(Color.blue);
for(int x=(0 - Main.length); x < Main.length; x++) //fills all possible values (limited through length)
{
g.drawOval((x) + (Main.length/2-1), Functions.solveTestFunction(x)+(Main.width/2-1), 3, 3);
}
}
//needs different methods for left and right Side
private void leftPaintLineFunction(Graphics g)
{
int [] pointOneX = new int [Main.length*2];
int [] pointOneY = new int [Main.length*2];
for(int x = 0; x < Main.length; x++)
{
pointOneX[x] = (((x)*(-1)) + (Main.length/2-1));
pointOneY[x]= (Functions.solveTestFunction(x) + (Main.width/2-1));
System.out.print(pointOneX[x]+" ");
System.out.print(pointOneY[x]);
}
g.drawPolyline(pointOneX, pointOneY, 100);
}
private void rightPaintLineFunction(Graphics g)
{
int [] pointOneX = new int [Main.length*2];
int [] pointOneY = new int [Main.length*2];
for(int x = 0; x < Main.length; x++)
{
pointOneX[x] = ((x) + (Main.length/2+2)); //no clue why 2?
pointOneY[x]= (Functions.solveTestFunction(x) + (Main.width/2));
System.out.print(pointOneX[x]+" ");
System.out.print(pointOneY[x]);
}
g.drawPolyline(pointOneX, pointOneY, 100);
}
}
My other class is:
package main;
public class Functions
{
int x = 0;
public static int solveTestFunction(int x) //simpel test function
{
int y=0;
y = (x^2)*(-1);//-1 cause the gui uses changed sides
return y;
}
}
And my main method is this one:
package main;
import javax.swing.JFrame;
public class Main {
static int length = 1000; // Playgrounds measures
static int width = 1000;
public static void main(String[] args)
{
JFrame frame = new JFrame("My Drawings");
MyPanel panel = new MyPanel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(length, width); //playground
frame.add(panel);
frame.setVisible(true);
}
}
I split the method rightpaintLineFUnctions() in left and right. Cause that was the only way I get them to work. When I tried x*x for my testFunction everything worked quit good. But since Iwrote it in this form x^2 it didn't worked correct. Some help would be very nice.
Related
Edited as an MRE. I wasn't really sure how to write the code without extending JFrame or JPanel. This will reproduce the same error I am seeing. I am trying to render bars on the JPanel, but it seems that only the last iteration of the for loop in the PlotPanel class is being drawn.
package com.company;
import java.awt.*;
import javax.swing.*;
public class VisualizeAlgorithms {
public static int initPosX = 0;
public static int initPosY = 0;
public static int numBars = 200;
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
PlotFrame frame = new PlotFrame();
});
}
}
class PlotFrame extends JFrame {
PlotPanel plotPanel;
PlotFrame() {
plotPanel = new PlotPanel();
this.add(plotPanel);
this.setTitle("Plot");
this.setBackground(Color.DARK_GRAY);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
this.setResizable(false);
this.setLocationRelativeTo(null);
}
public int[] createArray(int numBars) {
int[] numsArray = new int[numBars];
for (int i = 0; i < numBars; i++) {
numsArray[i] = i + 1;
}
return numsArray;
}
}
class PlotPanel extends JPanel{
static final int PLOT_WIDTH = 1200;
static final int PLOT_HEIGHT = 800;
static final int MAX_BAR_HEIGHT = PLOT_HEIGHT;
static final int BAR_WIDTH = PLOT_WIDTH / VisualizeAlgorithms.numBars;
Dimension plotSize = new Dimension(PLOT_WIDTH, PLOT_HEIGHT);
PlotPanel() {
this.setPreferredSize(plotSize);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.CYAN);
draw(g);
}
public void draw(Graphics g){
int numBars = VisualizeAlgorithms.numBars;
for (int i = 0; i < numBars; i++) {
g.fillRect(BAR_WIDTH * i, VisualizeAlgorithms.initPosY, BAR_WIDTH, ((i+1)/numBars)*(MAX_BAR_HEIGHT));
}
}
}
Not sure if I can accept comments as answers, so I posted the answer here. It was indeed changing the rectangle height to int h = ((i+1)*MAX_BAR_HEIGHT/numBars). Thanks both to #AndrewThompson and #c0der not only for the answer but the additional information as well.
Aside: #c0der gave great advice re "when in doubt, print out". I recommend using this change:
public void draw(Graphics g){
int numBars = VisualizeAlgorithms.numBars;
for (int i = 0; i < numBars; i++) {
int x = BAR_WIDTH * I;
int y = VisualizeAlgorithms.initPosY;
int w = BAR_WIDTH;
int h = ((i+1)/numBars)*(MAX_BAR_HEIGHT);
System.out.println(String.format("x,y WxH: %1s,%1s %1sx%1s", x,y,w,h));
g.fillRect(x,y,w,h);
}
}
(print the values used for fillRect)
I am making an asteroid game. Every so often an asteroid needs to be generated and fly across the screen. For some reason when more then 1 asteroid is created, the screen glitches out. If you maximize the screen you will be able to see the glitching. I have tried using paint instead of paintComponent. I have also tried extending JFrame instead of JPanel but that just makes it worse. The class below sets up the screen and handles the game loop
public class Game extends JPanel {
static ArrayList<Asteroids> rocks = new ArrayList<Asteroids>();
//This variable determines whether the game should keep running
static boolean running = true;
//Counter to access arraylist
static int counter = 0;
public static void main(String[] args) throws InterruptedException {
//Creating the window
JFrame frame = new JFrame("Asteroid Game");
frame.getContentPane().setBackground(Color.BLACK);
frame.setSize(1100, 1000);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Asteroids a = new Asteroids();
frame.add(a);
//Game loop
while(running) {
if(counter % 4 == 0) {
rocks.add(new Asteroids());
frame.add(rocks.get(rocks.size() - 1));
}
for(int i = 0; i < rocks.size(); i++) {
rocks.get(i).repaint();
rocks.get(i).move();
if(!rocks.get(i).isPosFine()) {
rocks.remove(i);
i--;
}
}
Thread.sleep(17);
counter++;
}
}
}
The class below sets up the asteroids
public class Asteroids extends JPanel {
//These arrays store the coordinates of the asteroid
private int[] xPos = new int[8];
private int[] yPos = new int[8];
//Determines whether asteroid should be generated from top or bottom
private int[] yGen = {-100, 1100};
//Determines the direction the asteroid shold go
int genLevel;
/**
* #param g Graphics
* This method paints the asteroid
*/
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D r = (Graphics2D)g;
r.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
r.setColor(Color.decode("#52575D"));
r.fillPolygon(xPos, yPos, 8);
}
/**
* This constructor sets up the asteroid location points
*/
public Asteroids() {
int x = (int)(Math.random() * (700 - 1) + 100);
int y = yGen[(int)(Math.random() * (1 + 1 - 0))];
updateAsteroids(x, y);
genLevel = y;
System.out.println("Created!");
}
/**
* #param x int
* #param y int
* This method generates the asteroid based on the points passed in
*/
public void updateAsteroids(int x, int y) {
xPos[0] = x;
xPos[1] = x + 20;
xPos[2] = x + 40;
xPos[3] = x + 35;
xPos[4] = x + 40;
xPos[5] = x + 4;
xPos[6] = x - 16;
xPos[7] = x - 20;
yPos[0] = y;
yPos[1] = y + 7;
yPos[2] = y + 20;
yPos[3] = y + 40;
yPos[4] = y + 80;
yPos[5] = y + 70;
yPos[6] = y + 40;
yPos[7] = y;
}
/**
* This moves the asteroid
*/
public void move() {
int moveSpeedx = (int)(Math.random() * (10 - 1) + 1);
int moveSpeedy = (int)(Math.random() * (10 - 1) + 1);
for(int i = 0; i < 8; i++) {
if(genLevel > 0) {
xPos[i] -= moveSpeedx;
yPos[i] -= moveSpeedy;
}
else {
xPos[i] += moveSpeedx;
yPos[i] += moveSpeedy;
}
}
}
/**
* #return if the asteroid should be kept on the screen or not
*/
public boolean isPosFine() {
for(int i = 0; i < 8; i++) {
if(xPos[i] > 1250 || xPos[i] < -150)
return false;
if(yPos[i] > 1250 || yPos[i] < - 150)
return false;
}
return true;
}
}```
Your biggest problem that I can see is that you made your Asteroids class extend JPanel, making it much heavier weight than it should be, and making it difficult for more than one to show and for them to interact well and easily.
I recommend that you:
Make Asteroid a non-component logical class,
one that knows how to draw itself by giving it a public void draw(Graphics2D g2) method
one that knows how to move itself in response to your game loop's tick
Create one JPanel just for drawing the entire animation
Give this JPanel a collection of Asteroid objects, say in an ArrayList
In this JPanel's paintComponent, loop through all Asteroids in the collection, calling each one's draw(...) method
Drive the whole animation in a Swing thread-safe and controllable way using a Swing Timer. In this timer's actionPerformed, tell each asteroid to move, and then call repaint() on the drawing JPanel
Don't call .repaint() within the loop, but rather after the loop is finished
Create a small BufferedImage sprite from your Shapes, and draw those as the asteroid
A simple example illustrating what I mean:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class Game2 extends JPanel {
private static final int PREF_W = 1000;
private static final int PREF_H = 800;
private static final int TIMER_DELAY = 20;
private List<Asteroid2> asteroids = new ArrayList<>();
public Game2() {
setBackground(Color.BLACK);
int rows = 5;
int cols = 5;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
Asteroid2 asteroid = new Asteroid2();
asteroid.setX(j * (PREF_W / cols));
asteroid.setY(i * (PREF_H / rows));
asteroids.add(asteroid);
}
}
new Timer(TIMER_DELAY, e -> {
for (Asteroid2 asteroid2 : asteroids) {
asteroid2.move();
}
repaint();
}).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Asteroid2 asteroid : asteroids) {
asteroid.draw(g);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
Game2 mainPanel = new Game2();
JFrame frame = new JFrame("Game2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class Asteroid2 {
private static final int[] POLY_X = { 20, 40, 60, 55, 60, 24, 4, 0 };
private static final int[] POLY_Y = { 0, 7, 20, 40, 80, 70, 40, 0 };
private static final Color ASTEROID_COLOR = Color.decode("#52575D");
private Image image;
private int x;
private int y;
public Asteroid2() {
Polygon poly = new Polygon(POLY_X, POLY_Y, POLY_X.length);
image = new BufferedImage(60, 80, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D) image.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(ASTEROID_COLOR);
g2.fill(poly);
g2.dispose();
}
public void move() {
x++;
y++;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void draw(Graphics g) {
if (image != null) {
g.drawImage(image, x - 20, y, null);
}
}
}
I'm begining with JFrame, I'm triying to make a StarField, for the moment I'm adding the Star JComponent to the Starfield JFrame:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
public class Star extends JComponent{
public int x;
public int y;
private final Color color = Color.YELLOW;
public Star(int x, int y) {
this.x = x;
this.y = y;
}
public void paintComponent(Graphics g) {
g.setColor(color);
g.fillOval(x, y, 8, 8);
}
}
and the StarField code:
import javax.swing.*;
public class StarField extends JFrame{
public int size = 400;
public Star[] stars = new Star[50];
public static void main(String[] args) {
StarField field = new StarField();
field.setVisible(true);
}
public StarField() {
this.setSize(size, size);
for (int i= 0; i< stars.length; i++) {
int x = (int)(Math.random()*size);
int y = (int)(Math.random()*size);
stars[i] = new Star(x,y);
this.add(stars[i]);
}
}
}
The problem it's thar it only print one star, I think it is the last one, the coords are working like they are supposed to do it, so I think the mistake is in the JComponent or JFrame implementation, I'm self-learning, so maybe my code isn't the correct way for using swing.
Thank you, and sorry for my english, I'd tried to write it the best I know.
In your case you cannot use a layout manager, and need to reset it to null. See the my code below
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class StarField extends JFrame {
public int size = 400;
public Star[] stars = new Star[50];
public static void main(String[] args) {
StarField field = new StarField();
field.setVisible(true);
}
public StarField() {
this.setSize(size, size);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// usually you should use a normal layout manager, but for your task we need null
getContentPane().setLayout(null);
for (int i = 0; i < stars.length; i++) {
int x = (int) (Math.random() * size);
int y = (int) (Math.random() * size);
stars[i] = new Star(x, y);
this.add(stars[i]);
}
}
public class Star extends JComponent {
private final Color color = Color.YELLOW;
public Star(int x, int y) {
// need to set the correct coordinates
setBounds(x, y, 8, 8);
}
#Override
public void paintComponent(Graphics g) {
g.setColor(color);
g.fillOval(0, 0, getWidth(), getHeight());
}
}
}
In a Java applet, I'm trying to slow down the painting of an image made up of parts, so I wrote a test program to get the basic concept working. I'm using a thread to draw a number of boxes one at a time instead of a timer because I want to be able to click the go button to reset the drawing process at any time.
The problem is, after drawing a box, it moves down a bit and an extra of the label shows up at the top of the screen. When the mouse moves off the button at the bottom, a dummy button also shows up at the top of the screen. The dummy button doesn't respond to clicks (only the real one at the bottom does), it's just there.
I'm still pretty new at this, so any help would be greatly appreciated.
Here's the JApplet class:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class TestDraw extends JApplet implements ActionListener
{
private DrawPanel panel;
private JLabel lbl1;
JButton go;
Thread t;
public void init()
{
lbl1 = new JLabel("hi");
go = new JButton("GO");
go.addActionListener(this);
panel = new DrawPanel();
getContentPane().setBackground(Color.yellow);
add(lbl1, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
add(go, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent ae){
// tried adding these. didnt help
//panel.validate();
//panel.repaint();
//validate();
panel.resetBoxes();
repaint();
}
public void start(){
t = new Thread(panel);
t.start();
}
}
Here's the DrawPanel Class:
import java.awt.*;
import java.security.SecureRandom;
import javax.swing.*;
public class DrawPanel extends JPanel implements Runnable
{
private SecureRandom randGen = new SecureRandom();
private Box[] boxes;
private int box2draw = 0;
public DrawPanel()
{
setBackground(Color.WHITE);
boxes = new Box[5];
for (int count = 0; count < boxes.length; count++){
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256), randGen.nextInt(256));
boxes[count] = new Box(x,y,w,h,color);
}
}
public void paintComponent(Graphics g)
{
boxes[box2draw].draw(g);
box2draw++;
}
public void resetBoxes(){
boxes = new Box[5];
for (int count = 0; count < boxes.length; count++){
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256), randGen.nextInt(256));
boxes[count] = new Box(x,y,w,h,color);
box2draw = 0;
}
}
public void run(){
while(true){
try{
Thread.sleep(750);
}
catch(InterruptedException e){
JOptionPane.showMessageDialog(null, "interrupted");
}
repaint();
}
}
}
And finally, the Box class:
import java.awt.Color;
import java.awt.Graphics;
public class Box
{
private int x;
private int y;
private int w;
private int h;
private Color color;
public Box(int x,int y,int w,int h,Color color)
{
// initialise instance variables
this.x = x;
this.y=y;
this.w=w;
this.h = h;
this.color=color;
}
public void draw(Graphics g)
{
g.setColor(color);
g.drawRect( x, y, w, h);
}
}
Thank you for your time!
Problems:
You've got code logic within a painting method -- something that you should never do -- including your incrementing an array index. You don't have full control of when or even if this method is called and so program logic does not belong there, just painting. If you need to increment your array index, do it elsewhere, perhaps within your thread's while (true) loop. Also take care not to have the index go beyond the size of the array.
You never call the super's paintComponent method within your override, and this will prevent the component from doing housekeeping painting, probably your main problem.
If you need to display multiple items, then consider either drawing to a BufferedImage and displaying that within paintComponent, or creating a collection of Shape objects and drawing all of them within paintComponent via a for-loop.
I prefer to use the Swing-safer Swing Timer. While it doesn't matter if only calling repaint() if you want to make any other Swing calls intermittently, it makes life much easier and coding safer.
For example
package foo1;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestDraw2 {
#SuppressWarnings("serial")
private static void createAndShowGui() {
final DrawPanel2 drawPanel = new DrawPanel2();
JButton drawButton = new JButton(new AbstractAction("Draw!") {
#Override
public void actionPerformed(ActionEvent e) {
drawPanel.resetBoxes();
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(drawButton);
JFrame frame = new JFrame("TestDraw2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(drawPanel);
frame.getContentPane().add(btnPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class DrawPanel2 extends JPanel {
private static final int BOX_COUNT = 5;
private static final int TIMER_DELAY = 750;
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private Random randGen = new Random();
private Box[] boxes;
private int box2draw = 0;
public DrawPanel2() {
setBackground(Color.YELLOW);
boxes = new Box[BOX_COUNT];
for (int count = 0; count < boxes.length; count++) {
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256),
randGen.nextInt(256));
boxes[count] = new Box(x, y, w, h, color);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < box2draw; i++) {
boxes[i].draw(g);
}
}
public void resetBoxes() {
boxes = new Box[BOX_COUNT];
for (int count = 0; count < boxes.length; count++) {
int x = randGen.nextInt(300);
int y = randGen.nextInt(300);
int w = randGen.nextInt(300);
int h = randGen.nextInt(300);
Color color = new Color(randGen.nextInt(256), randGen.nextInt(256),
randGen.nextInt(256));
boxes[count] = new Box(x, y, w, h, color);
box2draw = 0;
}
repaint();
new Timer(TIMER_DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
box2draw++;
if (box2draw > BOX_COUNT) {
box2draw = BOX_COUNT;
((Timer) e.getSource()).stop();
}
repaint();
}
}).start();
}
}
I'm new enough to Java and I'm learning Game Design at the minute. I'm kind of at the beginning so its not a Game really.
I'm dealing with a single thread and an array of numbers.I have to get multiple square shapes moving around a screen at the same time. I have the code running fine for one square but run into trouble when I try implement it to multiple squares. I'm working with two Interfaces and I don't know how to use the Array to make multiple cases of a single random square. Any help would really be appreciated.
Cheers guys.
import java.awt.*;
import javax.swing.*;
public class MovingSquares extends JFrame implements Runnable
{
private static final Dimension WindowSize = new Dimension(600, 600);
private static final int NUMGAMEOBJECTS = 30;
private GameObject[] GameObjectsArray = new GameObject[NUMGAMEOBJECTS];
static int strtCoX = (int) (Math.random() * 600);
static int strtCoY = (int) (Math.random() * 600);
public MovingSquares() {
this.setTitle("Crazy squares");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width/2 - WindowSize.width/2;
int y = screensize.height/2 - WindowSize.height/2;
setBounds(x, y, WindowSize.width, WindowSize.height);
setVisible(true);
Thread t = new Thread(this);
t.start();
}
public void run() {
while (true) {
try {
Thread.sleep(20);
} catch (InterruptedException e) { }
GameObject.move();
this.repaint();
}
}
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, WindowSize.width, WindowSize.height);
int size = 50;
int R = (int) (Math.random() * 256);
int G = (int) (Math.random() * 256);
int B = (int) (Math.random() * 256);
Color c = new Color(R, G, B);
g.setColor(c);
g.fillRect(strtCoX, strtCoY, size, size);
}
public static void main(String[] args) {
MovingSquares M = new MovingSquares();
}
}
Second Interface - GameObject class.
import java.awt.*;
public class GameObject
{
private static double x;
private static double y;
private Color c;
public static int ranCoX = (int) (Math.random() * 20);
public static int ranCoY = (int) (Math.random() * 20);
public GameObject() {
int strtX = (int) x;
int strtY = (int) y;
int speedX = (int) (Math.random() * 10);
int speedY = (int) (Math.random() * 10);
}
public static void move() {
int velX = (int) (Math.random() * ranCoX);
int velY = (int) (Math.random() * ranCoY);
if (MovingSquares.strtCoY > 600)
MovingSquares.strtCoY = MovingSquares.strtCoY - 550;
else if (MovingSquares.strtCoX > 600)
MovingSquares.strtCoX = MovingSquares.strtCoX - 550;
MovingSquares.strtCoX += velX;
MovingSquares.strtCoY += velY;
}
public static void paint(Graphics g) {
int size = 50;
x = (Math.random() * 600);
y = (Math.random() * 600);
int R = (int) (Math.random() * 256);
int G = (int) (Math.random() * 256);
int B = (int) (Math.random() * 256);
Color c = new Color(R, G, B);
g.setColor(c);
g.fillRect((int)x, (int) y, size, size);
}
}
Here's a version of Moving Squares that hopefully will help you understand how to do an animation.
One of the first things I did was to separate the classes into either a model class, a view class, or a controller class. The model / view / controller pattern helps to separate concerns.
Let's look at the revised version of your model class, the GameObject class.
package com.ggl.moving.squares;
import java.awt.Color;
import java.awt.Graphics;
public class GameObject {
private static final int size = 50;
private double x;
private double y;
private double dx;
private double dy;
private int drawingWidth;
public GameObject(int drawingWidth) {
x = Math.random() * drawingWidth;
y = Math.random() * drawingWidth;
dx = Math.random() * 30D - 15D;
dy = Math.random() * 30D - 15D;
this.drawingWidth = drawingWidth;
}
public void move() {
int lowerLimit = size;
int upperLimit = drawingWidth - size;
x += dx;
if (x < lowerLimit) {
x += upperLimit;
} else if (x > upperLimit) {
x -= upperLimit;
}
y += dy;
if (y < lowerLimit) {
y += upperLimit;
} else if (y > upperLimit) {
y -= upperLimit;
}
}
public void draw(Graphics g) {
g.setColor(generateRandomColor());
g.fillRect((int) x, (int) y, size, size);
}
private Color generateRandomColor() {
int R = (int) (Math.random() * 256);
int G = (int) (Math.random() * 256);
int B = (int) (Math.random() * 256);
Color c = new Color(R, G, B);
return c;
}
}
There's only one static field left, the size of the square. Everything else is a dynamic variable or method. This allows us to create more than one instance of the GameObject.
I changed the name of the drawing method from paint to draw. This is so we don't get confused about which methods are Swing methods, and which methods are our methods.
We pass the width of the drawing panel into the constructor. That way, we only have to define the width in one place. You can see in the move method that we allow a margin the size of the square in the drawing area.
The constructor defines a random initial position and initial velocity. Th move method merely keeps the square moving in a straight line.
Next, let's look at the revised version of your main class, the MovingSquares class.
package com.ggl.moving.squares;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class MovingSquares implements Runnable {
private static final int DRAWING_WIDTH = 600;
private static final int NUMGAMEOBJECTS = 30;
private GameObject[] gameObjectsArray = new GameObject[NUMGAMEOBJECTS];
private JFrame frame;
private MovingPanel movingPanel;
private ObjectsRunnable objectsRunnable;
public MovingSquares() {
for (int i = 0; i < gameObjectsArray.length; i++) {
gameObjectsArray[i] = new GameObject(DRAWING_WIDTH);
}
}
#Override
public void run() {
frame = new JFrame();
frame.setTitle("Crazy squares");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent event) {
exitProcedure();
}
});
movingPanel = new MovingPanel(gameObjectsArray, DRAWING_WIDTH);
frame.add(movingPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
objectsRunnable = new ObjectsRunnable(this, gameObjectsArray);
new Thread(objectsRunnable).start();
}
private void exitProcedure() {
objectsRunnable.setRunning(false);
frame.dispose();
System.exit(0);
}
public void repaintMovingPanel() {
movingPanel.repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingSquares());
}
}
We define the width of the drawing panel here, as well as an array to hold the game objects.
We start the Swing application on the Event Dispatch thread (EDT) by invoking the SwingUtilities invokeLater method. A Swing application must always start on the EDT.
We create the game objects in the constructor and create the Swing components in the run method. I moved the drawing panel into its own class, which we'll talk about later.
We use a window listener so we can stop the thread when we're done with the application.
We pack the JFrame. The only place we're specifying a size is when we create the drawing panel. We use a JFrame. The only time you extend a Swing component, or any Java class, is when you want to override one of the methods.
Let's look at the drawing panel class, the MovingPanel class.
package com.ggl.moving.squares;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class MovingPanel extends JPanel {
private static final long serialVersionUID = -6291233936414618049L;
private GameObject[] gameObjectsArray;
public MovingPanel(GameObject[] gameObjectsArray, int width) {
this.gameObjectsArray = gameObjectsArray;
setPreferredSize(new Dimension(width, width));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
for (int i = 0; i < gameObjectsArray.length; i++) {
gameObjectsArray[i].draw(g);
}
}
}
We override the paintComponent method to draw on the JPanel. Since the game objects draw themselves, we call the draw method on each game object.
Finally, let's look at the animation runnable, the ObjectsRunnable class.
package com.ggl.moving.squares;
import javax.swing.SwingUtilities;
public class ObjectsRunnable implements Runnable {
private volatile boolean running;
private GameObject[] gameObjectsArray;
private MovingSquares movingSquares;
public ObjectsRunnable(MovingSquares movingSquares,
GameObject[] gameObjectsArray) {
this.movingSquares = movingSquares;
this.gameObjectsArray = gameObjectsArray;
this.running = true;
}
#Override
public void run() {
while (running) {
updateObjects();
xxx();
sleep();
}
}
private void updateObjects() {
for (int i = 0; i < gameObjectsArray.length; i++) {
gameObjectsArray[i].move();
}
}
private void xxx() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
movingSquares.repaintMovingPanel();
}
});
}
private void sleep() {
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
}
}
public synchronized void setRunning(boolean running) {
this.running = running;
}
}
This is a straightforward animation or game loop. I put the repaint call in another SwingUtilities invokeLater method to ensure that the Swing components are updated on the EDT.
I hope this was helpful to you.
Static variables will only be available once per Runtime. To create several GameObjects you have to avoid the static keywords.
Then call new GameObject() several times to create serveral instances of GameObject, each with its own set of variables.
edit:
move the variables strtCoX and strtCoY into GameObject (as suggested by #andrew-thomson )
fix all references to strtCoX and strtCoY inside of GameObject
change g.fillRect(strtCoX, strtCoY, size, size); to for (GameObject currentObject : GameObjectsArray) g.fillRect(currentObject.strtCoX, currentObject.strtCoY, size, size);
Explanation: The coordinate are attributes of GameObject. Each GameObject should have its own coordinates.
=> Your code should work just as before
remove all static keywords in GameObject
change GameObject.move(); to for (GameObject currentObject : GameObjectsArray) currentObject.move();
initialize you GameObjectArray in public MovingSquares() constructor like this: for (int i = 0; i < GameObjectsArray.length; i++) GameObjectsArray[i] = new GameObject();
Explanation: with new GameObject() we are creating 30 instances (in that loop inside the constructor). Therefore we also have to call move() 30 times (and that is why that is also nested in a loop)
=> Wear sunglasses!