问题描述
我正在创建一个自上而下的射击游戏,让僵尸跟随您并旋转以直接瞄准您.如果有多达 5 个僵尸跟随,则没有闪烁.如果有超过 5 个僵尸,则绘制到屏幕上的所有其他图像都会闪烁.在我看来,随着越来越多的僵尸被添加,它会导致游戏运行速度变慢,并且它会时不时地将其他图像绘制得稍微错位.我正在使用 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?
提前致谢!
这是僵尸的代码...
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);
}
}
JPanel 类...
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" 是 Zombie 类的数组列表.手枪"等也是用于子弹等的数组列表.调用的循环方法用于移动所有图像、添加图像和检查碰撞.对代码的混乱程度表示歉意.(我很快就会把它放在不同的方法中来整理)
"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)
推荐答案
这是我拼凑起来的一个非常简单的例子...
This is a really simple example I put together...
它基本上有一个随机运动和旋转的精灵.该程序能够渲染 1 到 10, 000 个僵尸……真的……
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...
没有真正的优化,如果我真的这样做,我会在某个地方有一个僵尸池,可以从中拉出和放入,以减少创建它们所需的时间......
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;
}
}
}
}
}
这篇关于旋转多个图像导致闪烁.Java图形2D的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!