Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 9 years ago.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Improve this question
I want to make a program in Java. What I am trying to do is, when the user click the screen a small square is drawn. One by one ten more squares are displayed with the previous square in the center. When the sixth square is drawn the first disappears, when the seventh square is drawn the second square disappears etc. until all the squares are gone.
This is the MainActivity:
import java.awt.Color;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MainActivity {
public static int width = 900;
public static int height = width / 16 * 9;
static HandlerClass handler = new HandlerClass();
static JFrame window;
static JPanel windowInner;
public static void main(String[] args) {
window = new JFrame("Squarmony 1.0");
window.setSize(width, height);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
windowInner = new JPanel();
windowInner.setSize(width, height);
windowInner.setBackground(Color.BLACK);
window.add(windowInner);
windowInner.addMouseListener(handler);
}
private static class HandlerClass implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
GraphicsActivity g = new GraphicsActivity();
g.drawRectRipple(window.getGraphics(), e);
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
}
And here is the GraphicsActivity:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
public class GraphicsActivity {
public void drawRectRipple(Graphics g, MouseEvent e) {
g.setColor(Color.WHITE);
for (int i = 0; i <= 10; i++) {
for (int j = 0; j <= 250; j += 10) {
g.drawRect(e.getX() + j / 2, e.getY() - j / 2, j - j / 2, j + j / 2);
try {
Thread.sleep(250);
} catch (InterruptedException e1) {
}
}
}
}
}
How could I draw the rectangles one by one (like ripples) to the screen?
Thanks,
John
So there are probably a number of ways to achieve this, some easier, some harder.
Backgound:
Animation is the illusion of change over time. This means over a time period, some kind of change is expected to happen. In your case, some squares get painted, some don't.
Now, this suggests that we need some way to update our UI on a regular bases. Now you could use a Thread, but to be frank, there are easier ways.
Swing provides a Timer class that provides events on a regular time interval, this allows us to make changes to the UI safely from within the context of the Event Dispatching Thread.
This is important, because it is expected that all updates and modifications to the UI occur within the context of the EDT. Also, any action which blocks the EDT will prevent the UI from being updated. Check out Concurrency in Swing for more details.
Now, you could set a timer to tick at a predefined interval that meets your needs. Ie, you need 10 updates over a period of n seconds, but this begins to put you at a slight disadvantage, as all you calculations would be based on some known, concert value...While you can certainly do this, I prefer a more flexible solution.
So, instead, I've chosen to use a "percentage" based approach. That is, all my animation is based on knowing that at given percentage of the way through the cycle, something needs to happen. This makes it possible to have a variable time frame without adversely effecting the paint algorithm.
To achieve this, we need to know when the cycle started and how long the cycle is. This way we can calculate the duration a cycle has being running and the percentile of the cycle that is completed, for example
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Check to see if the cycle has being completed...
if (cycleStartedAt == -1) {
// The time that this cycle started
cycleStartedAt = System.currentTimeMillis();
}
// The amount of time this cycle has being running
long duration = System.currentTimeMillis() - cycleStartedAt;
// The progress through this cycle...
progress = (double)duration / (double)runningTime;
// Check for the completion of this cycle...
if (progress > 1.0d) {
// Reset..
cycleStartedAt = -1;
progress = 0;
}
repaint();
}
});
timer.setRepeats(true);
timer.start();
What does this actually provide us? The provides us the ability to model the animation based on percentage of time. For example...
private double[][] squares = {
{0, 0.30},
{0.075, 0.375},
{0.15, 0.45},
{0.225, 0.525},
{0.30, 0.60},
{0.30, 0.675},
{0.375, 0.75},
{0.45, 0.825},
{0.525, 0.90},
{0.6, 0.975},
};
This is a list of the 10 squares, each with a start and end percentage, indicating when each square should be visible.
And finally, a runnable example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Squares {
public static void main(String[] args) {
new Squares();
}
public Squares() {
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private int runningTime = 5000;
private long cycleStartedAt = -1;
private double progress = 0;
private double[][] squares = {
{0, 0.30},
{0.075, 0.375},
{0.15, 0.45},
{0.225, 0.525},
{0.30, 0.60},
{0.30, 0.675},
{0.375, 0.75},
{0.45, 0.825},
{0.525, 0.90},
{0.6, 0.975},
};
public TestPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (cycleStartedAt == -1) {
cycleStartedAt = System.currentTimeMillis();
}
long duration = System.currentTimeMillis() - cycleStartedAt;
progress = (double)duration / (double)runningTime;
if (progress > 1.0d) {
cycleStartedAt = -1;
progress = 0;
}
repaint();
}
});
timer.setRepeats(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics g2d = (Graphics2D) g.create();
int width = getWidth() - 1;
int height = getHeight() - 1;
int hGap = width / squares.length;
int vGap = height / squares.length;
int index = 0;
for (double[] square : squares) {
if (progress >= square[0] && progress <= square[1]) {
int sWidth = hGap * (index + 1);
int sHeight = vGap * (index + 1);
int x = (width - sWidth) / 2;
int y = (height - sHeight) / 2;
g2d.drawRect(x, y, sWidth, sHeight);
}
index++;
}
g2d.dispose();
}
}
}
Check out Performing custom painting and 2D Graphics for more details
Related
class GraphicsExampleComponent extends JComponent
{
//#Override
public void paintComponent(Graphics g)
{
//make the first call to your recursive routine
drawSquare1(g, 0, 0, 80);
}
public void drawSquare1(Graphics g, int x, int y, int size)
{
//draw a rectangle
g.drawRect(x, y, size, size);
g.fillRect(x, y, size, size);
//reset the parameters
x = x + size + 10;
y = y + (size/4);
size = size/4;
//determine if you should call it again.
if (size<4 || x>600)
drawSquare1(g, x, y, size);
}
My assignment is to create disappearing squares that get 25% smaller as they move to the right. When I run the code, it just creates the one square and stops. Can anyone help me understand what I am doing wrong.
disappearing squares that get 25% smaller as they move to the right.
Ok, let's step back for a second and break this down a bit.
You need to know...
The amount of space to be covered
The amount of space already covered (by the square)
The original size of the square
The size of the square should be when it reaches the other side (25% smaller)
When you have all this, you can calculate the size of the square at any point along its journey.
To determine the amount of space, you can use the component's width, via getWidth().
To determine the space already covered, you could start by having a look at box's current x position
// Assuming that box is a instance of Rectangle
double progress = (double)box.x / (double)getWidth();
We could argue that we should look at the middle of the box, or the trailing edge, but both of those are easy to implement.
Next, we need know the range of change (from start to end size), we can then use that to calculate the delta to be applied to the box...
double range = startSize - endSize;
double value = (range * progress);
box.width = (int)startSize - (int)value;
box.height = (int)startSize - (int)value;
Soooo, this will provide with the means to determine the size of the component based on it's current location (horizontally) through the component.
Next, you need some way to update the box's position and update the UI.
One of the better solutions is to use a Swing Timer. This will allow you to perform a repeating action (with a specified delay between updates) which won't block the UI and will generate updates within the Event Dispatching Queue, which is important because Swing is not Thread safe.
Have a look at How to Use Swing Timers for more details.
And finally, all we need, is to update the component with current state, via it's paintComponent method ... easy :P
Runnable example
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.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 Main {
public static void main(String[] args) {
new Main();
}
public Main() {
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
protected static double startSize = 50;
protected static double endSize = startSize * 0.25;
private Rectangle box;
private Timer timer;
public TestPane() {
box = new Rectangle(0, 100 - 25, 50, 50);
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (box.x + box.width >= getWidth()) {
box.x = getWidth() - box.width;
box.width = (int)endSize;
box.height = (int)endSize;
timer.stop();
repaint();
}
box.x += 1;
double progress = (double)box.x / (double)getWidth();
double range = startSize - endSize;
double value = (range * progress);
box.width = (int)startSize - (int)value;
box.height = (int)startSize - (int)value;
repaint();
}
});
}
#Override
public void addNotify() {
super.addNotify();
box.x = 0;
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fill(box);
g2d.dispose();
}
}
}
The condition if (size < 4 || x>600) is never true because when drawSquare1 is invoked for the first time size=80 and x=0.
Changing it to say if (size > 4 && x<600) will paint 3 squares on the screen without any noticeable animation.
To animate it we'll need to add some delay between paintings and remove previously painted squares.
To do so we use a swing Timer. We use the timer to repeatedly invoke drawSquare1.
drawSquare1 should modify the parameters controlling the painting, and call repaint.
import java.awt.*;
import javax.swing.*;
public class Main {
Main() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
GraphicsExampleComponent board = new GraphicsExampleComponent();
frame.add(board);
frame.pack();
frame.setVisible(true);
board.animate();
}
public static void main(String[] args) {
new Main();
}
}
class GraphicsExampleComponent extends JComponent{
private final static int W = 200, H = 100, MIN_SIZE = 4, STEP = 10, DELAY = 2000;
private int x= 0, y = 0, size = 80;
private Timer timer;
void animate(){
timer = new Timer(DELAY, e->drawSquare());
timer.start();
}
public void drawSquare(){
//check stop criteria
if (size < MIN_SIZE || x >= getWidth()) {
timer.stop();
return;
}
//reset the parameters
x = x + size + STEP;
y = y + size/4;
size = size/4;
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//draw a rectangle
g.fillRect(x, y, size, size);
g.dispose();;
}
#Override
public Dimension preferredSize() {
return new Dimension(W, H);
}
}
(Test in online here)
Thank you everyone for the help. I unfortunately could not use a timer due to us not learning about it in class and we are not allowed to use methods/codes that we haven't gone over. To fix this I had to fix my base case by making it size >= 4 && x < 600.
I tried to make double buffered graphics for my canvas but it always disappears right after rendering, and sometimes it doesn't even render, Here is the code:
package initilizer;
import java.awt.AWTException;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import input.Keyboard;
public class Main extends Canvas{
static int width = 800;
static int height = 600;
int cx = width/2;
int cy = height/2;
boolean initilized = false;
double FOV = 0.5 * Math.PI;
Camera cam = new Camera(1.0, 5.0, 3.0);
Camera cam1 = new Camera(10.0, 50.0, 30.0);
long lastFpsCheck = System.currentTimeMillis();
public static JFrame frame = new JFrame("3D Engine");
Robot robot;
static Keyboard keyboard = new Keyboard();
Image img;
public static void main(String[] args) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Canvas canvas = new Main();
canvas.setSize(width, height);
canvas.addKeyListener(keyboard);
canvas.setFocusable(true);
canvas.setBackground(Color.black);
frame.add(canvas);
frame.pack();
frame.setVisible(true);
BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
// Create a new blank cursor.
Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
cursorImg, new Point(0, 0), "blank cursor");
// Set the blank cursor to the JFrame.
canvas.setCursor(blankCursor);
}
void init() {
try {
robot = new Robot();
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
double[] rotate2D(double[] pos,double[] rot) {
double x = pos[0];
double y = pos[1];
double s = rot[0];
double c = rot[1];
double[] result = {(x * c) - (y * s), (y * c) + (x * s)};
return result;
}
public void paint(Graphics MainGraphics) {
Point startMousePos = MouseInfo.getPointerInfo().getLocation();
double startMouseX = startMousePos.getX();
double startMouseY = startMousePos.getY();
if(img == null)
{
img = createImage(width, height);
}
Graphics g = img.getGraphics();;
// First run initialization
if (initilized == false) {
initilized = true;
init();
}
// Storing start time for FPS Counting
long startTime = System.currentTimeMillis();
// Clearing Last Frame
//g.clearRect(0, 0, width, height);
// Drawing Crosshair
g.setColor(Color.white);
g.fillRect(cx - 8, cy - 1, 16, 2);
g.fillRect(cx - 1, cy - 8, 2, 16);
// Drawing Debugger Menu
g.drawString("HI WASSUp", 0, 16);
g.dispose();
if (frame.isFocused() == true) {
robot.mouseMove(cx, cy);
Point endMousePos = MouseInfo.getPointerInfo().getLocation();
double endMouseX = endMousePos.getX();
double endMouseY = endMousePos.getY();
double[] rel = {startMouseX - endMouseX, startMouseY - endMouseY};
cam.mouseMotion(rel);
}
// Limiting FPS
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Calculating FPS
long endTime = System.currentTimeMillis();
double delta_time = (endTime - startTime);
if ((lastFpsCheck + 1000) < endTime) {
lastFpsCheck = endTime;
frame.setTitle("3D Engine - FPS: " + (int) (1000/delta_time));
}
// Controlling camera movement
if (keyboard.getW() == true) {
cam.update(delta_time, "W");
}
if (keyboard.getA() == true) {
cam.update(delta_time, "A");
}
if (keyboard.getS() == true) {
cam.update(delta_time, "S");
}
if (keyboard.getD() == true) {
cam.update(delta_time, "D");
}
if (keyboard.getE() == true) {
cam.update(delta_time, "E");
}
if (keyboard.getQ() == true) {
cam.update(delta_time, "Q");
}
// Draw rendered frame
MainGraphics.drawImage(img, 0,0, null);
// Draw next frame
repaint();
}
}
I posted a question recently about this code, You could check keyboard java from that last post if you wanted to, But please help me with this I'm new to java programming (I still have some programming experience tho), Thank you
The answer to your question is complicated.
Java Swing JPanel (or JComponent) are double buffered by default
Swing already has a painting mechanism, which you don't control, so you need to work within it's functionality.
The only real reason you would use a java.awt.Canvas is if you want to take complete control over the painting process
The first thing I would suggest you do is take a look at Performing Custom Painting and Painting in AWT and Swing to get a better idea of how painting works in Swing/AWT. This will provide you a better understanding of the API and whether you want to work with it or define your own.
Some other areas of concern:
Don't do Thread.sleep(1000); in the paint method, nothing will be rendered until AFTER the paint method returns. You want to seperate the "update pass" from the "paint pass". Painting does nothing else by paints. All you decision making should be done as part of your "update pass" from your "main loop", which should be executed (in this case) off the Event Dispatching Thread, so as to prevent possible issues, but which then raises a bunch of other issues
MouseInfo.getPointerInfo().getLocation() is NOT how you should be tracking the position of the mouse. Swing already provides a number of mechanisms for tracking the mouse events. See How to write a Mouse Listener and How to Write a Mouse-Motion Listener for more details.
Based on every thing, I'd also be concerned about how you're tracking keyboard input and would highly recommend that you take a look at How to Use Key Bindings for the most commonly recommended method for managing key board input from the user.
The following example simply uses a JPanel as it's primary rendering surface. It takes advantage of the pre-existing painting process and uses a Swing Timer as the "main loop" mechanism, which is responsible for processing the user input and update the state before scheduling a new paint pass.
Remember, Swing is NOT thread safe and you should not update the UI or anything the UI might depend on, from outside the context of the Event Dispatching Thread.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.time.Duration;
import java.time.Instant;
import java.util.HashSet;
import java.util.Set;
import java.util.StringJoiner;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
Main main = new Main();
frame.add(main);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
main.start();
}
});
}
// Decouple the input from the implementation
enum Input {
UP, DOWN, LEFT, RIGHT
}
public class Main extends JPanel {
boolean initilized = false;
double FOV = 0.5 * Math.PI;
private Instant lastFpsCheck = Instant.now();
private Point mousePosition;
private Timer timer;
private Set<Input> input = new HashSet<>();
public Main() {
MouseAdapter mouseHandler = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
// This is within the components coordinate space
mousePosition = e.getPoint();
}
#Override
public void mouseEntered(MouseEvent e) {
mousePosition = e.getPoint();
}
#Override
public void mouseExited(MouseEvent e) {
mousePosition = null;
}
};
addMouseMotionListener(mouseHandler);
addMouseListener(mouseHandler);
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Pressed.up");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Released.up");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Pressed.down");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Released.down");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Pressed.left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Released.left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Pressed.right");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Released.right");
actionMap.put("Pressed.up", new InputAction(input, Input.UP, true));
actionMap.put("Released.up", new InputAction(input, Input.UP, false));
actionMap.put("Pressed.down", new InputAction(input, Input.DOWN, true));
actionMap.put("Released.down", new InputAction(input, Input.DOWN, false));
actionMap.put("Pressed.left", new InputAction(input, Input.LEFT, true));
actionMap.put("Released.left", new InputAction(input, Input.LEFT, false));
actionMap.put("Pressed.right", new InputAction(input, Input.RIGHT, true));
actionMap.put("Released.right", new InputAction(input, Input.RIGHT, false));
timer = new Timer(15, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
update();
}
});
}
public void start() {
startTime = Instant.now();
timer.start();
}
public void stop() {
timer.stop();
}
// The start time of a given cycle
private Instant startTime;
// The estimated number of frames per second
private double fps = 0;
// The number of acutal updates performed
// within a given cycle
private int updates = 0;
protected void update() {
if (startTime == null) {
startTime = Instant.now();
}
if (input.contains(Input.UP)) {
//cam.update(delta_time, "W");
}
if (input.contains(Input.LEFT)) {
//cam.update(delta_time, "A");
}
if (input.contains(Input.DOWN)) {
//cam.update(delta_time, "S");
}
if (input.contains(Input.RIGHT)) {
//cam.update(delta_time, "D");
}
// Don't know what these do, so you will need to devices
// your own action
//if (input.contains(Input.UP)) {
//cam.update(delta_time, "E");
//}
//if (input.contains(Input.UP)) {
//cam.update(delta_time, "Q");
//}
Instant endTime = Instant.now();
Duration deltaTime = Duration.between(startTime, endTime);
if (lastFpsCheck.plusSeconds(1).isBefore(endTime)) {
System.out.println(deltaTime.toMillis());
lastFpsCheck = endTime;
fps = updates;
updates = 0;
startTime = Instant.now();
}
updates++;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
double[] rotate2D(double[] pos, double[] rot) {
double x = pos[0];
double y = pos[1];
double s = rot[0];
double c = rot[1];
double[] result = {(x * c) - (y * s), (y * c) + (x * s)};
return result;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//Point startMousePos = MouseInfo.getPointerInfo().getLocation();
//double startMouseX = startMousePos.getX();
//double startMouseY = startMousePos.getY();
Graphics2D g2d = (Graphics2D) g.create();
// Drawing Debugger Menu
g2d.drawString("HI WASSUp", 0, 20);
if (mousePosition != null) {
g2d.drawString(mousePosition.x + "x" + mousePosition.y, 0, 40);
// Your old code is broken, because MouseInfo.getPointerInfo
// doesn't give you the position of the mouse from within
// the components coordinate space, but in the screen space
// instead
//robot.mouseMove(cx, cy);
//Point endMousePos = MouseInfo.getPointerInfo().getLocation();
//double endMouseX = endMousePos.getX();
//double endMouseY = endMousePos.getY();
//double[] rel = {startMouseX - endMouseX, startMouseY - endMouseY};
//cam.mouseMotion(rel);
}
g2d.drawString(Double.toString(fps), 0, 60);
StringJoiner sj = new StringJoiner(", ");
for (Input item : input) {
switch (item) {
case DOWN:
sj.add("down");
break;
case UP:
sj.add("up");
break;
case LEFT:
sj.add("left");
break;
case RIGHT:
sj.add("right");
break;
}
}
g2d.drawString(sj.toString(), 0, 80);
g2d.dispose();
}
public class InputAction extends AbstractAction {
private final Set<Input> input;
private final Input direction;
private final boolean add;
public InputAction(Set<Input> input, Input direction, boolean add) {
this.input = input;
this.direction = direction;
this.add = add;
}
#Override
public void actionPerformed(ActionEvent evt) {
if (add) {
input.add(direction);
} else {
input.remove(direction);
}
}
}
}
}
Now, because of the way Swing's paint process works, the FPS is at best a "guesstimate" and I'd personally no rely to heavy on it. I might consider setting the Timer to use a 5 millisecond delay instead and just go as fast as you can.
Now, if you absolutely, positively must have, without question, complete control over the painting process, then you will need to start with a java.awt.Canvas and make use of the BufferStrategy API.
This will give you complete control over the painting process. It's more complicated and will require you to take into consideration more edge cases, but will provide you with complete control over scheduling when a paint pass occurs and thus, better control over the FPS.
I would recommend having a look at the JavaDocs as the example is better.
I used the Thread.sleep(1000); to limit FPS only, It was 1000/60 but I changed it to this because I thought the problem may be in the speed of rendering
This is, to be frank, is a naive approach and demonstrates a lack of understanding with how the painting process works - no offensive, you've got to start somewhere. But a better place to start would be by reading the available documentation, which I've provided above so you can gain a better understanding of how the API actually works and make better decisions about whether you want to use it (ie JPanel) or roll your own (ie Canvas)
package me.tykutcher.text.gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.concurrent.TimeUnit;
import javax.swing.JPanel;
public class TestPane extends JPanel {
public boolean sleeper = false;
public boolean sleeper2 = false;
public boolean sleeper3 = false;
public boolean sleeper4 = false;
public boolean sleeper5 = false;
public boolean sleeper6 = false;
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Loading", 10, 23);
g.drawRect(60, 13, 175, 10);
g.setColor(Color.GREEN);
g.fillRect(61, 14, 25, 9);
sleeper = true;
if(sleeper == true){ //why do all of the delays run before adding the string, adding the rectangle, and filling the rectangle
try {
//delay for 10 seconds
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
sleeper = false;
g.fillRect(87, 14, 25, 9);
sleeper2 = true;
if(sleeper2 == true){
try {
//delay for 10 seconds
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
sleeper2 = false;
}
public Dimension getPreferredSize() {
return new Dimension(250, 37);
}
}
Above is the class that I want the 10 second delay (just testing).
Below is the class that makes the JFrame GUI that I am running the above code in
package me.tykutcher.text.gui;
import javax.swing.JFrame;
public class textGui {
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setTitle("Loading... ");
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
}
If i comment out the wait commands, the code all runs at once, eventually, i will have a notify or boolean way of completing this as my gui will actually be doing stuff
You painted shapes before the sleep but, you will only see them when the paintComponent method completes, and that's after the sleep.
If you want to draw some shapes, then have time pass, then draw some more, you need to make the time pass outside the paintComponent method (maybe with javax.swing.Timer) and decide what to paint each time paintComponent is called.
thread.sleep doesn't work here because it prevents Swing from executing its necessary functions. As a result, Swing isn't able to complete paintComponent and output your loading bar until both sleep timers run out. As Jonathan mentioned above, the easiest solution is to use Javax.swing.Timer and call repaint() anytime your GUI needs to be updated.
I fixed up your loading bar using the timer function:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.Timer;
import javax.swing.JPanel;
public class TestPane extends JPanel {
private static final long serialVersionUID = 1L;
private int xCoord = 63; // the x-coordinate value
private int totalWidth = 26; //total width of all the bars and spaces so far
public TestPane() {
//Below can be rewritten to be a lambda function if you want
Timer timer = new Timer(2500, new ActionListener() { //create a timer and actionListener - wait 2.5 seconds between each bar addition
#Override
public void actionPerformed(ActionEvent e) {
xCoord += 26; //add the length of each bar
if (xCoord >= 167) { //make sure we don't go past the initial rectangle width
((Timer)e.getSource()).stop();
}
repaint(); //repaint the GUI after the timer ends
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Loading", 10, 23);
g.drawRect(62, 13, 131, 10);
g.setColor(Color.GREEN);
int temp = xCoord; //temp variable that let's us iterate over the length of the bar without affecting the x-coordinate
while(temp >= 63){
g.fillRect(temp, 14, totalWidth-1, 9);
temp -=26;
}
}
public Dimension getPreferredSize() {
return new Dimension(250, 37);
}
}
I am using a Swing Timer in my game but when the game is running it appears to have moments when it runs smoothly and moments when it slows down.
Why is the time fluctuating?
And how do I fix it?
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main extends JFrame {
public Main() {
super("JFrame");
// you can set the content pane of the frame
// to your custom class.
setContentPane(new ImagePanel());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 400);
setResizable(false);
setVisible(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new Main();
}
class ImagePanel extends JPanel {
Timer movementtimer;
int x, y;
public ImagePanel() {
x = 0;
y = 0;
movementtimer = new Timer(12, new ActionListener() {
public void actionPerformed(ActionEvent e) {
long timstarted = System.currentTimeMillis();
moveImage();
repaint();
long timefinished = System.currentTimeMillis() - timstarted;
System.out.println(timefinished + " to run");
};
});
movementtimer.start();
}
public void moveImage() {
x++;
y++;
if (x > 800) {
x = 0;
}
if (y > 400) {
y = 0;
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(0, 0, 800, 400);
g.setColor(Color.BLUE);
g.fillRect(x, y, 50, 50);
}
}
}
Here is an example of my code. In my actual program I am drawing Images and not just a rectangle. There is also a lot of collision detection and other small calculations happening.
Also, here is a link to the Jar file for the game so you can run it and (hopefull) see what I mean. http://dl.dropbox.com/u/8724803/Get%20To%20The%20Chopper%201.3.jar
Thanks
Tom
Because the rendering is trivial, I find this variation of your example to be very smooth. The render time is well below a half millisecond, so the 12 millisecond period (~83 Hz) is plenty of time to finish a frame, typically taking less that 10% of one core. As the render time grows, the timer thread becomes saturated, and events are coalesced. The effect is magnified on a single core, as rendering competes with garbage collection and external processing demands. Java is not a real-time system, and not all schedulers are created equal.
You'll certainly want to profile your actual code, as suggested here, to see any correlation with fluctuating performance. One alternative approach is to lengthen the period (decrease the frequency) to meet your rendering deadline and use a larger increment in moveImage() to get the same velocity.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main extends JFrame {
private static final int W = 800;
private static final int H = 400;
public Main() {
super("JFrame");
this.add(new ImagePanel());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
setSize(W, H);
this.setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Main();
}
});
}
class ImagePanel extends JPanel {
Timer movementTimer;
int x, y;
public ImagePanel() {
x = 0;
y = 0;
movementTimer = new Timer(12, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
moveImage();
repaint();
}
});
movementTimer.start();
}
public void moveImage() {
x++;
y++;
if (x > W) {
x = 0;
}
if (y > H) {
y = 0;
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
long start = System.nanoTime();
g.setColor(Color.RED);
g.fillRect(0, 0, W, H);
g.setColor(Color.BLUE);
g.fillRect(x, y, 50, 50);
double delta = (System.nanoTime() - start) / 1000000d;
g.drawString(String.format("%1$5.3f", delta), 5, 15);
}
}
}
The Swing Timer is notorious for its inaccuracy. Use something else instead.
On prompting, I've decided to undelete this post. OTOH most of the extra information that makes it worth reinstating comes from trashgod, so I'll merely quote/paraphrase their comments.
I'd argue that it's reasonably accurate but easy to saturate.
And trashgod goes on to add in a separate comment that:
(I) might cite Clock Quality. javax.swing.Timer isn't very accurate, but it has usefully precise resolution on modern platforms.
In the panel constructor do:
setBackground(Color.RED);
Then you do not need to erase the background, as you are calling super.paintComponent.
In general calculate positions on actual time passed (System.nanoTime()) and do not rely on timer frames. There are a couple of gaming frameworks out there, so maybe it is worth looking at their solution. I liked the sample.
I am working on a Touch User interface in Swing. While I know this isn't optimal, I am on a short deadline and don't have time to Touch-screen specific GUI packages (if there are any).
I want my users to be able to 'swipe' their finger across the screen, and the view of a special JScrollPane I made moves with it.
The code is very simple -
public class PanScrollPane extends JScrollPane implements MouseMotionListener{
public PanScrollPane() {
super();
this.addMouseMotionListener(this);
}
#Override
public void mouseDragged(MouseEvent arg0) {
System.out.println("Mouse Dragged!");
}
#Override
public void mouseMoved(MouseEvent arg0) {
System.out.println("Mouse Moved!");
}
The problem I'm having is that the JScrollPane is a container for all sorts of JComponents. When I first started working on this, I figured the MouseMovedEvent and MouseDraggedEvent would propagate up the 'GUI tree', untill they encountered a Component with a listener specifically for that event. Now it seems that any component I add to the panScrollPane blocks any of these MouseMotion events, leaving me unable to pan.
panScrollPane.add(new JButton("This thing blocks any mouse motion events"));
I figured propagating the MouseEvent by hand (adding listeners to every single component and then having them send the event to their parent) would work. This, however, is a very time-intensive undertaking and as I would rather spend my time working on other things, I was wondering if any of you know any work-around for this problem.
Thanks for reading, and hopefully thanks for answering! :)
edit: To make my intentions clearer. I only want the mousemotion events to be caught by the panPanel, any other event (like MouseClick, MouseRelease) should be processed normally
This ad hoc approach leverages the existing JScrollPane actions that are usually used in key bindings. You'll have to tune N to your implementation of Scrollable.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.Timer;
/** #see http://stackoverflow.com/questions/7201509 */
public class ScrollAction extends JPanel {
private static final int TILE = 64;
private static final int DELTA = 16;
public ScrollAction() {
this.setOpaque(false);
this.setFocusable(true);
this.setPreferredSize(new Dimension(50 * TILE, 50 * TILE));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.lightGray);
int w = this.getWidth() / TILE + 1;
int h = this.getHeight() / TILE + 1;
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
if ((row + col) % 2 == 0) {
g.fillRect(col * TILE, row * TILE, TILE, TILE);
}
}
}
}
private void display() {
JFrame f = new JFrame("ScrollAction");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JScrollPane scrollPane = new JScrollPane(this);
final ScrollTimer left = new ScrollTimer(scrollPane, "scrollLeft");
final ScrollTimer right = new ScrollTimer(scrollPane, "scrollRight");
final ScrollTimer up = new ScrollTimer(scrollPane, "scrollUp");
final ScrollTimer down = new ScrollTimer(scrollPane, "scrollDown");
final JViewport viewPort = scrollPane.getViewport();
viewPort.setPreferredSize(new Dimension(5 * TILE, 5 * TILE));
viewPort.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
left.stop();
if (e.getX() < DELTA) {
left.start();
}
right.stop();
if (e.getX() > viewPort.getWidth() - DELTA) {
right.start();
}
up.stop();
if (e.getY() < DELTA) {
up.start();
}
down.stop();
if (e.getY() > viewPort.getHeight() - DELTA) {
down.start();
}
}
});
f.add(scrollPane);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static final class ScrollTimer implements ActionListener {
private static int N = 10;
private static int DELAY = 100;
private String cmd;
private Timer timer;
private Action action;
private JScrollPane scrollPane;
private int count;
public ScrollTimer(JScrollPane scrollPane, String action) {
this.cmd = action;
this.timer = new Timer(DELAY, this);
this.action = scrollPane.getActionMap().get(action);
this.scrollPane = scrollPane;
}
#Override
public void actionPerformed(ActionEvent e) {
if (count++ < N) {
action.actionPerformed(new ActionEvent(scrollPane, 0, cmd));
} else {
timer.stop();
}
}
public void start() {
count = 0;
timer.start();
}
public void stop() {
timer.stop();
count = 0;
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ScrollAction().display();
}
});
}
}
How about using a GlassPane? I think its meant to address exactly these types of situations.
Getting mouseEvents for a component and all its children is ... tricky to get right. You might consider to rely on stable (and extensively tested :-) code around. The jdk7 way of doing it is to use a JLayer (which internally registers an AWTEventListener as it has all priviledges). For earlier versions, you can use its predecessor JXLayer