How do you create object several times to move simultaneously on screen? - java

I have a thread which drops a circle in the y direction. I want to now create several circles on screen dropping at the same time with random x positions.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Goo
{
protected GooPanel gooPanel;
private boolean loop = true;
protected int width , height;
private int frameTimeInMillis = 50;
private RenderingHints renderingHints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING , RenderingHints.
VALUE_ANTIALIAS_ON);
#SuppressWarnings("serial")
class GooPanel extends JPanel
{
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHints(renderingHints);
draw(g2d);
}
}
public Goo()
{
this (800, 500);
}
public Goo(int w, int h)
{
width = w;
height = h;
JFrame frame = new JFrame ();
frame.setSize(width , height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gooPanel = new GooPanel ();
gooPanel.setPreferredSize(new Dimension(w, h));
frame.getContentPane ().add(gooPanel);
frame.pack();
frame.setVisible(true);
}
public void go()
{
while (loop)
{
gooPanel.repaint();
try
{
Thread.sleep(frameTimeInMillis);
} catch (InterruptedException e) {}
}
}
public void draw(Graphics2D g) {}
public void setFrameTime(int millis)
{
frameTimeInMillis = millis;
}
public Component getGooPanel ()
{
return gooPanel;
}
}
My FallingDrop class:
import java.awt.*;
public class FallingDrops extends Goo
{
double x, y, r;
int red, green, blue = 0;
Color a;
FallingDrops()
{
x = width / 2;
r = 10;
y = -r;
}
FallingDrops(double x)
{
this.x = x;
r = 10;
y = -r;
}
public void draw(Graphics2D g)
{
g.setColor(Color.GRAY);
g.fillRect(0, 0, width , height);
g.setColor(Color.WHITE);
g.fillOval ((int) (x - r), (int) (y - r), (int) (2 * r),
(int) (2 * r));
y++;
if (y - r > height)
y = -r;
}
public static void main(String [] args)
{
int num = 10;
Goo gooDrop [] = new FallingDrops[num];
for(int i = 0; i < gooDrop.length; i++)
{
double x = Math.random()*800;
gooDrop[i] = new FallingDrops(x);
System.out.println(x);
gooDrop[i].go();
}
}
}
At current, the loop fails to complete when the go() method is executed; thus only painting ONE object on screen, and not several as indicated in my loop. This is a simple fix I am sure. Any ideas what I am doing wrong?

The method go() never returns. when it is called on the first object in the array, it continues working infinitely. you should either make the repainting in a separate thread that is constantly repainting. or if you want repainting only when drops are added, then remove the while in your go method
public void go()
{
gooPanel.repaint();
try
{
Thread.sleep(frameTimeInMillis);
} catch (InterruptedException e) {}
}
this way it will returns after it had made a repaining and a pause.

while (loop) .. gooPanel.repaint();
Not the way to do custom painting. Establish a Swing Timer and call repaint() in the actionPerformed() method of the listener.
See the Custom Painting lesson in the tutorial for details and working examples.

Related

Problems with BufferedImage in Java

I am making a game (like Civilization) that has different tile types that I want to render as images. I have 9 different 16x16 png images to load in (called Con1, Con2, etc.), and here is my image loading code: (img[] is my BufferedImage array)
public void loadImages(){
for(int i = 0; i < 9; i++){
try {
img[i] = ImageIO.read(this.getClass().getResource("Con"+i+".png"));
}catch (Exception ex) {
System.out.println("Missing Image");
ex.printStackTrace();
}
}
}
I then paint these images with this code: (t[][] is my tile type array)
public void paint(Graphics g){
if(loop){
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
for (int x = 0; x < WIDTH; x++){
for (int y = 0; y < HEIGHT; y++){
if(i[x][y] == 0){
if (t[x][y] == 0){
g.drawImage(img[0], x, y, this);
}
else if(t[x][y] == 1){
g.drawImage(img[1], x, y, this);
}
else if(t[x][y] == 3){
g.drawImage(img[3], x, y, this);
}
else if(t[x][y] == 4){
g.drawImage(img[4], x, y, this);
}
else if(t[x][y] == 5){
g.drawImage(img[5], x, y, this);
}
}
r.fillRect(x*SCALE, y*SCALE, SCALE, SCALE);
}
}
g.drawImage(B, 0, 22, this);
}
}
My problem is that it doesn't show up correctly when I run it. I get this image:
that flashes on and off in the top-left corner of the window. What I am supposed to see is an image similar to the top-left portion of the above one (landmasses surrounded by ocean) except large enough to fill the window. Here is some runnable code: (I don't think the code will run without the required images but I would appreciate some help with getting the images to you all.)
//imports
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
public class MCVCon extends JFrame implements KeyListener, MouseListener{
//setting up variables
public BufferedImage[] img = new BufferedImage[9];
private final int WIDTH = 50, HEIGHT = 50;
private boolean loop = false;
private int SCALE = 16;
int t[][] = new int[WIDTH][HEIGHT]; //terrain type (since I took out the terrain generation it is set to 0 or ocean)
public MCVCon(){
//creating the window
super("Conqueror");
setSize(SCALE*WIDTH, SCALE*HEIGHT+22);
setVisible(true);
requestFocusInWindow();
setDefaultCloseOperation(EXIT_ON_CLOSE);
loadImages();
loop = true;
while(true){
this.repaint();
//delay for repaint
try{
Thread.sleep(50);
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
//load images
public void loadImages(){
for(int i = 0; i < 9; i++){
try {
img[i] = ImageIO.read(this.getClass().getResource("Con"+i+".png"));
}catch (Exception ex) {
System.out.println("Missing Image");
ex.printStackTrace();
}
}
}
//paint the images
public void paint(Graphics g){
if(loop){
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
for (int x = 0; x < WIDTH; x++){
for (int y = 0; y < HEIGHT; y++){
if (t[x][y] == 0){
g.drawImage(img[0], x, y, this);
}
else if(t[x][y] == 1){
g.drawImage(img[1], x, y, this);
}
r.fillRect(x*SCALE, y*SCALE, SCALE, SCALE);
}
}
g.drawImage(B, 0, 22, this);
}
}
//run the program
public static void main(String[] args) {
new MCVCon();
}
//necessary overrides
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
I was wondering what the problem might be. Thanks in advance.
So, I had a look at your code, there's no easy way to say, but it's a mess, with compounding issues which would make it very difficult to isolate the origin of any one problem, other than to say, they all feed into each other.
Let's start with the painting...
You're painting directly to the frame. This is generally discouraged for a number of reasons.
Let's start with the fact that JFrame isn't a single component, it's made up of a number compounding components
This makes it inherently dangerous to paint directly to, as any one of the child components could be painted without the frame's paint method been called.
Painting directly to a frame also means you're painting without consideration to the frame's borders/decorations, which are added into the visible area of the window, but wait...
setSize(SCALE*WIDTH, SCALE*HEIGHT+22);
suggests that you've tried to compensate for this, but this is "guess" work, as the decorations could take up more or less space depending on the configuration of the system
And, finally, top level containers aren't actually double buffered.
"But I'm painting to my own buffer" you say - but you're not
You create a BufferdImage and assign it's Graphics context t r
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
But when you paint anything, you're using g, which is the Graphics context passed to the paint method
g.drawImage(img[0], x, y, this);
This is also woefully inefficient, as you're creating a new BufferedImage each time paint is called, which takes time to create, takes up memory and puts extra strain on the garbage collection process as the local object becomes eligible for disposal almost immediately
Don't even get me started on the "main paint loop"
The next problem you have, is you have no concept of virtual and real world space.
You make a virtual map of your world using t, which maintains information about which tile should be used for a given x/y coordinate, but you never map this to the real world, instead, you paint each tile exactly at the same pixel x/y position, which means they now overlap, tiles have width and height, which means they need to be offset when painted onto the real world.
And finally, which I think is your actually question, is about scaling. There are a number of ways you could apply scaling, you could pre-scale the tiles when you load them, this gives you a lot of control over "how" they are scaled, but locks you into a single scale.
You could instead maintain a list of the scaled tiles, generated from a master list, which can be updated if the scale changes
Or you could simply scale the Graphics context directly.
Now, I've create a simple example, based on your code, correcting for most of the above. It creates a series of randomly colored rectangles at 10x10 pixels, instead of tiles, but the concept is the same.
import java.awt.Color;
import java.awt.Dimension;
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.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MCVCon extends JFrame {
//setting up variables
public MCVCon() {
//creating the window
super("Conqueror");
add(new GamePane());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MCVCon frame = new MCVCon();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
//necessary overrides
public class GamePane extends JPanel {
public BufferedImage[] img = new BufferedImage[9];
private final int width = 5, height = 5;
private int scale = 16;
int t[][] = new int[width][height]; //terrain type (since I took out the terrain generation it is set to 0 or ocean)
Color[] colors = new Color[]{
Color.RED,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.YELLOW
};
int tileHeight = 10;
int tileWidth = 10;
public GamePane() {
loadImages();
Random rnd = new Random();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int value = rnd.nextInt(9);
System.out.println(value + "- " + colors[value]);
t[x][y] = value;
}
}
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width * tileWidth * scale, height * tileHeight * scale);
}
public void loadImages() {
for (int i = 0; i < 9; i++) {
try {
img[i] = new BufferedImage(tileWidth, tileHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img[i].createGraphics();
g2d.setColor(colors[i]);
g2d.fill(new Rectangle(0, 0, tileWidth, tileHeight));
g2d.dispose();
} catch (Exception ex) {
System.out.println("Missing Image");
ex.printStackTrace();
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setTransform(AffineTransform.getScaleInstance(scale, scale));
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
g2d.drawImage(img[t[x][y]], x * tileWidth, y * tileHeight, this);
}
}
g2d.dispose();
}
}
}

Graphics not appearing in Java Jframe Canvas

I am trying to draw a vine within a Jframe however the graphics are not appearing when I run the program. The idea is that a "Vine" is randomly generated and can randomly branch out into other "Vines". If anyone can help that would be much appreciated.
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Vine extends JPanel {
public void paint(Graphics g, int a, int b, int c)
{
super.paint(g);
g.setColor(Color.BLACK);
g.drawLine(10, 10, 100, 100);
grow(g, a, b, c);
}
public Boolean TF(){
Boolean A;
int B = ((int)Math.random()+1);
if (B==1){
A = true;
} else {
A = false;
}
return A;
}
public void grow(Graphics g, int a, int b, int c){
int x = a;
int y = b;
int age = c;
for (int i=0; i<= age; i++) {
if (TF()) {
if (TF()) {
grow(g, x, y, ((age-i)/2));
}
}
if (TF()){
if (TF()){
g.drawLine(x, y, (x+1), y);
x++;
} else {
g.drawLine(x, y, (x-1), y);
x++;
}
} else {
g.drawLine(x, y, x, (y+1));
y++;
}
}
}
public static void main(String[] args)
{
JFrame f = new JFrame("Vine");
f.setBounds(300, 300, 200, 120);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Vine panel = new Vine();
Container c = f.getContentPane();
panel.setBackground(Color.WHITE);
c.add(panel);
f.setResizable(true);
f.setVisible(true);
}
}
There are several issues with this code:
Nobody is calling the paint(Graphics g, int a, int b, int c) method. When you inherit from a Swing component like this, there are several methods that are invoked "automatically" (among them a paint(Graphics g) method). But in order to perform custom painting, you should usually override the paintComponent(Graphics g) method.
You are generating random numbers while you are painting. This will have some odd effects. The most obvious one: When you resize the frame (or something else happens that causes the frame to be repainted), a new random vine will appear. It will have nothing in common with the previous one, causing a flickering mess, in the best case.
In order to create reporoducible results that are still "random", I generally recommend to not use Math.random(). (In fact, I never use this, at all). The java.util.Random class is usually preferable. It allows creating reproducible random number sequences (which is helpful for debugging), and it offers convenient methods like Random#nextBoolean(), which has exactly the effect that you probably wanted to achieve with the (somewhat oddly implemented) TF() method. This leads to the next point...:
Use better variable- and method names. Naming variables like the c in your example, or methods TF(), will make the code unreadable. You may call them x and y if they refer to screen coordinates, but the c should probably be called age, and the TF() method ... heck, I don't know how this should be called, maybe something like shouldBranch()?
If you have to perform "extensive" computations (like the random branching, in your example), it is usually better to pull this computation out of the painting process. You can assemble the desired lines and paintings in various ways. For the given example, a simple Path2D should be sufficient.
So far, the technical things. Apart from that: The algorithm itself will not lead to "nice" results. The random branching for each pixel will cause the lines to clob together to a black, fuzzy spot.
In fact, it can be pretty hard to tweak this to create "nice" results. It is hard to exactly say how the "randomness" should influence the overall appearance. The branching should be random, the angles should be random, the branch lengths should be random. Additionally, it will always look a bit odd when all the lines are drawn with the same thickness. The thickness of the lines should probably decrease at each branch, and along the branches in general.
In some practical applications, this generation of random plant-like structures is done with Lindenmayer systems - this may be a starting point for further research.
However, here is a very simple example of how simple lines can be assembled, somewhat randomly, to create something that resembles a plant:
Of course, this looks like cr*p compared to what one could do, given enough time and incentive. But it consists only of a few lines of code that randomly assemble a few branching lines, so this is as good as it gets without considering all the possible improvements.
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class VinePainting
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
private void createAndShowGUI()
{
JFrame f = new JFrame("Vine");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
VinePanel panel = new VinePanel();
Container c = f.getContentPane();
panel.setBackground(Color.WHITE);
c.add(panel);
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
class VinePanel extends JPanel
{
private static final Random RANDOM = new Random(0);
private Path2D vine;
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
if (vine == null)
{
vine = createVine(getWidth()/2, getHeight());
}
g.setColor(Color.BLACK);
g.draw(vine);
}
private Path2D createVine(int x, int y)
{
Path2D path = new Path2D.Double();
double angleRad = Math.toRadians(-90);
grow(path, x, y, angleRad, 10.0, 0, 30);
return path;
}
private static void grow(Path2D path,
double x, double y, double angleRad,
double stepSize, int step, int steps)
{
if (step == steps)
{
return;
}
path.moveTo(x, y);
double dirX = Math.cos(angleRad);
double dirY = Math.sin(angleRad);
double distance = random(stepSize, stepSize + stepSize);
double newX = x + dirX * distance;
double newY = y + dirY * distance;
path.lineTo(newX, newY);
final double angleRadDeltaMin = -Math.PI * 0.2;
final double angleRadDeltaMax = Math.PI * 0.2;
double progress = (double)step / steps;
double branchProbability = 0.3;
double branchValue = RANDOM.nextDouble();
if (branchValue + 0.1 < branchProbability)
{
double angleRadDelta = random(angleRadDeltaMin, angleRadDeltaMax);
double newAngleRad = angleRad + angleRadDelta;
double newStepSize = (1.0 - progress * 0.1) * stepSize;
grow(path, newX, newY, newAngleRad, newStepSize, step+1, steps);
}
double angleRadDelta = random(angleRadDeltaMin, angleRadDeltaMax);
double newAngleRad = angleRad + angleRadDelta;
double newStepSize = (1.0 - progress * 0.1) * stepSize;
grow(path, newX, newY, newAngleRad, newStepSize, step+1, steps);
}
private static double random(double min, double max)
{
return min + RANDOM.nextDouble() * (max - min);
}
}
A side note: This is somewhat similiar to this question. But there, randomness did not play a role, so the recursion is done while painting the tree. (Therefore, it allows playing around with some sliders, to modify the parameters and observe the effects).
Here's a GUI I created using your code. I didn't understand how your grow method worked, so I pretty much left it alone.
Here's what I did.
Created a GrowVine method to hold the line segments to draw. I also created a LineSegment class to hold one line segment. I did this so that I could keep the model data separate from the view. By separating concerns, I could focus on one part of the problem at a time.
I started the Swing application with a call to the SwingUtilities invoke later method. This ensures that the Swing components are created and used on the Event Dispatch thread.
I created a DrawingPanel from a JPanel, and overrode the paintComponent method. Notice that my override code does nothing but draw. All of the calculations are done in the GrowVine class.
I greatly simplified your TF method and renamed it to coinFlip. coinFlip better indicates to future readers of the code (including yourself) that the boolean should be true half the time and false half the time.
I left your grow method alone. I removed the drawLine methods and had the grow method write line segments to the List.
Once you get your grow method working, you can run the GrowVine class in a separate thread to animate the drawing of the vine.
Here's the code. I hope this helps you.
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Vine implements Runnable {
#Override
public void run() {
JFrame frame = new JFrame("Vine");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawingPanel panel = new DrawingPanel(400, 400);
frame.add(panel);
frame.pack();
frame.setVisible(true);
new GrowVine(panel, 400, 400).run();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Vine());
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = -8460577623396871909L;
private List<LineSegment> lineSegments;
public DrawingPanel(int width, int height) {
this.setPreferredSize(new Dimension(width, height));
this.lineSegments = new ArrayList<>();
}
public void setLineSegments(List<LineSegment> lineSegments) {
this.lineSegments = lineSegments;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
for (int i = 0; i < lineSegments.size(); i++) {
LineSegment ls = lineSegments.get(i);
Point s = ls.getStartPoint();
Point e = ls.getEndPoint();
g.drawLine(s.x, s.y, e.x, e.y);
}
}
}
public class GrowVine implements Runnable {
private int width;
private int height;
private DrawingPanel drawingPanel;
private List<LineSegment> lineSegments;
public GrowVine(DrawingPanel drawingPanel, int width, int height) {
this.drawingPanel = drawingPanel;
this.lineSegments = new ArrayList<>();
lineSegments.add(new LineSegment(10, 10, width - 10, height - 10));
this.width = width;
this.height = height;
}
#Override
public void run() {
grow(width / 2, height / 2, 200);
drawingPanel.setLineSegments(lineSegments);
}
public void grow(int a, int b, int c) {
int x = a;
int y = b;
int age = c;
for (int i = 0; i <= age; i++) {
if (coinFlip()) {
if (coinFlip()) {
grow(x, y, ((age - i) / 2));
}
}
if (coinFlip()) {
if (coinFlip()) {
lineSegments.add(new LineSegment(x, y, (x + 1), y));
x++;
} else {
lineSegments.add(new LineSegment(x, y, (x - 1), y));
x++;
}
} else {
lineSegments.add(new LineSegment(x, y, x, (y + 1)));
y++;
}
}
}
private boolean coinFlip() {
return Math.random() < 0.50D;
}
}
public class LineSegment {
private final Point startPoint;
private final Point endPoint;
public LineSegment(int x1, int y1, int x2, int y2) {
this.startPoint = new Point(x1, y1);
this.endPoint = new Point(x2, y2);
}
public Point getStartPoint() {
return startPoint;
}
public Point getEndPoint() {
return endPoint;
}
}
}
There are lots of implementation issues here. You can override the paintComponent of the JPanel to paint in it. Here is a quick fix to demonstrate how this is done. I changed your implementation a lot to fix the issues so you can get an idea.
Note: This was a quick fix. So the code quality is low and some OOP concepts were ignored. Just go through this and understand how this work and implement your own code.
Explanation:
You have to call repaint method of JPanel class to make it repaint itself. It will paint all Lines in the LinkedList to the panel too(refer the implementation). After repainting, create a new random line and add it to the LinkedList. It will be painted next time.
Then we have to animate this. So I implemented runnable interface in the Vine class. run method will be called when we add this vine object(panel) to a Thread and start() the thread. We need to run it forever. So add a loop in the run method. Then repaint the panel every time run method is called. This animation is too fast, so add a Thread.sleep() to slow down the animation.
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.util.LinkedList;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Line {//Line class to store details of lines
int x1, y1, x2, y2;
public Line(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
}
class Vine extends JPanel implements Runnable {//implements runnable to animate it
LinkedList<Line> lines = new LinkedList<>();//LinkedList to store lines
int x = 10;
int y = 10;
Line line;
public Boolean TF() {
Boolean A;
int B = (int) (Math.random() * 2 + 1);//logical error fixed
if (B == 1) {
A = true;
} else {
A = false;
}
return A;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
for (Line line : lines) {//paint all lines in the LinkedList
g.drawLine(line.x1, line.y1, line.x2, line.y2);
}
//Create and add a next line
if (TF()) {
if (TF()) {
line = new Line(x, y, (x + 1), y);
lines.add(line);
x++;
} else {
line = new Line(x, y, (x - 1), y);
lines.add(line);
x++;
}
} else {
line = new Line(x, y, x, (y + 1));
lines.add(line);
y++;
}
}
private static Vine panel;
public static void main(String[] args) {
JFrame f = new JFrame("Vine");
f.setSize(300, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new Vine();
Container c = f.getContentPane();
panel.setBackground(Color.WHITE);
c.add(panel);
f.setResizable(true);
f.setVisible(true);
panel.start();//start the animation(thread)
}
private void start() {
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
while (true) {
try {
Thread.sleep(100);//slow down the animation
panel.repaint();//then repaint the panel
} catch (InterruptedException ex) {
}
}
}
}

repaint() paints slower than paintComponent()?

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

timer.schedule() in not working at all (in Java)

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);
}
});
}
}

Creating a Spiral Animation (Delay Help)

Note: I am fairly new to Java so if the answer is incredibly simple, please keep that in mind :)
All I'm trying to do is make a nice looking spiral animation like the one it would show in Windows Media Player while music was playing or like an animation similar to one of the screen savers from Windows XP.
I'm stuck trying to figure out how to create delay between the creation of one line and then the creation of another line.
I want the program to start out with a black screen and every half-second or so, add one line at a slightly different location from the one before making for a cool spiral animation
I'm sure there's a way to do what I want using Thread.Sleep() I just don't know how to do it.
Any help or advice would be greatly appreciated! :D
A picture of my code currently: http://imgur.com/bsIqUOW
Swing is a single threaded environment. Care needs to be taken when you want change the state of the UI on a regular bases. You need to ensure that you don't block the Event Dispatching Thread in any way, doing so will prevent any new paint events (amongst others) from been processed, making your UI look like it's hung and also ensure you that you are synchronising your updates with the Event Dispatching Thread so as to ensure you are not risking any race conditions or other threaded issues
Take a closer look at Concurrency in Swing for more details. A simple approach would be to use a Swing Timer, for example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Spinner {
public static void main(String[] args) {
new Spinner();
}
public Spinner() {
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 SpinnerPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class SpinnerPane extends JPanel {
private float angle;
public SpinnerPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angle -= 5;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(40, 40);
}
protected Point calculateOutterPoint(float angel) {
int radius = Math.min(getWidth(), getHeight());
int x = Math.round(radius / 2);
int y = Math.round(radius / 2);
double rads = Math.toRadians((angel + 90));
// This determins the length of tick as calculate from the center of
// the circle. The original code from which this derived allowed
// for a varible length line from the center of the cirlce, we
// actually want the opposite, so we calculate the outter limit first
int fullLength = Math.round((radius / 2f)) - 4;
// Calculate the outter point of the line
int xPosy = Math.round((float) (x + Math.cos(rads) * fullLength));
int yPosy = Math.round((float) (y - Math.sin(rads) * fullLength));
return new Point(xPosy, yPosy);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int diameter = Math.min(getWidth(), getHeight());
int x = (getWidth() - diameter) / 2;
int y = (getHeight() - diameter) / 2;
Point to = calculateOutterPoint(angle);
g2d.drawLine(x + (diameter / 2), y + (diameter / 2), x + to.x, y + to.y);
g2d.dispose();
}
}
}
Using similar mechanisms, I've been able to create wait animations like...
You can use my AnimationPanel class and do your drawing in it instead. This technique is based on Active Rendering.
Code:
// AnimationPanel.java
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public abstract class AnimationPanel extends JPanel implements Runnable
{
private static final long serialVersionUID = 6892533030374996243L;
private static final int NO_DELAYS_PER_YIELD = 16;
private static final int MAX_FRAME_SKIPS = 5;
private static long fps = 30; // Frames Per Second.
private static long period = 1000000L * (long) 1000.0 / fps;
protected final int WIDTH;
protected final int HEIGHT;
private Thread animator;
private volatile boolean running = false;
private volatile boolean isWindowPaused = false;
private Graphics dbg;
private Image dbImage = null;
public AnimationPanel(int width, int height)
{
WIDTH = width;
HEIGHT = height;
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
requestFocus();
}
public void addNotify()
{
super.addNotify();
startAnimation();
}
void startAnimation()
{
if (animator == null || !running)
{
animator = new Thread(this);
animator.start();
}
}
public void run()
{
long beforeTime, afterTime, timeDiff, sleepTime;
long overSleepTime = 0L;
int noDelays = 0;
long excess = 0L;
beforeTime = System.nanoTime();
running = true;
while (running)
{
requestFocus();
animationUpdate();
animationRender();
paintScreen();
afterTime = System.nanoTime();
timeDiff = afterTime - beforeTime;
sleepTime = (period - timeDiff) - overSleepTime;
if (sleepTime > 0)
{
try
{
Thread.sleep(sleepTime / 1000000L);
}
catch (InterruptedException ignored)
{
}
overSleepTime = (System.nanoTime() - afterTime - sleepTime);
}
else
{
excess -= sleepTime;
overSleepTime = 0L;
if (++noDelays >= NO_DELAYS_PER_YIELD)
{
Thread.yield();
noDelays = 0;
}
}
beforeTime = System.nanoTime();
int skips = 0;
while ((excess > period) && (skips < MAX_FRAME_SKIPS))
{
excess -= period;
animationUpdate();
skips++;
}
}
stopAnimation();
System.exit(0);
}
void stopAnimation()
{
running = false;
}
private void animationUpdate()
{
if (!isWindowPaused)
{
update();
}
}
public abstract void update();
private void animationRender()
{
if (dbImage == null)
{
dbImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
if (dbImage == null)
{
System.out.println("Image is null.");
return;
}
else
{
dbg = dbImage.getGraphics();
}
}
draw(dbg);
}
public abstract void draw(Graphics graphics);
private void paintScreen()
{
Graphics g;
try
{
g = this.getGraphics();
if ((g != null) && (dbImage != null))
{
g.drawImage(dbImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync();
if (g != null)
{
g.dispose();
}
}
catch (Exception e)
{
System.out.println("Graphics context error : " + e);
}
}
public void setWindowPaused(boolean isPaused)
{
isWindowPaused = isPaused;
}
}
How to use:
AnimationPanel is a JPanel. Copy-Paste this class in your project.
Make another class, and make it extend AnimationPanel.
Override the update() and draw() methods of the AnimationPanel which are declared abstract in it.
Inside update() method you can change the values of variables which are declared in this custom class of yours. These variables will be the ones which are required for the animation purposes and which keep changing from frame to frame.
Inside draw() method, do all your custom painting using the variables that you've defined in your custom class.
You can use setWindowPaused() method to pause the animation if you like to as well.
Demonstration:
// DemonstrationPanel.java
import java.awt.*;
public class DemonstrationPanel extends AnimationPanel
{
private int red, green, blue;
private int a, b, c, d;
public DemonstrationPanel(int WIDTH, int HEIGHT)
{
super(WIDTH, HEIGHT);
red = 100;
green = blue = 5;
a = 2;
b = 500;
c = 200;
d = 5;
}
#Override
public void update()
{
red += 5;
red %= 255;
blue += 1;
blue %= 255;
green += 10;
green %= 255;
a += 20;
a %= HEIGHT;
b += 1;
b %= WIDTH;
c += 15;
c %= HEIGHT;
d += 20;
d %= WIDTH;
}
#Override
public void draw(Graphics graphics)
{
// Uncomment the below two statements to just see
// one line per frame of animation:
// graphics.setColor(BACKGROUND_COLOR);
// graphics.fillRect(0, 0, WIDTH, HEIGHT);
graphics.setColor(new Color(red, green, blue));
graphics.drawLine(b, c, d, a);
}
}
And here's Demo class:
// Demo.java
import javax.swing.*;
import java.awt.*;
public class Demo
{
public Demo()
{
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DemonstrationPanel(800, 600));
frame.setBackground(Color.BLACK);
frame.setSize(800, 600);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new Demo();
}
});
}
}
You can adjust the value of fps in the AnimationPanel class to change the speed of the animation.

Categories

Resources