I've a VERY simple AWT Painting. Just playing aound to make something bigger. But can't get it working ...
What happens is that only elypse2 is shown - regardless of repaint()ing it or not.
I also tried to use Swing components instead of AWT (JFrame, JComponent) but this also changes nothing.
Is using a Layout Manager necessary? But I want to draw only graphical components, like arcs, rectangles, line, poly-lines, aso ...
Here's the main():
public static void main(String[] args) {
Frame testFrame = new Frame("Grafx-Test");
testFrame.setSize(300, 200);
testFrame.setAlwaysOnTop(true);
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
testFrame.setVisible(true);
}
});
Elypse elypse = new Elypse(new Point(70, 80), 30, 30, Color.BLUE, false);
testFrame.add(elypse);
Elypse elypse2 = new Elypse(new Point(70, 50), 50, 30, Color.BLUE, true);
testFrame.add(elypse2);
}
and here the used class:
public class Elypse extends Canvas {
private Point start;
private int width;
private int height;
private Color c;
private boolean filled;
public Elypse(Point start, int width, int height, Color c, boolean filled) {
this.start = start;
this.width = width;
this.height = height;
this.c = c;
this.filled = filled;
}
#Override
public void paint(Graphics g) {
g.setColor(c);
if (filled) {
g.fillOval(start.x, start.y, width, height);
}
else {
g.drawOval(start.x, start.y, width, height);
}
}
}
You neglect to pack() the enclosing Window. Note the characteristic symptom in your original code: resizing the frame, which generates an update, causes elypse2 to appear.
Addendum: You can see both Elypse instances by using a layout such as GridLayout.
testFrame.setLayout(new GridLayout(0, 1));
As tested:
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Point;
public class Test {
public static void main(String[] args) {
Frame testFrame = new Frame("Grafx-Test");
testFrame.setAlwaysOnTop(true);
Elypse elypse = new Elypse(new Point(70, 80), 30, 30, Color.BLUE, false);
testFrame.add(elypse);
Elypse elypse2 = new Elypse(new Point(70, 50), 50, 30, Color.BLUE, true);
testFrame.add(elypse2);
testFrame.pack();
testFrame.setVisible(true);
}
private static class Elypse extends Canvas {
private Point start;
private int width;
private int height;
private Color c;
private boolean filled;
public Elypse(Point start, int width, int height, Color c, boolean filled) {
this.start = start;
this.width = width;
this.height = height;
this.c = c;
this.filled = filled;
}
#Override
public void paint(Graphics g) {
g.setColor(c);
if (filled) {
g.fillOval(start.x, start.y, width, height);
} else {
g.drawOval(start.x, start.y, width, height);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
}
}
Related
I'm trying to set an animated gradient background for a JPanel. The effect works, but I would like to get a smooth transition when it starts again. Below is my current implementation. When the xvalue2 reaches a limit set, I swap the colors and start again.
public class GradientAnimation {
static class GradientPanel extends JPanel {
private static final long serialVersionUID = -4185583782901846967L;
private Timer timer;
private float Xend;
private final float MAXVALUE = 800f;
private Color color1 = new Color(128,62,153,255);
private Color color2 = new Color(192,201,200,255);
GradientPanel() {
Xend = 0f;
setOpaque(true);
ActionListener action = new ActionListener(){
#Override
public void actionPerformed(ActionEvent evt){
if (Xend < MAXVALUE) Xend+=2f;
else{
Color aux = color1;
color1 = color2;
color2 = aux;
Xend = 0f;
}
revalidate();
repaint();
}
};
timer = new Timer(5, action);
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
final BufferedImage image = new BufferedImage(
getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
g2d = image.createGraphics();
GradientPaint prim = new GradientPaint(0f, 0f, color1,
Xend, 0f, color2);
g2d.setPaint(prim);
g2d.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(image, 0, 0, null);
}
}
private static void createAndShowUI() {
try {
JFrame frame = new JFrame("Gradient Animation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
GradientPanel imagePanel = new GradientPanel();
frame.add(imagePanel);
frame.setSize(400, 400);
frame.setVisible(true);
}
catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Again, I want to hide the moment that I swap the colors to have a perfect loop of gradient animation. Please let me know if the code looks correct and how could I improve the quality.
In the variation below,
Use Color.getHSBColor() to cycle through the available hues; because the hue values wrap around, the transition though the spectrum is smooth. Alternatively, change the sign of delta at the end-points.
Override getPreferredSize() to establish the initial panel geometry.
Don't buffer rendering unnecessarily.
Don't revalidate components unnecessarily.
How would I adapt it to avoid cycling through all colors?
Endless variations are possible; the critical issue is avoiding abrupt changes. Here, HUE_MIN and HUE_MAX are narrowed to a slice of the spectrum, the sign of delta is changed at the end-points, and the other HSB components get updated.
private static final float HUE_MIN = 4f/6;
private static final float HUE_MAX = 5f/6;
…
#Override
public void actionPerformed(ActionEvent evt) {
hue += delta;
if (hue > HUE_MAX) {
hue = HUE_MAX;
delta = -delta;
}
if (hue < HUE_MIN) {
hue = HUE_MIN;
delta = -delta;
}
color1 = Color.getHSBColor(hue, 1, 1);
color2 = Color.getHSBColor(hue, 3f/4 + delta, 3f/4 + delta);
repaint();
}
Code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
/** #see https://stackoverflow.com/q/45603312/230513 */
public class GradientAnimation {
static class GradientPanel extends JPanel {
private static final int WIDE = 640;
private static final int HIGH = 240;
private static final float HUE_MIN = 0;
private static final float HUE_MAX = 1;
private final Timer timer;
private float hue = HUE_MIN;
private Color color1 = Color.white;
private Color color2 = Color.black;
private float delta = 0.01f;
GradientPanel() {
ActionListener action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
hue += delta;
if (hue > HUE_MAX) {
hue = HUE_MIN;
}
color1 = Color.getHSBColor(hue, 1, 1);
color2 = Color.getHSBColor(hue + 16 * delta, 1, 1);
repaint();
}
};
timer = new Timer(10, action);
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
GradientPaint p = new GradientPaint(
0, 0, color1, getWidth(), 0, color2);
g2d.setPaint(p);
g2d.fillRect(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDE, HIGH);
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("Gradient Animation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GradientPanel imagePanel = new GradientPanel();
frame.add(imagePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
I am learning java gui interface and wrote a program that has a button. Each time the button is clicked, a random sized rectangle will be added to the screen. But instead of adding it to the screen, the program keeps erasing the old one, which I want to keep on the screen. Here is my code. I tried to do paint() and it did not work. Thanks in advance.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class SimpleGui implements ActionListener {
JFrame frame = new JFrame();
public static void main(String[] args){
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go(){
JButton button = new JButton("Add a rectangle");
MyDrawPanel panel = new MyDrawPanel();
button.addActionListener(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event){
frame.repaint();
}
class MyDrawPanel extends JPanel{
public void paintComponent(Graphics g){
g.setColor(Color.blue);
int height = (int) (Math.random()*120 + 10);
int width = (int) (Math.random()*120 + 10);
int x = (int) (Math.random()*40 + 10);
int y = (int) (Math.random()*40 + 10);
g.fillRect(x, y, height, width);
}
}
}
Your paintComponent method is written to draw only one rectangle, so its behavior should come as no shock to you. If you want it to draw multiple, you have one of two options:
Create an ArrayList<Rectangle>, and in the actionPerformed method, add a new random Rectangle to this List and then call repaint(). In the paintComponent method, iterate through this List with a for-loop, painting each Rectangle.
Or you could draw the new random rectangle onto a BufferedImage that is displayed by the paintComponent method.
The first method is the easier of the two, the 2nd is better if you're worried about program responsiveness, say in an animation program.
For example:
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.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class TwoDrawRectMethods extends JPanel {
// Array to hold our two drawing JPanels
private AddRandomRect[] addRandomRects = {
new DrawList("Using List"),
new DrawBufferedImage("Using BufferedImage")};
// constructor
public TwoDrawRectMethods() {
// add drawing rectangles onto GUI
for (AddRandomRect addRandomRect : addRandomRects) {
add(addRandomRect);
}
// button to tell rectangles to add a new Rectangle
add(new JButton(new DrawAction("Add New Rectangle")));
}
// The button's Action -- an ActionListener on "steroids"
private class DrawAction extends AbstractAction {
public DrawAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// tell both drawing JPanels to add a new rectangle
for (AddRandomRect addRandomRect : addRandomRects) {
addRandomRect.addRectangle();
}
}
}
private static void createAndShowGui() {
TwoDrawRectMethods mainPanel = new TwoDrawRectMethods();
JFrame frame = new JFrame("TwoDrawRectMethods");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class DrawList extends AddRandomRect {
private static final Color RECT_COLOR = Color.RED;
private List<Rectangle> rectList = new ArrayList<>();
public DrawList(String title) {
super(title);
}
#Override
public void addRectangle() {
rectList.add(createRandomRect());
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(RECT_COLOR);
for (Rectangle rectangle : rectList) {
g2.draw(rectangle);
}
}
}
#SuppressWarnings("serial")
class DrawBufferedImage extends AddRandomRect {
private static final Color RECT_COLOR = Color.BLUE;
private BufferedImage img = null;
public DrawBufferedImage(String title) {
super(title);
}
#Override
public void addRectangle() {
if (img == null) {
img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Rectangle rect = createRandomRect();
Graphics2D g2 = img.createGraphics();
g2.setColor(RECT_COLOR);
g2.draw(rect);
g2.dispose();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
}
}
#SuppressWarnings("serial")
abstract class AddRandomRect extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private Random random = new Random();
public AddRandomRect(String title) {
setBorder(BorderFactory.createTitledBorder(title));
}
abstract void addRectangle();
protected Rectangle createRandomRect() {
int x1 = random.nextInt(PREF_W);
int x2 = random.nextInt(PREF_W);
int y1 = random.nextInt(PREF_H);
int y2 = random.nextInt(PREF_H);
int x = Math.min(x1, x2);
int y = Math.min(y1, y2);
int width = Math.abs(x1 - x2);
int height = Math.abs(y1 - y2);
return new Rectangle(x, y, width, height);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
I'm trying to make two blinking circles that blink at different rates. I'm using ScheduledExecutorService in the Circle class to regulate the blinking, and its duration is set by the ms (milliseconds) variable in each Circle.
When I make one car individually, they blink at the correct rates (I have the black one set to 1000ms, the red set to 10ms). However, when I create them both and add them to my JLayeredPane, they both blink at the shorter period.
I'm not too familiar with the use of ScheduledExecutorService, so if someone could help me out with what's going wrong it'd be greatly appreciated!
import java.awt.Color;
import java.awt.Graphics;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.awt.*;
import javax.swing.*;
public class blinker extends JFrame
{
JLayeredPane lp = new JLayeredPane();
public carlight()
{
lp.setLayout(new BorderLayout());
lp.setPreferredSize(new Dimension(450, 450));
car c1 = new car(new Color(0, 0, 0), "1", 10, 0, 0);
c1.setOpaque(false);
car c2 = new car(new Color(255, 0, 0), "2", 1000, 100, 100);
c2.setOpaque(false);
c1.setBounds(0, 0, 450, 450);
c2.setBounds(0, 0, 450, 450);
lp.add(c2);
lp.add(c1);
add(lp);
setTitle("Carlights");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 500);
setVisible(true);
}
public static void main(String[] args)
{
carlight cl = new carlight();
}
}
class Circle extends JPanel
{
private Color color;
private String name;
private long ms;
private int x, y;
private boolean on = true;
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
#Override
public void paintComponent(Graphics g)
{
super.paintComponents(g);
if(on)
{
g.setColor(color);
int r = 50;
g.fillOval(x, y, r, r);
on = false;
}
else
{
on = true;
}
}
public car(Color c, String s, long l, int x, int y)
{
color = c;
name = s;
ms = l;
this.x = x;
this.y = y;
this.service.scheduleAtFixedRate(new Runnable()
{
public void run()
{
repaint();
}
}, 0, ms, TimeUnit.MILLISECONDS);
}
}
Your problem is that you have program logic in the paintComponent method, where you change the state of the boolean variable. You don't have full control over when or even if this method will be called, and in fact both paintComponents will be called when repaint is called which is why your blinkers aren't working. The solution: get the logic out of the paintComponent method by changing the state of the boolean field, on, elsewhere. Also you'll want to use a Swing Timer for better Swing threading.
You also will want to fix your use of layouts including avoiding use of setBounds. This is especially dangerous and unpredictable in your setup, using it with a BorderLayout. Myself, I'd not make the Circle class extend a JPanel but rather make it a logical class, not a component class, and then I'd have the drawing component, a class that does extend JPanel, hold instances of Circle classes and then draws them in its paintComponent. For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class BlinkerEg extends JPanel {
private static final int PREF_W = 450;
private static final int PREF_H = PREF_W;
private List<Circle> circles = new ArrayList<>();
public BlinkerEg() {
circles.add(new Circle(Color.red, 1000, 0, 0, 450, this));
circles.add(new Circle(Color.black, 60, 0, 0, 450, this));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Circle circle : circles) {
circle.paint(g2);
}
}
private static void createAndShowGui() {
BlinkerEg mainPanel = new BlinkerEg();
JFrame frame = new JFrame("BlinkerEg");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class Circle {
private Color color;
private int x, y;
private int diam;
private JComponent component;
private boolean on = true;
public Circle(Color color, int ms, int x, int y, int diam, JComponent component) {
this.color = color;
this.x = x;
this.y = y;
this.diam = diam;
this.component = component;
new Timer(ms, new TimerListener()).start();
}
public void paint(Graphics g) {
if (on) {
g.setColor(color);
g.fillOval(x, y, diam, diam);
}
}
public boolean isOn() {
return on;
}
public void setOn(boolean on) {
this.on = on;
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
setOn(!isOn());
component.repaint();
}
}
}
I have a Class Circle with a button and a Class with a jPanel what i want to do is when that button is clicked a circle will be drawn on the panel and each time i click that button and change x and y "some how not implemented here" i got a circle on the JPanel over and over .
How to do that, or is there a way to do what i descriped regardless of my code but i want the class circle to extends Shape.
public class Window{
private JFrame frame;
private JPanel panel = new JPanel();
Circle c = new Circle(frame, panel);
// some other buttons
.
.
// some code to set the panel grid bag constaraints and background then
frame.getContentPane().add(panel, gbc_panel);
}
then the Circle Class
public class Circle extends Shape implements ActionListener{
private JPanel Panel;
private GridBagConstraints gbc_btnCircle;
private JButton btnCircle;
public void setPanel(JPanel panel) {
Panel = panel;
}
public Circle(JFrame frame, JPanel panel){
btnCircle = new JButton("Circle");
// some code to set grid bag constraint then
frame.getContentPane().add(btnCircle, gbc_btnCircle);
setPanel(panel);
btnCircle.addActionListener(this);
}
public void paint(Graphics g) {
super.paintComponents(g);
g.setColor(Color.red);
g.fillOval(100, 100, 100, 100);
Panel.add(this);
}
public void actionPerformed(ActionEvent arg0) {
repaint();
}
}
You kinda have the wrong idea. In your drawing panel, you should have a List<Circle>. And in the paintComponent method of the drawing panel, you should iterate through the list to draw each circle
class Circle {
int x, int y, int width, int height;
public Circle (int x, int y, int width, int height) {
... set em
}
public void draw(Graphics g) {
g.fillOval(x, y, width, height);
}
}
class DrawingPanel extends JPanel {
List<Circle> circles = new ArrayList<>();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Circle circle : circles) {
circle.draw(g);
}
}
// Dont forget to override public Dimension getPreferredSize()
}
To add more Circles to the list, just have a addCircle method in the DrawingPanel class
public void addCircle(Circle circle) {
circles.add(circle);
repaint();
}
As far as the button, you should be creating it in the Window class. In the ActionListener, just create a new Circle and add it the DrawingPanel by calling the addCircle method
An aside, Circle doesn't need the extend Shape. The Shape API already has an Ellipse2D class, which you can create circles from
class DrawingPanel extends JPanel {
List<Ellipse2D> circles = new ArrayList<>();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g.create();
for (Ellipse2D circle : circles) {
g2.fill(circle);
}
g2.dispose();
}
// Dont forget to override public Dimension getPreferredSize()
}
See 2D Graphics
UPDATE: full example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CirclesDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new CirclesDemo();
}
});
}
public CirclesDemo() {
JFrame frame = new JFrame();
frame.add(panel);
frame.add(createButton(), BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private final DrawingPanel panel = new DrawingPanel();
private JButton createButton() {
JButton button = new JButton("Add Circle");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
int[] circleValues = generateRandomValues(300, 300, 50, 150);
int x = circleValues[0];
int y = circleValues[1];
int width = circleValues[2];
int height = width;
Circle circle = new Circle(x, y, width, height);
panel.addCircle(circle);
}
});
return button;
}
private int[] generateRandomValues(int maxX, int maxY,
int minSize, int maxSize) {
Random random = new Random();
int[] values = new int[3];
values[0] = random.nextInt(maxX);
values[1] = random.nextInt(maxY);
values[2] = Math.min(random.nextInt(maxSize) + minSize, maxSize);
return values;
}
class Circle {
int x, y, width, height;
public Circle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void draw(Graphics g) {
g.drawOval(x, y, width, height);
}
}
class DrawingPanel extends JPanel {
List<Circle> circles = new ArrayList<>();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Circle circle : circles) {
circle.draw(g);
}
}
public void addCircle(Circle circle) {
circles.add(circle);
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
}
You are overriding the paint method of Circle. You need to be overriding the paint method of the panel.
It always works with images but rectangles and ovals never buffer right. I have a basic game loop in my gamepanel class that draws the player repeatedly. It doesn't remove the rectangle, just leaves a trace. I want to use a rectangle instead of an image for learning purposes. I tried using repaint in the game loop, but it flickered like crazy and still didn't work. I looked at another tutorial on this in this website but they used opengl witch is foreign to me and I don't want to take the time to figure it out.
JFrame:
import javax.swing.JFrame;
public class Game {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setTitle("OMG I MADE A GAME");
f.setResizable(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(new Panel());
f.pack();
f.setVisible(true);
}
}
JPanel Class:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.event.*;
import javax.swing.JPanel;
import com.game.entity.Player;
public class Panel extends JPanel implements Runnable, KeyListener{
private static final long serialVersionUID = -5122190028751177848L;
// dimensions
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
// game thread
private Thread thread;
private boolean running;
// image
private BufferedImage image;
private Graphics2D g;
private Player p;
public Panel() {
super();
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
// DRAWS PANEL TO FRAME
public void addNotify() {
super.addNotify();
if(thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
private void init() {
image = new BufferedImage(
WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB );
g = (Graphics2D) image.getGraphics();
p = new Player(100, 100);
running = true;
}
public void run() {
init();
// game loop
while(running) {
update();
draw();
drawToScreen();
System.out.println("ELAPSED :" + System.nanoTime()/ 1000000 + " Seconds");
try {
Thread.sleep(10);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
private void update() {
p.update();
}
private void draw(){
// NAME (remember it loops)
String name = "2014 Jay H.";
g.setFont(new Font("Name", 0, 12));
g.setColor(Color.WHITE);
g.drawString(name, 0, 10);
g.setColor(Color.BLUE);
g.fillRect( 0, 10, 65, 5);
//TITLE looks sexy :D
g.setColor(Color.GREEN);
g.setFont(new Font("Title", 0, WIDTH/ 10));
g.drawString("JAY'S GAME", WIDTH/ 5, 100);
//DRAW PLAYER
p.draw(g);
}
// SCREEN IMAGE (dont have to use. Just use this^)
private void drawToScreen() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0,
WIDTH * SCALE, HEIGHT * SCALE,null);
g2.dispose();
}
public void keyTyped(KeyEvent key) {}
// PUBLIC KEYRELEASES
public void keyPressed(KeyEvent key) {
int KeyCode = key.getKeyCode();
//EXIT SYSTEM
if(KeyCode == KeyEvent.VK_Q) {System.exit(0);
} //UP
if(KeyCode == KeyEvent.VK_W){p.setDY(-2);}
}
// PUBLIC KEYRELEASES
public void keyReleased(KeyEvent key) {
int KeyCode = key.getKeyCode();
//UP
if(KeyCode == KeyEvent.VK_W) {p.setDY(0);}
}
}
Player Class:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.ImageIcon;
//FOR NOW THE PLAYER IS A RECTANGLE
public class Player {
// PLAYER CORDINATES AND VELOCITY
int x,y,dx,dy;
public Player(int x, int y) {
//NEEDED TO USE THE X AND Y
this.x =x;
this.y = y;
}
public void update() {
x += dx;
y += dy;
}
// DRAW TO PANEL CLASS
public void draw(Graphics2D g) {
//BODY
g.setColor(Color.PINK);
g.fillRect(x, y, 20, 20);
//EYES
g.setColor(Color.BLACK);
g.fillRect(x+3, y+2, 5, 10);
g.fillRect(x+ 12, y+2, 5, 10);
//EYERIS
g.setColor(Color.WHITE);
g.fillRect(x+3, y+2, 2, 10);
g.fillRect(x+15, y+2, 2, 10);
//NOSE
g.setColor(Color.MAGENTA);
g.fillRect(x+5, y+13, 10, 5);
//NOSTRILLS
g.setColor(Color.red);
g.fillRect(x+6, y+15, 2, 2);
g.fillRect(x+12, y+15, 2, 2);
}
//GET METHODS FOR CORDINATES AND VELOCITY (Unused for now... i think)
public int getX() {return x;}
public int getY() {return y;}
public int getDX() {return dx;}
public int getDY() {return dy;}
//SET METHODS TO CHANGE
public void setX(int x) {this.x = x;}
public void setY(int y) {this.y = y;}
public void setDX(int dx) {this.dx = dx;}
public void setDY(int dy) {this.dy = dy;}
}
You need to "reset" the background of the buffer before you paint to it.
Remember, painting is accumilitive, that is, what ever you painted previously, will remain. You will need to rebuild each frame from scratch each time you paint to it
Flickering will occur for two reasons...
You are using AWT based components, which aren't double buffered
You are overriding paint of a top level container like JFrame, which isn't double buffered.
You should either use a BufferStrategy or override the paintComponent method of a Swing based component, like JPanel which are double buffered by default