Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
440 views
in Technique[技术] by (71.8m points)

graphics - Rotating multiple images causing flickering. Java Graphics2D

I am creating a top down shooter that has zombies follow you and rotate to aim directly at you. If there are up to 5 zombies following there is no flickering. If there are over 5 zombies there is a flicker on all other images drawn to the screen. It seems to me that as more zombies get added it is causing the game to run slower and for it to draw the other images slightly out of place every now and again. I am using Graphics2D and rotating the image. The image after being drawn is then rotated back to the original position so it doesn't affect other images being drawn after it. How can I stop the flickering and slight movement in pixels?

Thanks in advance!

Here is the code for the zombies...

 package com.game.stayalive;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Random;

public class Zombie extends Sprite {

Zombie() {
    x = -50;
    width = Display.spriteWidth;
    height = Display.spriteHeight;
    DELAY = rand.nextInt(8)+8;
    life = 5;
}

long lastUpdate;
boolean barrierDestroyed = false;
public void drawZombie(Graphics g,int playerX, int playerY) {

    if (!visible) {
        return;
    }
    Graphics2D g2d = (Graphics2D) g;

    direction = Math.atan2(y - playerY, x - playerX);

    g2d.rotate(direction - Math.PI / 2.0, x + width / 2, y + height / 2);
    if(DELAY > 10){
    g2d.setColor(Color.green.darker());
    }else{
        g2d.setColor(Color.red.darker());
    }
    // Main body rectangle
    g2d.drawRect(x, y, width, height);
    // Left arm
    g2d.drawRect(x - width / 2, y, width / 2, height);
    g2d.fillRect(x - width / 2, y, width / 2, height);
    // Right arm
    g2d.drawRect(x + width, y, width / 2, height);
    g2d.fillRect(x + width, y, width / 2, height);
    // Main body filled
    if(DELAY > 10){
    g2d.setColor(Color.green);
}else{
        g2d.setColor(Color.red);
    }
    g2d.fillRect(x, y, width, height);
    g2d.rotate(-(direction - Math.PI / 2.0), x + width / 2, y + height / 2);

}




long waited = System.currentTimeMillis();
long waitTime;
public void setWaitTime(long waitTime){
    this.waitTime = waitTime;
    if (System.currentTimeMillis() - waited < waitTime) {
        return;

    }
}
int moveSpeed = 10;

public void collisionDetection(Zombie zombie1, Zombie zombie2) {
    xAcc += gradientX;
    yAcc += gradientY;
    x = (int) xAcc;
    y = (int) yAcc;
    if (zombie1.getRect().intersects(zombie2.getRect()) && zombie1.visible &&  zombie1.life > 0) {
        xAcc -= gradientX;
        yAcc -= gradientY;
        xAcc += (gradientX/10)+rand.nextDouble()-0.5;
        yAcc += (gradientY/10) + rand.nextDouble()-0.5;
        x = (int) xAcc;
        y = (int) yAcc;
}
    xAcc -= gradientX;
    yAcc -= gradientY;
    x = (int) xAcc;
    y = (int) yAcc;
}

Random rand = new Random();
long spawnRate = 10000, lastZombieAdded;

public void addZombie() {
    if (System.currentTimeMillis() - lastZombieAdded > spawnRate) {
        Display.zombie.add(new Zombie());
        Display.zombie.get(Display.zombie.size()-1).visible = true;
        Display.zombie.get(Display.zombie.size()-1).setWaitTime(500);
        Display.zombie.get(Display.zombie.size()-1).xAcc = rand.nextInt(950);
        Display.zombie.get(Display.zombie.size()-1).yAcc = rand.nextInt(600);
        lastZombieAdded = System.currentTimeMillis();
    }
}
public void removeZombie(){
    for (int i = 0; i < Display.zombie.size(); i++) {
        if(!Display.zombie.get(i).visible ){
        Display.zombie.remove(i);
        }
    }
}

double gradientX, gradientY;

public void move() {
    setWaitTime(waitTime);
    if (life <= 0) {
        visible = false;
    }
    if (System.currentTimeMillis() - lastUpdate > DELAY) {
        xAcc += gradientX;
        yAcc += gradientY;
        x = (int) xAcc;
        y = (int) yAcc;
        lastUpdate = System.currentTimeMillis();
    }gradientX = -Math.cos(direction);
    gradientY = -Math.sin(direction);
    }

}

The JPanel Class...

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
            RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    player.drawPlayer(g2d);

    for (int i = 0; i < zombie.size(); i++) {
        if (zombie.get(i).visible) {
            zombie.get(i).drawZombie(g2d,playerX,playerY);
        }
    }
    for (int i = 0; i < pistol.size(); i++) {
        if (pistol.get(i).visible) {
            pistol.get(i).drawPistol(g2d);
        }
    }
    for (int i = 0; i < machineGun.size(); i++) {
        if (machineGun.get(i).visible) {
            machineGun.get(i).drawMachineGun(g2d);
        }
    }
    for (int i = 0; i < flamethrower.size(); i++) {
        if (flamethrower.get(i).visible) {
            flamethrower.get(i).draw(g2d);
        }
    }
    reloadBar.drawReloadBar(g2d);
    selector.drawSelector(g2d);
    money.draw(g2d);
    Toolkit.getDefaultToolkit().sync();
    g.dispose();
}
@Override
public void run() {
    long beforeTime, timeDiff, sleep;
    beforeTime = System.currentTimeMillis();
    while (true) {
        cycle();
        repaint();
        timeDiff = System.currentTimeMillis() - beforeTime;
        sleep = DELAY - timeDiff;
        if (sleep < 0) {
            sleep = 2;
        }
        try {
            Thread.sleep(sleep);
        } catch (InterruptedException e) {
            System.out.println("interrupted");
        }
        beforeTime = System.currentTimeMillis();
    }
}

}

"zombie" is an Array List of the class Zombie. "Pistol" and the others are also Array Lists used for bullets etc. The cycle method that gets called is used to move all the images, add images and check for collisions. Apologies for how unorganized the code is. (I will soon put it in separate methods to neaten things up)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is a really simple example I put together...

It basically has a sprite which has a random motion and rotation. The program is capable of rendering between 1 and 10, 000 zombies...seriously...

I've used you "main loop" as well, just to be sure...

Zombies

There's no real optimisation, if I was really doing this, I would have a pool somewhere with zombies that could pulled from and put to in order to reduce the amount of time it takes to create them...

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class ZombieLand {

    protected static final Random RND = new Random();

    private static BufferedImage zombie;

    public static void main(String[] args) {
        new ZombieLand();
    }

    public ZombieLand() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                try {
                    zombie = ImageIO.read(getClass().getResource("/Zombie.png"));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }

                final ZombiePane zombiePane = new ZombiePane();

                final JSlider slider = new JSlider(1, 10000);
                slider.setMajorTickSpacing(1000);
                slider.setMinorTickSpacing(100);
                slider.setPaintTicks(true);
                slider.addChangeListener(new ChangeListener() {
                    @Override
                    public void stateChanged(ChangeEvent e) {
                        JSlider slider = (JSlider) e.getSource();
                        zombiePane.setZombies(slider.getValue());
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(zombiePane);
                frame.add(slider, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        slider.setValue(10000);
                    }
                });
            }
        });
    }

    public static class ZombiePane extends JPanel {

        private List<ZombieSprite> sprites;
        protected static final Object SPRITE_LOCK = new Object();

        private int desiredCount = 1;

        public ZombiePane() {
            sprites = new ArrayList<>(25);
            sprites.add(new ZombieSprite());
            Thread t = new Thread(new MainLoop());
            t.setDaemon(false);
            t.start();
            Font font = getFont();
            setFont(font.deriveFont(Font.BOLD, 48f));
        }

        public void setZombies(int count) {
            desiredCount = count;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            synchronized (SPRITE_LOCK) {
                for (ZombieSprite sprite : sprites) {
                    sprite.paint(g2d);
                }
            }
            String text = Integer.toString(sprites.size());
            FontMetrics fm = g2d.getFontMetrics();
            g2d.drawString(text, getWidth() - fm.stringWidth(text), getHeight() - fm.getHeight() + fm.getAscent());
            g2d.dispose();
        }

        protected void cycle() {
            synchronized (SPRITE_LOCK) {
                if (desiredCount != sprites.size()) {
                    int count = 0;
                    int fill = 100;
                    while (sprites.size() > desiredCount && count < fill) {
                        sprites.remove(0);
                        count++;
                    }
                    count = 0;
                    while (sprites.size() < desiredCount && count < fill) {
                        sprites.add(new ZombieSprite());
                        count++;
                    }
                }

                for (ZombieSprite sprite : sprites) {
                    sprite.update(getWidth(), getHeight());
                }
            }
        }

        public class MainLoop implements Runnable {

            private int DELAY = 40;

            public void run() {
                long beforeTime, timeDiff, sleep;
                beforeTime = System.currentTimeMillis();
                while (true) {
                    cycle();
                    repaint();
                    timeDiff = System.currentTimeMillis() - beforeTime;
                    sleep = DELAY - timeDiff;
                    if (sleep < 0) {
                        sleep = 2;
                    }
                    try {
                        Thread.sleep(sleep);
                    } catch (InterruptedException e) {
                        System.out.println("interrupted");
                    }
                    beforeTime = System.currentTimeMillis();
                }
            }
        }
    }

    public static class ZombieSprite {

//        private BufferedImage zombie;
        private Point motionDelta;
        private double rotationDelta;

        private Point location;
        private double angle;

        public ZombieSprite() {
            motionDelta = new Point();
            motionDelta.x = (int) ((Math.random() * 3) + 1);
            motionDelta.y = (int) ((Math.random() * 3) + 1);
            if (Math.random() > 0.5) {
                motionDelta.x *= -1;
            }
            if (Math.random() > 0.5) {
                motionDelta.y *= -1;
            }
            rotationDelta = (int) ((Math.random() * 9) + 1);
            if (Math.random() > 0.5) {
                rotationDelta *= -1;
            }
        }

        public void paint(Graphics2D g2d) {
            if (location != null) {
                Graphics2D g = (Graphics2D) g2d.create();
                AffineTransform at = new AffineTransform();
                at.translate(location.x, location.y);
                at.rotate(Math.toRadians(angle), zombie.getWidth() / 2, zombie.getHeight() / 2);
                g.setTransform(at);
                g.drawImage(zombie, 0, 0, null);
                g.dispose();
            }
        }

        public void update(int width, int height) {
            if (location == null) {
                angle = (Math.random() * 360d);
                location = new Point();
                location.x = (int) (Math.random() * (width - zombie.getWidth()));
                location.y = (int) (Math.random() * (height - zombie.getHeight()));
            } else {
                angle += rotationDelta;
                location.x += motionDelta.x;
                location.y += motionDelta.y;

                if (location.x < 0) {
                    location.x = 0;
                    motionDelta.x *= -1;
                } else if (location.x + zombie.getWidth() > width) {
                    location.x = width - zombie.getWidth();
                    motionDelta.x *= -1;
                }
                if (location.y < 0) {
                    location.y = 0;
                    motionDelta.y *= -1;
                } else if (location.y + zombie.getHeight() > height) {
                    location.y = height - zombie.getHeight();
                    motionDelta.y *= -1;
                }
            }
        }

    }

}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...