I put this code together based on a lot of examples I found around here on stackoverflow. When I run the program the entire screen flickers intensely. I'm sure there is something simple I'm overlooking, but so far have been unable to track down a solution. I've been debugging this for a couple hours mostly with the help of online forum reading, so I figured it was time to ask the audience.
public class Screen extends JComponent {
#Override
public Dimension getPreferredSize(){
Dimension tempDimension = Toolkit.getDefaultToolkit().getScreenSize();
return tempDimension;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)bufferStrategy.getDrawGraphics();
g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); //sprites overlap instead of overwrite
if(game==null){
drawSplash(g2D);
}else{
drawBoard(g2D);
}
g2D.dispose();
bufferStrategy.show();
}
}
If any additional code is required, I can provide it. Thank you for your help, stackoverflow!
To achieve the results you are getting, you either have another class which extends from Canvas or are using the BufferStrategy from the top level container. In either case, both must be visible on the screen.
Basically, they are fighting each other, as they are two different painting algorithms. Swing, which is a passive painting algorithm, paints updates as they are needed and the BufferStrategy, which uses an active algorithm, requiring your to schedule the updates to the buffer as required.
Both use a double buffering algorithm.
So, you should pick one or the other...
public class Screen extends JComponent {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g.create();
g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); //sprites overlap instead of overwrite
if (game == null) {
drawSplash(g2D);
} else {
drawBoard(g2D);
}
g2D.dispose();
}
}
or something like...
public void gameEngine(BufferStrategy strategy) {
// Main loop
while (!done) {
// Prepare for rendering the next frame
// ...
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics2D g2D = (Graphics2D) strategy.getDrawGraphics();
// Render to graphics
g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); //sprites overlap instead of overwrite
if (game == null) {
drawSplash(g2D);
} else {
drawBoard(g2D);
}
// Dispose the graphics
g2D.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
Which was pretty much ripped from the JavaDocs for BufferStrategy
BTW, this...
#Override
public Dimension getPreferredSize(){
Dimension tempDimension = Toolkit.getDefaultToolkit().getScreenSize();
return tempDimension;
}
is a really bad design, you are making assumptions about the state of the component which may not meet reality. You should allow the window to decide how large it ultimately wants to be, which can be achieved by using setExtendedState and passing it JFrame.MAXIMIZED_BOTH, which will take into consideration other OS elements, like the task bar or dock
Related
I'm creating a game using Java Swing, and I'm finding the need for graphical displays of what's going on at this point. The current display uses a grid of JButtons to represent the tiles on a battlefield. Would it be possible to display floating/disappearing damage numbers over the JButtons, using perhaps custom made pixel art GIFs? If so, how would I go about implementing this?
Almost all Swing components can be extended to change its presentation.
Create an own button class extending JButton and override its paintComponent method to display the required damage. This method receives a Graphic (actually an instance of Graphics2D) on which you can draw the damage, if one is active.
Very simple example:
public class DamageButton extends JButton {
private String damage = null;
public DamageButton(String text) {
super(text);
}
public void setDamage(String damage) {
this.damage = damage;
System.out.println(damage);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (damage != null) {
Graphics2D gg = (Graphics2D) g.create();
try {
gg.setColor(Color.RED);
gg.drawString(damage, 10, 10);
} finally {
gg.dispose();
}
}
}
}
Creating a new Graphics (gg) so the settings of g are not changed and need not to be restored.
Call setDamage() with some text to have it displayed over the button or with null to cancel the effect.
Animation or other effects can (more or less) easily be added. Just be aware that the given Graphics2D has a clipping area set to the dimension of the button.
I know there's no direct replacement for java.awt.Canvas in swing, and I know I'm supposed to use a JPanel and override paintComponent, for example like so:
public void paintComponent(Graphics g) {
g.setColor(Color.black);
g.drawOval(0, 0, 100, 100);
}
And this would draw a black circle on the JPanel when it is created. The problem I have is that I want a dynamic canvas: I want to be able to draw things in response to user input, and redraw continuously, not just once when the application starts. An example would be having a moving object on a canvas, that would need to be redrawn at a rate of say 60 frames per second. How could I achieve this without using AWT components?
EDIT: what I mean is, in an actual canvas, I'd be able to arbitrarily call, say, drawOval anywhere in my code, and that would draw an oval on the canvas; is this doable with JPanel?
Store the information to be drawn (e.g. a Shape or a group of them) and call repaint() from a Swing Timer. Each time the paintComponent(..) method is called, first call the super(..) method to erase the previous drawings, then iterate the list of shapes, move them if necessary, and draw each one.
Here's one way to do it:
public class Renderer extends JComponent implements ActionListener {
private int x;
public Renderer() {
Timer timer = new Timer(1000/60, this);
timer.start();
x = 0;
}
#Override
public void paintComponent(Graphics g) {
super.paint(g);
// drawing code
g.setColor(Color.black);
g.drawOval(x, 0, 100, 100);
}
private void update() {
this.x++;
}
#Override
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
}
Now just add this to your component (JPanel or whatever):
comp.add(new Renderer());
Let me be honest, I'm not really know what I'm doing.
I just moved from python to Java, and i'm still trying to get used to all the classes and the types things.
I decide to make a break with java concepts tutorials and start to get my hands dirty. According to my understanding, I'm using swing to paint a ball on the screen and make it move.
I tried to design a ball object that handle the ball position and the screen bumping, but the ball doesn't moved at all. When I turn on the debug I noticed that the paint() function get called only at creation, but not get called with repaint().
I got a feeling that I'm using a bad tutorial to do this stuff, its look like there is a better way to do it.
Anyway, I will be glad to hear what you guys thinking.
Edit: After I saw your comments I notice that paint actually get called when I put sysout there. Its seems that the debugger doesn't jump to there before I put sysout in paint(). My guess is that I'm not really changing the position of the ball.
#SuppressWarnings("serial")
public class Tennis extends JPanel {
Ball ball = new Ball(50,50);
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int[] position = ball.getPosition();
g2d.fillOval(position[0],position[1], 30, 30);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Mini Tennis");
Tennis game = new Tennis();
frame.add(game);
frame.setSize(300, 300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (true) {
// just change the position and check for bump
game.ball.move(game.getHeight(), game.getWidth());
game.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Change paint() to paintComponent(), for an explanation of the differences therein see this.
#Override
public void paintComponent(Graphics g){ //CHANGE HERE
super.paintComponent(g); //AND HERE
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int[] position = ball.getPosition();
g2d.fillOval(position[0],position[1], 30, 30);
}
so recently I decided to try and make some form of sprite-esque game, similar to the older Final Fantasy games. I'm mostly doing this as a code learning process rather than using things like RPGMaker to actually create an idea.
The biggest part I'm concerned about are how to properly paint multiple layers of images (such as static Sprites, background images, etc) in a simple way. While I would normally just test a bunch of things to see rather than asking, I realize there are many (possibly easy) methods of ways to implement any one thing in coding.
What are some ways (or Classes) to implement this? Preferably a way that is easy to change what layers are where (layer A in front of B... oh wait no B in front of A! at any time)
I do hope that's not too broad of a question.. .
Thanks.
The most basic approach is the painter's algorithm, simply paint layers in reverse order of the distance to the screen (first background, then characters, then foreground for instance).
You have each layer represented by a simple class, something like:
public class Layer
{
List<Sprite> sprites;
public void draw(Graphics g)
{
for (Sprite sprite : sprites)
sprite.draw(g);
}
}
where a sprite would be:
public class Sprite
{
BufferedImage image;
float x, y, w, h;
public void draw(Graphics g)
{
g.drawImage(image, (int)x, (int)y, (int)(x+w), (int)(y+h), 0, 0, image.getWidth(), image.getHeight(), null);
}
}
Here, the background would be a layer with a single sprite that is the size of the screen. You can subclass the background layer to achieve effects like parallax.
Finally, your layers would be part of a scene:
public class Scene
{
List<Layer> layers;
public void draw(Graphics g)
{
for (Layer layer : layers)
layer.draw(g);
}
}
Changing the order of rendering is as simple as changing the order in the 'layers' list in a scene.
Are you looking to put foreground images into a background image? If yes, then the following code will help:
public class Image {
public static void main(String args[]) throws IOException {
BufferedImage bgImage = ImageIO.read(new File("1.jpg"));
BufferedImage fgImage =ImageIO.read(new File("2.jpg"));
BufferedImage overlayedImage = overlay(bgImage, fgImage);
if (overlayedImage != null) {
File outputfile = new File("3.jpg");
ImageIO.write(overlayedImage, "jpg", outputfile);
} else {}
}
public static BufferedImage overlay(BufferedImage bgImage,
BufferedImage fgImage) {
Graphics2D g = bgImage.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(bgImage, 0, 0, null);
g.drawImage(fgImage, 0, 0, null);
g.dispose();
return bgImage;
}
}
I am making a molecule designing application. I can draw the lines and circles, but it clears the old lines each time you click, so basically, you can only design molecules with 2 atoms.
Also, the mouseEvents don't deliver if you click very fast which is also a problem.
Here is the code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class MoleculeDesigner extends JComponent implements MouseListener {
private Point op, cp;
private boolean first = true;
public static final Color linecolor = new Color(0, 255, 0);
private static final long serialVersionUID = 1L;
private BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
public MoleculeDesigner() {
JFrame f = new JFrame("Molecule Designer");
f.setBackground(Color.WHITE);
f.addMouseListener(this);
f.add(this);
f.setSize(100, 100);
f.setDefaultCloseOperation(3);
f.setVisible(true);
}
public static void main(String[] args) {
new MoleculeDesigner();
}
#Override
protected void paintComponent(Graphics g) {
if(op != null && cp != null) {
Graphics2D g2 = img.createGraphics();
super.paintComponent(g2);
g2.setColor(linecolor);
g2.drawLine((int) op.getX(), (int) op.getY(), (int) cp.getX(), (int) cp.getY());
g2.setColor(Color.BLACK);
g2.fillOval((int) cp.getX(), (int) cp.getY(), 10, 10);
op = (Point) cp.clone();
g2.dispose();
}
}
#Override
public Dimension getPreferredSize() {
return getParent().getMaximumSize();
}
#Override
public void mouseClicked(MouseEvent e) {
if(!first) {
cp = e.getPoint();
cp.setLocation(cp.getX(), cp.getY() - 8);
}
else {
op = e.getPoint();
first = false;
}
repaint();
}
#Override public void mousePressed(MouseEvent e) {}
#Override public void mouseReleased(MouseEvent e) {}
#Override public void mouseEntered(MouseEvent e) {}
#Override public void mouseExited(MouseEvent e) {}
}
All help appreciated!
Either 1) draw in a BufferedImage which is then displayed inside of your paintComponent override, or 2) put your data into an ArrayList or other collection, and then iterate through the collection inside of paintComponent. I'd do the latter if I needed the data for other purposes. Also, never ever do this:
public void update(Graphics g) {
paintComponent(g);
}
This is not how Swing graphics are supposed to be done and is potentially dangerous code. Please read:
Basic Swing Graphics Tutorial
Advanced Swing Graphics Information
Edit
More detail regarding option 1:
Create a BufferedImage using one of its constructors.
Do your drawing on the image.
When you need to draw, get a Graphics object from the BufferedImage using getGraphics() or createGrahpics() (for a Graphics2D object)
Draw with this Graphics object
Then dispose() the Graphics object.
Then call repaint() to ask the JVM to repaint the component.
Draw the image in your paintComponent method by calling g.drawImage(...), passing in your buffered image.
Benefits: often the drawing is quicker, and I often use this to draw background images.
Drawbacks: the data points are not available, and so if you need to do manipulation or animation of your data points, this is not the way to go.
You don't, nor should you.
paint in Swing is a destructive process, this is the way it was designed. That is, there is an expectation that when you component is requested to paint itself, it will clean up the Graphics context before painting anything (this is slightly different for transparent components though).
Swing has no concept of what was painted on your component before and because the Graphics context is shared amongst all the components been painted, unless you clear the graphics first, you could end up with unwanted paint artifacts
Possible solutions might include...
Painting to some kind of backing buffer (such as a BufferedImage), which you use the paintComponent method to draw. This is limited in the fact that it just acts like a paint program, painting pixels to the image. You will also need to provide functionality when the size of the viewable area changes, as the BufferedImage won't know.
Place each object you wanted painted into some kind of List and iterate this list when paintComponent is called. This is a little more flexible in that you can control the order of the objects drawn, remove objects and insert new ones where you like