I want to make the pacman open/close mouth animation using the easiest method.
Here is my recent code: The problem is, nothing is happening?
package ordner;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class PacMan implements ActionListener {
private JFrame frame;
private DrawPanel panel;
private void initGui() {
frame = new JFrame("Pacman");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new DrawPanel();
frame.add(panel);
panel.setBackground(Color.BLACK);
frame.setSize(300, 300);
frame.setVisible(true);
}
public static void main(String[] args) {
PacMan pm = new PacMan();
pm.initGui();
}
#Override
public void actionPerformed(ActionEvent e) {
panel.repaint();
}
}
and here is my draw panel:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DrawPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.yellow);
g.fillArc(70,50,150,150,30,300);
int i = 0;
while ( i <= 60) {
g.fillArc(70,50,150,150,30-i,300+i+i);
try {
Thread.sleep(25);
}
catch (Exception e) {
Thread.currentThread().interrupt();
}
i++;
}
}
}
The while loop doesn't affect anything, what could be the reason for that?
Something like this might work for PacMan images. It uses a Java 2D based Shape instance to represent the form, and an AffineTransform to produce the different orientations.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.io.*;
import javax.imageio.ImageIO;
class PacManShape {
private double size;
private double rotation;
final int maxSize = 4;
static File home = new File(System.getProperty("user.home"));
static File images = new File(home, "images");
PacManShape(int size, double rotation) {
this.size = size;
this.rotation = rotation;
}
public Area getPacManShape(double jaws) {
Area area = new Area(new Ellipse2D.Double(0d, 0d, size, size));
double x1 = size / 2 + (2d * size * Math.cos(jaws / 2d));
double y1 = size / 2 + (2d * size * Math.sin(jaws / 2d));
double x2 = x1;
double y2 = size / 2 - (2d * size * Math.sin(jaws / 2d));
Polygon mouth = new Polygon();
mouth.addPoint((int) (size / 2), (int) (size / 2));
mouth.addPoint((int) x1, (int) y1);
mouth.addPoint((int) x2, (int) y2);
mouth.addPoint((int) (size / 2), (int) (size / 2));
area.subtract(new Area(mouth));
return area;
}
public BufferedImage getPacManImage(double angle, Color color) {
BufferedImage bi = new BufferedImage(
(int) size, (int) size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bi.createGraphics();
g2.setColor(color);
g2.fillRect(0, 0, (int) size, (int) size);
AffineTransform rotate = AffineTransform.getRotateInstance(
rotation, size / 2, size / 2);
g2.setTransform(rotate);
Area pacMan = getPacManShape(angle);
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.YELLOW);
float[] dist = {.15f, .9f};
Color[] colors = {Color.YELLOW, Color.ORANGE};
Point2D center = new Point2D.Double(size / 2, size / 2);
RadialGradientPaint radial = new RadialGradientPaint(
center, (float) ((size / 2) - 2f), dist, colors);
g2.setPaint(radial);
g2.fill(pacMan);
GradientPaint gradient = new GradientPaint(
0, 0, new Color(255, 255, 225, 220),
(int) (size / 3), 0, new Color(255, 255, 255, 0));
g2.setPaint(gradient);
g2.fill(pacMan);
g2.dispose();
return bi;
}
public void savePacManImage(int q, int num) throws IOException {
double angle = Math.PI*2 / 3d * ((double) num / (double) maxSize);
BufferedImage bi = getPacManImage(angle, Color.WHITE);
images.mkdirs();
File img = new File(images, "PacMan-" + q + "x" + num + ".gif");
ImageIO.write(bi, "gif", img);
}
public static void main(String[] args) {
try {
for (int ii = 0; ii < 4; ii++) {
PacManShape pms = new PacManShape(100, (double) ii * Math.PI / 2d);
for (int jj = 0; jj <= pms.maxSize; jj++) {
pms.savePacManImage(ii, jj);
}
}
Desktop.getDesktop().open(images);
} catch (IOException ex) {
ex.printStackTrace();
}
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new BorderLayout());
gui.add(new PacManComponent());
JOptionPane.showMessageDialog(null, gui);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
class PacManComponent extends JPanel {
double angle = 0d;
int preferredSize = 100;
double diff = Math.PI / 8;
boolean chomp = true;
Timer timer;
PacManComponent() {
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
};
timer = new Timer(180, listener);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(preferredSize, preferredSize);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
//double size = (getWidth() < getHeight() ? getWidth() : getHeight());
if (angle > 2 * Math.PI / 3) {
chomp = true;
} else if (angle < 0.01) {
chomp = false;
}
if (chomp) {
angle -= diff;
} else {
angle += diff;
}
PacManShape pms = new PacManShape(100, 0d);
Image image = pms.getPacManImage(angle, new Color(0, 0, 0, 0));
g2.drawImage(image, 0, 0, this);
g2.dispose();
}
}
If you wanted to transform & render images at run-time, try starting with this series of PNG format images that uses partial transparency to soften the edges.
For the animation, you could use a Swing Timer to move the Pacman graphic as well as adjusting the angle by which the "mouth" opens, by varying the parameters used by fillArc.
Interaction with KeyEvents for movement control can be acheived using Key Bindings.
Also, I would move the paint functionality to a paintComponent method in a sub-classed JComponent for better paint performance.
Related: Painting with Swing
Edit:
As youre starting Java there are a number of tasks to get working first
Create the JComponent based class with a static Pacman graphic. Move your painting logic to paintComponent
Get the Swing Timer functionality working. Follow the Oracle guide for Timers.
Implement the Key Bindings
Other functionality such as score keeping, etc.
2d animation:
http://en.wikipedia.org/wiki/File:The_Horse_in_Motion.jpg
Pseudocode:
while programActive:
deltatime = get_time_since_last_call()
update_animation_frame(deltatime)
image = get_animation_frame()
draw_background()
draw(image)
enforce_framerate(24)
To learn and do this sort of thing in Pygame would be easy compared to Java, but unsuitable for larger projects
Related
BEFORE YOU MARK IT AS DUPLICATE
I have searched a lot in the internet for that and tried every solution, but no one does it the same way I do it. In my case the rotation is in a sperate class.
I have created a java class that inherits JLabel class, in my class I have an arrow BufferedImage which I draw using the paintComponent(Graphics g) method.
I am trying to make the arrow point to a specific point (which I get from a different method) but something goes wrong and the arrow rotates to the wrong direction.
I THINK: it doesn't calculate correctly because the imageLocation is relative to the label.
Here is my code:
package pkg1;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
public final class ImageLabel extends JLabel {
private float angle = 0.0f; // in radians
private Point imageLocation = new Point();
private File imageFile = null;
private Dimension imageSize = new Dimension(50, 50);
private BufferedImage bi;
private BufferedImage resizeImage(BufferedImage originalImage, int img_width, int img_height) {
int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
BufferedImage resizedImage = new BufferedImage(img_width, img_height, type);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(originalImage, 0, 0, img_width, img_height, null);
g.dispose();
return resizedImage;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (bi == null) {
return;
}
imageLocation = new Point(getWidth() / 2 - bi.getWidth() / 2, getHeight() / 2 - bi.getHeight() / 2);
Graphics2D g2 = (Graphics2D) g;
g2.rotate(angle, imageLocation.x + bi.getWidth() / 2, imageLocation.y + bi.getHeight() / 2);
g2.drawImage(bi, imageLocation.x, imageLocation.y, null);
}
public void rotateImage(float angle) { // rotate the image to specific angle
this.angle = (float) Math.toRadians(angle);
repaint();
}
public void pointImageToPoint(Point target) {
calculateAngle(target);
repaint();
}
private void calculateAngle(Point target) {
// calculate the angle from the center of the image
float deltaY = target.y - (imageLocation.y + bi.getHeight() / 2);
float deltaX = target.x - (imageLocation.x + bi.getWidth() / 2);
angle = (float) Math.atan2(deltaY, deltaX);
if (angle < 0) {
angle += (Math.PI * 2);
}
}
}
Okay, so two things jump out at me...
If you take a Point from outside the context of the label, you will have to translate the point into the components coordinate context
The calculateAngle seems wrong
So starting with...
private void calculateAngle(Point target) {
// calculate the angle from the center of the image
float deltaY = target.y - (imageLocation.y + bi.getHeight() / 2);
float deltaX = target.x - (imageLocation.x + bi.getWidth() / 2);
angle = (float) Math.atan2(deltaY, deltaX);
if (angle < 0) {
angle += (Math.PI * 2);
}
}
angle = (float) Math.atan2(deltaY, deltaX); should be angle = (float) Math.atan2(deltaX, deltaY); (swap the deltas)
You will find that you need to adjust the result by 180 degrees in order to get the image to point in the right direction
angle = Math.toRadians(Math.toDegrees(angle) + 180.0);
Okay, I'm an idiot, but it works :P
I'd also make use of a AffineTransform to translate and rotate the image - personally, I find it easier to deal with.
In the example, I've cheated a little. I set the translation of the AffineTransform to the centre of the component, I then rotate the context around the new origin point (0x0). I then paint the image offset by half it's height/width, thus making it appear as the if the image is been rotated about it's centre - It's late, I'm tired, it works :P
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private ImageLabel label;
public TestPane() {
setLayout(new GridBagLayout());
label = new ImageLabel();
add(label);
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
label.pointImageToPoint(e.getPoint(), TestPane.this);
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public final class ImageLabel extends JLabel {
private double angle = 0;
private Point imageLocation = new Point();
private File imageFile = null;
private Dimension imageSize = new Dimension(50, 50);
private BufferedImage bi;
public ImageLabel() {
setBorder(new LineBorder(Color.BLUE));
bi = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setColor(Color.RED);
g2d.drawLine(25, 0, 25, 50);
g2d.drawLine(25, 0, 0, 12);
g2d.drawLine(25, 0, 50, 12);
g2d.dispose();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(bi.getWidth(), bi.getHeight());
}
protected Point centerPoint() {
return new Point(getWidth() / 2, getHeight() / 2);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (bi == null) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform at = g2d.getTransform();
Point center = centerPoint();
at.translate(center.x, center.y);
at.rotate(angle, 0, 0);
g2d.setTransform(at);
g2d.drawImage(bi, -bi.getWidth() / 2, -bi.getHeight() / 2, this);
g2d.dispose();
}
public void rotateImage(float angle) { // rotate the image to specific angle
this.angle = (float) Math.toRadians(angle);
repaint();
}
public void pointImageToPoint(Point target, JComponent fromContext) {
calculateAngle(target, fromContext);
repaint();
}
private void calculateAngle(Point target, JComponent fromContext) {
// calculate the angle from the center of the image
target = SwingUtilities.convertPoint(fromContext, target, this);
Point center = centerPoint();
float deltaY = target.y - center.y;
float deltaX = target.x - center.x;
angle = (float) -Math.atan2(deltaX, deltaY);
angle = Math.toRadians(Math.toDegrees(angle) + 180.0);
repaint();
}
}
}
I just want to add that using a JLabel for this purpose is overkill, a simple JPanel or JComponent would do the same job and carry a lot less overhead with it, just saying
I am display an image on JPanel and on that image i am creating a Grid of 8*8 by setStroke of Graphics2D. Now when i zooming that grid lines by setting setStroke it does not work for all images. I am thinking that this problem is associated with resolution of images. How can i solve this problem
BufferedImage bi; //loading buffered image some how
JCombobox jComboBox; //creating combobox having different zoom factor
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
int gridLinesThikness = (int) ((float) jComboBox.getSelectedIndex() * (float) (bi.getWidth() / (float) screen.width));
if (gridLinesThikness < 1)
gridLinesThikness = 1;
g2.setStroke(new BasicStroke(gridLinesThikness));
Try this code:
package sad;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class zoom extends javax.swing.JPanel {
private static int prevN = 0;
private Dimension preferredSize = new Dimension(400,400);
private Rectangle2D[] rects = new Rectangle2D[50];
public static void main(String[] args) {
JFrame jf = new JFrame("test");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(800, 800);
JPanel containerPanel = new JPanel(); // extra JPanel
containerPanel.setLayout(new GridBagLayout());
containerPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(1, 0, 1)));
zoom zoomPanel = new zoom();
containerPanel.add(zoomPanel);
zoomPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
jf.add(new JScrollPane(containerPanel));
jf.setVisible(true);
}
public zoom() {
// generate rectangles with pseudo-random coords
for (int i=0; i<rects.length; i++) {
rects[i] = new Rectangle2D.Double(
Math.random()*.8, Math.random()*.8,
Math.random()*.2, Math.random()*.2);
}
addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
updatePreferredSize(e.getWheelRotation(), e.getPoint());
}
});
}
private void updatePreferredSize(int n, Point p) {
if(n == 0) // ideally getWheelRotation() should never return 0.
n = -1 * prevN; // but sometimes it returns 0 during changing of zoom
// direction. so if we get 0 just reverse the direction.
double d = (double) n * 1.08;
d = (n > 0) ? 1 / d : -d;
int w = (int) (getWidth() * d);
int h = (int) (getHeight() * d);
preferredSize.setSize(w, h);
int offX = (int)(p.x * d) - p.x;
int offY = (int)(p.y * d) - p.y;
getParent().setLocation(getParent().getLocation().x-offX,getParent().getLocation().y-offY);
//in the original code, zoomPanel is being shifted. here we are shifting containerPanel
getParent().doLayout(); // do the layout for containerPanel
getParent().getParent().doLayout(); // do the layout for jf (JFrame)
prevN = n;
}
#Override
public Dimension getPreferredSize() {
return preferredSize;
}
private Rectangle2D r = new Rectangle2D.Float();
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.red);
int w = getWidth();
int h = getHeight();
for (Rectangle2D rect : rects) {
r.setRect(rect.getX() * w, rect.getY() * h,
rect.getWidth() * w, rect.getHeight() * h);
((Graphics2D)g).draw(r);
}
}
}
I have a simple animation in Java that consists of a wheel moving across a window. It is just a plain circle that starts off of the screen from the left, enters and continues to the right until it goes off of the screen. Then it loops and repeats this process.
X is a variable that contains the position of the wheel. It can be between -(wheel width) and the window size + the wheel width.
I would like to simulate rotation by drawing a circle within this wheel, that rotates around the circle as if it were attached.
Imagine a bike wheel in real life with a red flag on the wheel. As the wheel rotates, the red flag would be on the edge on the wheel moving as the wheel progresses. This is the behavior I want.
I am getting a percentage to pass into my wheel class like this:
int percentage = x/windowWidth;
Each frame that the wheel moves, I call wheel.rotate(percentage).
This is the implementation:
private int diameter = 50;
private final int SPOKE_DIAMETER = diameter/5;
public void rotate(double percent){
this.percent = percent;
this.theta = percent*(PI*2);
System.out.println(percent*PI);
}
public void paintComponent(Graphics canvas)
{
// wheel
canvas.setColor(Color.gray);
canvas.fillOval(0, 0, diameter, diameter);
// spinning flag
canvas.setColor(Color.red);
canvas.fillOval((int)(percent*diameter),(int)((sin((percent*(PI*2)))*diameter)), SPOKE_DIAMETER,SPOKE_DIAMETER);
}
The x location works more or less how I wanted, but the y does not. It wiggles like a sin wave, which is expected (I did use sin...), however, I'm not sure how to alter my math to follow the circle around.
What is wrong with my implementation? (I'm not very good with drawing with trigonometric functions)
Basically, you need to calculate the point on the circle, based on an angle that the object should appear...
Like most things, I stole this off the internet somewhere, but it works...
protected Point getPointOnCircle(float degress, float radius) {
int x = Math.round(getWidth() / 2);
int y = Math.round(getHeight() / 2);
double rads = Math.toRadians(degress - 90); // 0 becomes the top
// Calculate the outter point of the line
int xPosy = Math.round((float) (x + Math.cos(rads) * radius));
int yPosy = Math.round((float) (y + Math.sin(rads) * radius));
return new Point(xPosy, yPosy);
}
Based on an angel (in degrees) and the radius of the circle, this will return the x/y position along the circumference of the circle...
import java.awt.Color;
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.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RotateWheel {
public static void main(String[] args) {
new RotateWheel();
}
public RotateWheel() {
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 float degrees = 0;
public TestPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
degrees += 0.5f;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int diameter = Math.min(getWidth(), getHeight());
int x = (getWidth() - diameter) / 2;
int y = (getHeight() - diameter) / 2;
g2d.setColor(Color.GREEN);
g2d.drawOval(x, y, diameter, diameter);
g2d.setColor(Color.RED);
float innerDiameter = 20;
Point p = getPointOnCircle(degrees, (diameter / 2f) - (innerDiameter / 2));
g2d.drawOval(x + p.x - (int) (innerDiameter / 2), y + p.y - (int) (innerDiameter / 2), (int) innerDiameter, (int) innerDiameter);
g2d.dispose();
}
protected Point getPointOnCircle(float degress, float radius) {
int x = Math.round(getWidth() / 2);
int y = Math.round(getHeight() / 2);
double rads = Math.toRadians(degress - 90); // 0 becomes the top
// Calculate the outter point of the line
int xPosy = Math.round((float) (x + Math.cos(rads) * radius));
int yPosy = Math.round((float) (y + Math.sin(rads) * radius));
return new Point(xPosy, yPosy);
}
}
}
Background
Im total Java newbie, today I started learning it (with thenewboston.org). I already know how to make simple windows/forms/gui, how to draw lines etc.
My goal is to create in Java gauge like this:
This is gauge which I created in .NET C# WPF, and now I want to rewrite this to Java.
Main question:
How to create triangle or other shape with some transparency and rotate it?
I tried to draw something by using Graphics object like this:
public void paint(Graphics g){
g.drawLine(0, 0, 100, 100);
}
But I think this is wrong direction, because when I put something on graphics - it just stays there, I can't move or rotate it.
I have to clear whole graphics and draw it again to make kind of "animation", or there is easier way?
Edit:
I already know how to antialias (Hovercraft Full Of Eels already helped me in this - thanks).
Edit2:
My code actually looks like this:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MainWindow extends JPanel {
private Point p1 = new Point(100, 100);
private Point p2 = new Point(740, 450);
public MainWindow() {
this.setPreferredSize(new Dimension(800, 600));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawLines(g);
}
private void drawLines(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.DARK_GRAY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
private void display() {
JFrame f = new JFrame("Main Window");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
new MainWindow().display();
}
}
You state:
I tried to draw something by using Graphics object like this:
public void paint(Graphics g){
g.drawLine(0, 0, 100, 100);
}
But I think this is wrong direction, because when I put something on graphics - it just stays there, I can't move or rotate it.
I have to clear whole graphics and draw it again to make kind of "animation", or there is easier way?
Suggestions:
Don't hard-code your numbers. Use class fields (variables) instead so that your program can change the position of items drawn easily.
Don't override a component's paint(...) method. Instead override the paintComponent(Graphics g) method of an object that derives from JComponent or one of its children such as JPanel. This will give you the benefit of automatic double-buffering for smoother animation, and also will reduce the likelihood of erroneous drawing of a component's children or borders.
Cast your Graphics object to a Graphics2D object so that you can do more advanced drawing using classes that implement the Shape interface, including Rectangle2D, Ellipse2D, Line2D, Path2D, and many more.
Draw the background image as a BufferedImage using Graphics#drawImage(...) method, and then draw your moving images on top of this, again using the Graphics2D object and again changing the images drawn based on the state of the object (the values held by its fields).
Be careful when doing animations that you obey Swing threading rules, that you don't have any animation or game loops that tie up the Swing thread. A Swing Timer can allow you to create a quick and easy (albeit somewhat primitive) game loop.
For example:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class DailAnimation extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = 350;
private static final Point2D CENTER = new Point2D.Double(PREF_W / 2.0,
PREF_W / 2.0);
private static final double RADIUS = PREF_W / 2.0;
private static final Color LARGE_TICK_COLOR = Color.green;
private static final Color CENTER_HUB_COLOR = Color.LIGHT_GRAY;
private static final Stroke LARGE_TICK_STROKE = new BasicStroke(3f);
private static final int LRG_TICK_COUNT = 9;
private static final double TOTAL_LRG_TICKS = 12;
private static final double LRG_TICK_OUTER_RAD = 0.9;
private static final double LRG_TICK_INNER_RAD = 0.8;
private static final int START_TICK = 10;
private static final double CENTER_HUB_RADIUS = 10;
public static final int MAX_SPEED = 100;
private static final double INIT_SPEED = 0;
private static final double DIAL_INNER_RAD = 0.02;
private static final double DIAL_OUTER_RAD = 0.75;
private static final Color DIAL_COLOR = Color.DARK_GRAY;
private BufferedImage backgroundImg;
private double speed;
private double theta;
private double cosTheta;
private double sinTheta;
public DailAnimation() {
setBackground(Color.white);
backgroundImg = createBackgroundImg();
setSpeed(INIT_SPEED);
}
public void setSpeed(double speed) {
if (speed < 0) {
speed = 0;
} else if (speed > MAX_SPEED) {
speed = MAX_SPEED;
}
this.speed = speed;
this.theta = ((speed / MAX_SPEED) * LRG_TICK_COUNT * 2.0 + START_TICK)
* Math.PI / TOTAL_LRG_TICKS;
cosTheta = Math.cos(theta);
sinTheta = Math.sin(theta);
repaint();
}
private BufferedImage createBackgroundImg() {
BufferedImage img = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(LARGE_TICK_COLOR);
g2.setStroke(LARGE_TICK_STROKE);
for (int i = 0; i < LRG_TICK_COUNT; i++) {
double theta = (i * 2.0 + START_TICK) * Math.PI / TOTAL_LRG_TICKS;
double cosTheta = Math.cos(theta);
double sinTheta = Math.sin(theta);
int x1 = (int) (LRG_TICK_INNER_RAD * RADIUS * cosTheta + CENTER.getX());
int y1 = (int) (LRG_TICK_INNER_RAD * RADIUS * sinTheta + CENTER.getY());
int x2 = (int) (LRG_TICK_OUTER_RAD * RADIUS * cosTheta + CENTER.getX());
int y2 = (int) (LRG_TICK_OUTER_RAD * RADIUS * sinTheta + CENTER.getY());
g2.drawLine(x1, y1, x2, y2);
}
g2.setColor(CENTER_HUB_COLOR);
int x = (int) (CENTER.getX() - CENTER_HUB_RADIUS);
int y = (int) (CENTER.getY() - CENTER_HUB_RADIUS);
int width = (int) (2 * CENTER_HUB_RADIUS);
int height = width;
g2.fillOval(x, y, width, height);
// g2.draw(ellipse);
g2.dispose();
return img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (backgroundImg != null) {
g.drawImage(backgroundImg, 0, 0, this);
}
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(DIAL_COLOR);
int x1 = (int) (DIAL_INNER_RAD * RADIUS * cosTheta + CENTER.getX());
int y1 = (int) (DIAL_INNER_RAD * RADIUS * sinTheta + CENTER.getY());
int x2 = (int) (DIAL_OUTER_RAD * RADIUS * cosTheta + CENTER.getX());
int y2 = (int) (DIAL_OUTER_RAD * RADIUS * sinTheta + CENTER.getY());
g.drawLine(x1, y1, x2, y2);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
final DailAnimation mainPanel = new DailAnimation();
JFrame frame = new JFrame("DailAnimation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
int delay = 100;
new Timer(delay, new ActionListener() {
int speed = 0;
#Override
public void actionPerformed(ActionEvent evt) {
speed ++;
if (speed > DailAnimation.MAX_SPEED) {
((Timer)evt.getSource()).stop();
}
mainPanel.setSpeed(speed);
}
}).start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Drawing a line via Graphics.drawLine() writes pixels directly to whatever is backing the Graphics instance. If you want to rotate the line, you must calculate what its coordinates should be when rotated. This is the only way to draw things in AWT and Swing.
You could write a needle class that maintained its angle, and then have it handle its rendering every frame.
I want to make the pacman open/close mouth animation using the easiest method.
Here is my recent code: The problem is, nothing is happening?
package ordner;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class PacMan implements ActionListener {
private JFrame frame;
private DrawPanel panel;
private void initGui() {
frame = new JFrame("Pacman");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new DrawPanel();
frame.add(panel);
panel.setBackground(Color.BLACK);
frame.setSize(300, 300);
frame.setVisible(true);
}
public static void main(String[] args) {
PacMan pm = new PacMan();
pm.initGui();
}
#Override
public void actionPerformed(ActionEvent e) {
panel.repaint();
}
}
and here is my draw panel:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DrawPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.yellow);
g.fillArc(70,50,150,150,30,300);
int i = 0;
while ( i <= 60) {
g.fillArc(70,50,150,150,30-i,300+i+i);
try {
Thread.sleep(25);
}
catch (Exception e) {
Thread.currentThread().interrupt();
}
i++;
}
}
}
The while loop doesn't affect anything, what could be the reason for that?
Something like this might work for PacMan images. It uses a Java 2D based Shape instance to represent the form, and an AffineTransform to produce the different orientations.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.io.*;
import javax.imageio.ImageIO;
class PacManShape {
private double size;
private double rotation;
final int maxSize = 4;
static File home = new File(System.getProperty("user.home"));
static File images = new File(home, "images");
PacManShape(int size, double rotation) {
this.size = size;
this.rotation = rotation;
}
public Area getPacManShape(double jaws) {
Area area = new Area(new Ellipse2D.Double(0d, 0d, size, size));
double x1 = size / 2 + (2d * size * Math.cos(jaws / 2d));
double y1 = size / 2 + (2d * size * Math.sin(jaws / 2d));
double x2 = x1;
double y2 = size / 2 - (2d * size * Math.sin(jaws / 2d));
Polygon mouth = new Polygon();
mouth.addPoint((int) (size / 2), (int) (size / 2));
mouth.addPoint((int) x1, (int) y1);
mouth.addPoint((int) x2, (int) y2);
mouth.addPoint((int) (size / 2), (int) (size / 2));
area.subtract(new Area(mouth));
return area;
}
public BufferedImage getPacManImage(double angle, Color color) {
BufferedImage bi = new BufferedImage(
(int) size, (int) size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bi.createGraphics();
g2.setColor(color);
g2.fillRect(0, 0, (int) size, (int) size);
AffineTransform rotate = AffineTransform.getRotateInstance(
rotation, size / 2, size / 2);
g2.setTransform(rotate);
Area pacMan = getPacManShape(angle);
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.YELLOW);
float[] dist = {.15f, .9f};
Color[] colors = {Color.YELLOW, Color.ORANGE};
Point2D center = new Point2D.Double(size / 2, size / 2);
RadialGradientPaint radial = new RadialGradientPaint(
center, (float) ((size / 2) - 2f), dist, colors);
g2.setPaint(radial);
g2.fill(pacMan);
GradientPaint gradient = new GradientPaint(
0, 0, new Color(255, 255, 225, 220),
(int) (size / 3), 0, new Color(255, 255, 255, 0));
g2.setPaint(gradient);
g2.fill(pacMan);
g2.dispose();
return bi;
}
public void savePacManImage(int q, int num) throws IOException {
double angle = Math.PI*2 / 3d * ((double) num / (double) maxSize);
BufferedImage bi = getPacManImage(angle, Color.WHITE);
images.mkdirs();
File img = new File(images, "PacMan-" + q + "x" + num + ".gif");
ImageIO.write(bi, "gif", img);
}
public static void main(String[] args) {
try {
for (int ii = 0; ii < 4; ii++) {
PacManShape pms = new PacManShape(100, (double) ii * Math.PI / 2d);
for (int jj = 0; jj <= pms.maxSize; jj++) {
pms.savePacManImage(ii, jj);
}
}
Desktop.getDesktop().open(images);
} catch (IOException ex) {
ex.printStackTrace();
}
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new BorderLayout());
gui.add(new PacManComponent());
JOptionPane.showMessageDialog(null, gui);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
class PacManComponent extends JPanel {
double angle = 0d;
int preferredSize = 100;
double diff = Math.PI / 8;
boolean chomp = true;
Timer timer;
PacManComponent() {
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
};
timer = new Timer(180, listener);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(preferredSize, preferredSize);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
//double size = (getWidth() < getHeight() ? getWidth() : getHeight());
if (angle > 2 * Math.PI / 3) {
chomp = true;
} else if (angle < 0.01) {
chomp = false;
}
if (chomp) {
angle -= diff;
} else {
angle += diff;
}
PacManShape pms = new PacManShape(100, 0d);
Image image = pms.getPacManImage(angle, new Color(0, 0, 0, 0));
g2.drawImage(image, 0, 0, this);
g2.dispose();
}
}
If you wanted to transform & render images at run-time, try starting with this series of PNG format images that uses partial transparency to soften the edges.
For the animation, you could use a Swing Timer to move the Pacman graphic as well as adjusting the angle by which the "mouth" opens, by varying the parameters used by fillArc.
Interaction with KeyEvents for movement control can be acheived using Key Bindings.
Also, I would move the paint functionality to a paintComponent method in a sub-classed JComponent for better paint performance.
Related: Painting with Swing
Edit:
As youre starting Java there are a number of tasks to get working first
Create the JComponent based class with a static Pacman graphic. Move your painting logic to paintComponent
Get the Swing Timer functionality working. Follow the Oracle guide for Timers.
Implement the Key Bindings
Other functionality such as score keeping, etc.
2d animation:
http://en.wikipedia.org/wiki/File:The_Horse_in_Motion.jpg
Pseudocode:
while programActive:
deltatime = get_time_since_last_call()
update_animation_frame(deltatime)
image = get_animation_frame()
draw_background()
draw(image)
enforce_framerate(24)
To learn and do this sort of thing in Pygame would be easy compared to Java, but unsuitable for larger projects