Java Game Image Buffering Leaves Tail - java

I have the following class (Snippet), and in my render() method, I am doing buffering using BufferStrategy. The issue I am having is when I move an image, it leaves a tail.
What Do I need to do with my code to make it so the tail doesn't show? Here is the code:
public class Main extends JFrame implements Runnable{
private BufferStrategy bufferStrategy;
public synchronized void start(){
Thread thread = new Thread(this);
thread.start();
}
public void run(){
// Main Game Loop
this.render();
// End Main Game Loop
}
protected void render(){
if(bufferStrategy == null){
this.createBufferStrategy(3);
bufferStrategy = this.getBufferStrategy();
}
Graphics g = bufferStrategy.getDrawGraphics();
// Loop through a list of items to draw
for(GameObject go : gameObjects){
Image sprite = go.getComponent(SpriteRenderer.class).getSprite();
Vector2 pos = go.getComponent(Transform.class).getPosition();
g.drawImage(sprite, (int)pos.x, (int)pos.y, this);
}
g.dispose();
bufferStrategy.show();
Toolkit.getDefaultToolkit().sync();
}
}
Edit
I figured it out:
Graphics g = bufferStrategy.getDrawGraphics();
super.paint(g);

You need to refresh every time the wheel moves and repaint the Canvas to black before painting the wheel's position again.

Related

JPanel drawing is slowed down after several instances are painted

I'm building a game and I'm painting the road sprites with the JPanel's draw function. The roads (Building class) can be built by dragging the mouse and on each field a new road sprite appeares. But after I've drawn like 20 road sprites, the drawing gets really slow.
I have a frame and there is this JPanel on it.
Here is the code of the JPanel on which my game drawing is:
private class GamePanel extends JPanel implements ActionListener{
Field[][] map = gameEngine.getMap().getFields();
ArrayList<Building> buildings = gameEngine.getBuildings();
Timer timer;
ArrayList<Field> fields = new ArrayList<>();
GameFrame frame; //REFERENCE FOR THE CONTAINER OF THIS PANEL
private int mousePosX;
private int mousePosY;
GamePanel(GameFrame frame){
/*...*/
Mouse mouseListener = new Mouse();
addMouseListener(mouseListener);
addMouseMotionListener(mouseListener);
timer = new Timer(1000/30,this);
timer.start();
/*...*/
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g){
for(Building b : buildings){
int drawPosX = b.getLocation().getPos().x*40;
int drawPosY = b.getLocation().getPos().y*40;
try {
BufferedImage img = ImageIO.read(new File("src/GFX/" + b.getType() + ".png"));
g.drawImage(img, drawPosX, drawPosY, null);
} catch (IOException e) {
}
}
}
#Override
public void actionPerformed(ActionEvent e) {
// I thought this is not needed because I call "repaint()" only at mouse events (like
// building road by dragging)
}
public class Mouse extends MouseAdapter{
#Override
public void mouseDragged(MouseEvent evt){
repaint(); // THIS IS AN EXAMPLE TO WHERE I CALL THE REPAINT()
fieldPosX = evt.getX() - (evt.getX() % 40);
fieldPosY = evt.getY() - (evt.getY() % 40);
gameEngine.placeRoad(new SimpleRoad(new Field(fieldPosX/40,fieldPosY/40)));
}
/*... OTHER MOUSE EVENTS ...*/
}
I thought that calling repaint() only at mouse events will optimise the speed but it really isn't. I attached a GIF on which it can be seen that after 2 line of roads, it gets really slow.
I heard about invokeLater and people advised me to use it but I don't know how to implement that in this project. Why is my game getting slower after several buildings, where am I making a mistake? Would invokeLater solve the problem? How do I place it in my project?
Thanks for helping!
Why is my game getting slower after several buildings,
try
{
BufferedImage img = ImageIO.read(new File("src/GFX/" + b.getType() + ".png"));
g.drawImage(img, drawPosX, drawPosY, null);
}
Don't do I/O in a painting method. As you add more building you are doing more I/O.
The images should be read in the constructor of your class.
You can save them in a HashMap for easy access in the painting method.
Or, the image can be saved as part of the Building class itself.

Is setting the BufferStrategy every time the game renders a good idea?

For some reason everyone sets their BufferStrategy to the BufferStrategy in their canvas whenever they render their game.
BufferStrategy bs;
Graphics2D g;
private void render() {
bs = window.getCanvas().getBufferStrategy();
if (bs == null) {
window.getCanvas().createBufferStrategy(3);
return;
}
g = (Graphics2D) bs.getDrawGraphics();
bs.show();
g.dispose();
}
// When thread starts
public void run() {
while (running) {
//Game loop stuff here.
render();
}
}
Can you just set it once instead of every time the game re-renders?

image is drawn half second later then other paint compontents

After I start my applet every component is drawn alright, besides my background image that is drawn with about a half second delay. I deleted my thread thinking it's maybe the cause of my problem, but it's not, so i didn't include it here.... I use Double Buffering, because I would have flickering of my components that are repainted by thread. I tried to provide as little code as possible....
public class balg extends Applet implements Runnable {
private Image i;
private Graphics doubleG;
URL url;
Image city; //background image
public void init(){
setSize(800, 600);
try{
url = getDocumentBase();
}catch(Exception e){
}
city = getImage(url , "multiplen/images/SPACE.png");
}
public void start(){
Thread thread = new Thread(this);
thread.start();
}
public void run(){
// here goes the repiant();
}
public void stop(){
}
public void destroy(){
}
#Override
public void update(Graphics g) {
if(i == null){
i = createImage(this.getSize().width, this.getSize().height);
doubleG = i.getGraphics();
}
doubleG.setColor(getBackground());
doubleG.fillRect(0, 0, this.getSize().width, this.getSize().height);
doubleG.setColor(getForeground());
paint(doubleG);
g.drawImage(i, 0,0, this);
}
public void paint(Graphics g){
g.drawImage(city,(int) 800 , 0 , this); // it's drawn here
String s = "15";
g.setColor(Color.BLACK);
g.drawString(s, getWidth() - 150, 50);
}
}
It takes that much time to read the image, about 100-200 ms.

Java Graphics(2D) for offscreen rendering

I'm getting into Java graphics at the moment and I'm reading the book Killer game programming in Java by Andrew Davison. He programs a simple window application that uses the Image class for off-screen rendering and then copying the image to the component. What I find confusing is that one can just create the image and then get the graphics context with:
dbg = dbImage.getGraphics();
I find it confusing, because then we only use the dbg Graphics object to draw stuff but later on we use the dbImage in the paintComponent method to display all the stuff we have drawn to the dbg Object:
g.drawImage(dbImage, 0, 0, null);
So how does the dbImage "know", that it contains all the graphics stuff we have drawn onto the dbg Graphics object?
Here is the whole code:
public class GamePanel extends JPanel implements Runnable{
private static final int PWIDTH = 500;
private static final int PHEIGHT = 400;
private Thread animator;
private volatile boolean running = false;
private volatile boolean gameOver = false;
private Graphics dbg;
private Image dbImage = null;
private Counter counter;
public GamePanel(){
setBackground(Color.white);
setPreferredSize(new Dimension(PWIDTH, PHEIGHT));
// create game components eg. counter.
counter = new Counter(10);
counter.start();
}
private void stopGame(){
running = false;
}
#Override public void addNotify(){
super.addNotify();
startGame();
}
public void startGame(){
if(animator == null || !running){
animator = new Thread(this);
animator.start();
}
}
#Override
public void run() {
running = true;
while(running){
gameUpdate();
gameRender();
repaint();
try{
Thread.sleep(20);
}
catch(InterruptedException ex){
// do something with the exception...
}
}
}
private void gameUpdate(){
if(!gameOver){
// update game state...
if(counter.getCounter() == 0)
gameOver = true;
}
}
private void gameRender(){
if(dbImage == null){
dbImage = createImage(PWIDTH, PHEIGHT);
if(dbImage == null){
System.out.println("dbImage is null");
return;
}
else
dbg = dbImage.getGraphics(); // get graphics context for drawing to off-screen images.
}
// clear the background
dbg.setColor(Color.white);
dbg.fillRect(0, 0, PWIDTH, PHEIGHT);
// draw game elements here...
dbg.setColor(Color.black);
dbg.drawString(Integer.toString(counter.getCounter()), 10, 10);
if(gameOver)
dbg.drawString("Game over...", 10, 20);
}
#Override public void paintComponent(Graphics g){
super.paintComponent(g);
if(dbImage != null)
g.drawImage(dbImage, 0, 0, null);
}
}
the important parts are only the paintComponent method and the gameRender() method though.
Basically, a Graphics object is not something you draw on. It's something you draw with.
When you call dbImage.getGraphics(), you are saying "Give me a toolbox that allows me to draw on this image". All of the draw methods in a Graphics object draw on the component for which it was created. They are not drawn on the Graphics object, they are drawn on the Image or the Component that belong to it.
Think of the Graphics object as the palette and brushes, and on the Image or Component as the canvas.
So when you are done running the operations with your dbg, it means you have an image full of colorful pixels that this Graphics object drew on it. Now you can take this image and copy it to another component - by using the drawImage "tool" in that component's Graphics - its "toolbox".

Java graphics being drawn on top of existing graphic

Main class:
public Main() {
Frame f = new Frame();
final Panel p = f.p;
final Player player = new Player();
Timer t = new Timer(UPDATE_PERIOD, new ActionListener() {
public void actionPerformed(ActionEvent e) {
Graphics g = p.getGraphics();
p.render(g);
player.tick();
player.render(g);
g.dispose();
}
});
t.start();
}
Player render method:
public void render(Graphics g) {
g.drawImage(Images.get("player"), x, y, null);
}
The problem is, that all previous drawn images are still there. Example (when I change the drawn image's x or y):
To draw in Swing, you should not be getting the Graphics object directly from the JPanel. Instead, override the paintComponent method and use the parameter Graphics object to perform your custom drawing, with a call to the parent method to erase previous painting
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
//custom painting goes here
}
If you wish to trigger a repaint, use the method by that name on the JPanel:
p.repaint();
Rather than doing custom rendering your your timer, you should really be doing all your painting in your paintComponent method. Something like:
public void actionPerformed(ActionEvent e) {
player.tick();
p.repaint();
}
And then re-render the player and the background in paintComponent()
Painting like you currently are runs into issues when you resize the panel, etc
Try calling 'p.repaint()' in your ActionListener once you have changed the position of the Graphic.

Categories

Resources