为什么我的简单java2d太空入侵者游戏落后了?
我目前正在为我的软件工程课程制作一个太空入侵者式的游戏。我已经做了所有符合要求的工作,所以这不是“解决我的家庭作业”之类的问题。我的问题是,游戏会延迟(在似乎是随机时间和间隔的情况下)到令人沮丧的程度而无法玩。我认为可能导致这种情况的一些因素——尽管我不是积极的——如下所示: 计时器事件每10毫秒出现问题(我对此表示怀疑,因为这个游戏所需的资源非常有限) 碰撞检测问题(每10毫秒检查一次与每个可见敌人的碰撞似乎会占用大量资源) 重新喷漆有问题吗?但我觉得这似乎不太可能为什么我的简单java2d太空入侵者游戏落后了?,java,2d,game-engine,Java,2d,Game Engine,我目前正在为我的软件工程课程制作一个太空入侵者式的游戏。我已经做了所有符合要求的工作,所以这不是“解决我的家庭作业”之类的问题。我的问题是,游戏会延迟(在似乎是随机时间和间隔的情况下)到令人沮丧的程度而无法玩。我认为可能导致这种情况的一些因素——尽管我不是积极的——如下所示: 计时器事件每10毫秒出现问题(我对此表示怀疑,因为这个游戏所需的资源非常有限) 碰撞检测问题(每10毫秒检查一次与每个可见敌人的碰撞似乎会占用大量资源) 重新喷漆有问题吗?但我觉得这似乎不太可能 @SuppressWarn
@SuppressWarnings("serial")
public class SIpanel extends JPanel {
private SIpanel panel;
private Timer timer;
private int score, invaderPace, pulseRate, mysteryCount, distanceToEdge;
private ArrayList<SIthing> cast;
private ArrayList<SIinvader> invaders, dead;
private ArrayList<SImissile> missileBase, missileInvader;
private SIinvader[] bottomRow;
private SIbase base;
private Dimension panelDimension;
private SImystery mysteryShip;
private boolean gameOver, left, right, mysteryDirection, space, waveDirection;
private boolean runningTimer;
private Music sound;
private void pulse() {
pace();
processInputs();
if (gameOver) gameOver();
repaint();
}
private void pace() {
// IF invaders still live
if (!invaders.isEmpty()) {
invaderPace++;
// Switch back manager
if (distanceToEdge <= 10) {
switchBack();
pulseRate = (pulseRate >= 16) ? (int) (pulseRate*(0.8)) : pulseRate;
waveDirection = !waveDirection;
distanceToEdge = calculateDistanceToEdge();
}
// Move invaders left/right
else if (invaderPace >= pulseRate) {
invaderPace = 0;
distanceToEdge = calculateDistanceToEdge();
moveAI();
invadersFire();
if (!dead.isEmpty()) removeDead();
if (mysteryCount < 1) tryInitMysteryShip();
}
// All invaders are kill, create new wave
} else if (missileBase.isEmpty() && missileInvader.isEmpty() && !cast.contains(mysteryShip)) {
// System.out.println("New Wave!");
newWave();
}
// Every pace
if (!missileBase.isEmpty()) moveMissileBase();
// Every two paces
if (invaderPace % 2 == 0) {
if (!missileInvader.isEmpty()) moveMissileInvader();
if (mysteryCount > 0) moveMysteryShip();
}
}
private void processInputs() {
if (left) move(left);
if (right) move(!right);
if (space) fireMissile(base, true);
}
protected void fireMissile(SIship ship, boolean isBase) {
if(isBase && missileBase.isEmpty()) {
base.playSound();
SImissile m = new SImissile(ship.getX()+(ship.getWidth()/2), ship.getY()-(ship.getHeight()/4));
missileBase.add(m);
cast.add(m);
} else if (!isBase && missileInvader.size()<3) {
base.playSound();
SImissile m = new SImissile(ship.getX()+(ship.getWidth()/2), ship.getY()+(ship.getHeight()/4));
missileInvader.add(m);
cast.add(m);
}
}
private void newWave() {
pulseRate = 50;
int defaultY=60, defaultX=120, defaultWidth=30, defaultHeight=24;
for(int i=0; i<5; i++) {
for(int j=0; j<10; j++) {
if (i<1) invaders.add(new SItop((j*defaultWidth)+defaultX, (i*defaultHeight)+defaultY, defaultWidth, defaultHeight));
else if (i<3) invaders.add(new SImiddle((j*defaultWidth)+defaultX, (i*defaultHeight)+defaultY, defaultWidth, defaultHeight));
else if (i<5) invaders.add(new SIbottom((j*defaultWidth)+defaultX, (i*defaultHeight)+defaultY, defaultWidth, defaultHeight));
}
}
for (SIinvader s: invaders) {
cast.add(s);
}
if (!cast.contains(base)) {
cast.add(base);
}
bottomRow = getBottomRow();
}
private void tryInitMysteryShip() {
Random rand = new Random();
int x=rand.nextInt(1000);
if (x<=3) {
mysteryCount = 1;
if (rand.nextBoolean()) {
mysteryDirection = true;
}
if (mysteryDirection) {
mysteryShip = new SImystery(0, 60, 36, 18);
} else {
mysteryShip = new SImystery(480, 60, 36, 18);
}
cast.add(mysteryShip);
}
}
private void moveMysteryShip() {
int distance = 0;
if (mysteryDirection) {
mysteryShip.moveRight(5);
distance = getWidth() - mysteryShip.getX();
} else {
mysteryShip.moveLeft(5);
distance = 30+mysteryShip.getX()-mysteryShip.getWidth();
}
if (distance <= 5) {
dead.add(mysteryShip);
mysteryShip = null;
mysteryCount = 0;
}
}
private void removeDead() {
@SuppressWarnings("unchecked")
ArrayList<SIinvader> temp = (ArrayList<SIinvader>) dead.clone();
dead.clear();
for (SIinvader s : temp) {
invaders.remove(s);
cast.remove(s);
}
bottomRow = getBottomRow();
}
private void invadersFire() {
int[] p = new int[bottomRow.length];
for (int i=0; i<p.length; i++) {
for (int j=0; j<p.length; j++) {
p[j] = j;
}
Random rand = new Random();
int a=rand.nextInt(101);
if (a>=20) {
int b=rand.nextInt(p.length);
fireMissile(bottomRow[b], false);
}
}
}
private int calculateDistanceToEdge() {
int distance = 0;
SIinvader[] outliers = getOutliers();
if (waveDirection) {
distance = getWidth() - outliers[0].getX()-outliers[0].getWidth();
} else {
distance = outliers[1].getX();
}
return distance;
}
private SIinvader[] getOutliers() {
SIinvader leftMost = invaders.get(0), rightMost = invaders.get(0);
for (SIinvader s : invaders) {
if (s.getX() < leftMost.getX()) {
leftMost = s;
}
if (s.getX() > rightMost.getX()) {
rightMost = s;
}
}
return new SIinvader[] { rightMost, leftMost };
}
private SIinvader[] getBottomRow() {
SIinvader[] x = new SIinvader[(invaders.size()>10)?10:invaders.size()];
for (int i=0; i<x.length; i++) {
x[i] = invaders.get(i);
for (SIinvader s:invaders) {
if (s.getX() == x[i].getX()) {
if (s.getY() > x[i].getY()) {
x[i] = s;
}
}
}
}
return x;
}
private void move(boolean b) {
int defaultX = 5;
if (b) base.moveLeft(defaultX);
else base.moveRight(defaultX);
}
private void moveAI() {
for(SIinvader s : invaders) {
s.changeImage();
int defaultX = 5;
if (waveDirection) s.moveRight(defaultX);
else s.moveLeft(defaultX);
}
}
private void moveMissileBase() {
if (invaders.isEmpty()) return;
int movement = -5, bound = 0;
SImissile missile = missileBase.get(0);
missile.moveDown(movement);
SIinvader lowestInvader = getLowestInvader();
if (missile.getY() < (lowestInvader.getY() + lowestInvader.getHeight())) {
for (SIinvader s:bottomRow) {
if (checkCollision(missile, s)) {
s.setHit();
dead.add(s);
cast.remove(missile);
missileBase.clear();
score += s.value;
return;
}
}
if (mysteryCount > 0) {
if (checkCollision(missile, mysteryShip)) {
mysteryShip.setHit();
dead.add(mysteryShip);
cast.remove(missile);
missileBase.clear();
score += mysteryShip.value;
return;
}
}
if (missile.getY() < bound) {
missileBase.remove(missile);
cast.remove(missile);
}
}
}
private SIinvader getLowestInvader() {
SIinvader lowest = bottomRow[0];
for (SIinvader invader : bottomRow) {
if (invader.getY() > lowest.getY()) {
lowest = invader;
}
}
return lowest;
}
private void moveMissileInvader() {
int movement = 5, bound = (int) panelDimension.getHeight();
for (SImissile missile : missileInvader) {
missile.moveDown(movement);
if(missile.getY() >= base.getY()) {
if (checkCollision(missile, base)) {
base.setHit();
gameOver = true;;
missileInvader.remove(missile);
cast.remove(missile);
return;
} else if (missile.getY() >= bound-25) {
missileInvader.remove(missile);
cast.remove(missile);
return;
}
}
}
}
private boolean checkCollision(SIthing missile, SIthing ship) {
Rectangle2D rect1 = new Rectangle2D.Double(
missile.getX(),
missile.getY(),
missile.getWidth(),
missile.getHeight()
);
Rectangle2D rect2 = new Rectangle2D.Double(
ship.getX(),
ship.getY(),
ship.getWidth(),
ship.getHeight()
);
return rect1.intersects(rect2);
}
private void switchBack() {
int defaultY = 12;
for (SIinvader s : invaders) {
if (s.getY() > getHeight()) {
gameOver = true;
return;
}
s.moveDown(defaultY);
}
}
private void gameOver() {
pause(true);
SI.setGameOverLabelVisibile(true);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.GREEN);
Font font = new Font("Arial", 0, 20);
setFont(font);
String score = "Score: "+this.score;
Rectangle2D rect = font.getStringBounds(score, g2.getFontRenderContext());
int screenWidth = 0;
try { screenWidth = (int) panelDimension.getWidth(); }
catch (NullPointerException e) {}
g2.setColor(Color.GREEN);
g2.drawString(score, (int) (screenWidth - (10 + rect.getWidth())), 20);
for(SIthing a:cast) {
a.paint(g);
}
}
public SIpanel() {
super();
setBackground(Color.BLACK);
cast = new ArrayList<SIthing>();
missileBase = new ArrayList<SImissile>();
score = invaderPace = mysteryCount = pulseRate = 0;
sound = new Music("AmbientMusic.wav");
panel = this;
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT : left = true; break;
case KeyEvent.VK_RIGHT : right = true; break;
case KeyEvent.VK_SPACE : space = true; break;
}
}
@Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT : left = false; break;
case KeyEvent.VK_RIGHT : right = false; break;
case KeyEvent.VK_SPACE : space = false; break;
}
}
});
setFocusable(true);
timer = new Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pulse();
}
});
}
public void reset() {
SI.setGameOverLabelVisibile(false);
score = invaderPace = mysteryCount = 0;
pulseRate = 50;
cast = new ArrayList<SIthing>();
invaders = new ArrayList<SIinvader>();
dead = new ArrayList<SIinvader>();
missileBase = new ArrayList<SImissile>();
missileInvader = new ArrayList<SImissile>();
base = new SIbase(230, 370, 26, 20);
waveDirection = true;
gameOver = false;
sound.stop();
sound.loop();
panelDimension = SI.getFrameDimensions();
bottomRow = getBottomRow();
newWave();
timer.start();
runningTimer=true;
}
public SIpanel getPanel() {
return this.panel;
}
public void pause(boolean paused) {
if (paused) timer.stop();
else timer.start();
}
}
@SuppressWarnings(“串行”)
公共类SIpanel扩展了JPanel{
私人SIpanel小组;
私人定时器;
私人整数分数、侵入面、脉冲数、神秘计数、距离边缘;
私人ArrayList演员阵容;
私人ArrayList入侵者,已死;
private ArrayList missileBase,missileInvader;
私人入侵者[]底层;
私有SIbase库;
私人层面;
私密的神秘感;
私有布尔gameOver,左,右,mysteryDirection,空格,waveDirection;
专用布尔运行计时器;
私人音乐声;
私人空间脉冲(){
步调();
processInputs();
如果(gameOver)gameOver();
重新油漆();
}
私人空间(){
//如果入侵者还活着
如果(!invaders.isEmpty()){
inverspace++;
//切换管理器
如果(距离边缘=16)?(int)(脉冲速率*(0.8)):脉冲速率;
waveDirection=!waveDirection;
distanceToEdge=CalculatedInstanceToEdge();
}
//向左/向右移动入侵者
else if(Investerface>=pulseRate){
侵入面=0;
distanceToEdge=CalculatedInstanceToEdge();
moveAI();
入侵者火力();
如果(!dead.isEmpty())删除了dead();
if(mysteryCount<1)tryInitMysteryShip();
}
//所有入侵者都被杀死,创造新的浪潮
}else if(missileBase.isEmpty()&&missileInvader.isEmpty()&&!cast.contains(mysteryShip)){
//System.out.println(“新浪潮!”);
newWave();
}
//每一步
如果(!missileBase.isEmpty())moveMissileBase();
//每隔两步
如果(入侵空间%2==0){
如果(!missleinvader.isEmpty())移动missleinvader();
如果(mysteryCount>0)moveMysteryShip();
}
}
私有void processInputs(){
如果(左)移动(左);
如果(右)移动(!右);
if(太空)火力导弹(基地,真);
}
受保护的空火导弹(SIship飞船,布尔isBase){
if(isBase&&missileBase.isEmpty()){
base.playSound();
SImissile m=新的SImissile(ship.getX()+(ship.getWidth()/2),ship.getY()-(ship.getHeight()/4));
增加(m);
增加(m);
}否则,如果(!isBase&&missleinvader.size()我认为碰撞检测可能是滞后的原因,您应该通过尝试大幅增加和减少敌人或导弹的数量来进行调查,看看这是否会产生影响
认为垃圾收集器是你的敌人。在你的检查碰撞方法中,你正在实例化两个(非常简单的)对象。它看起来不太多,但是考虑到你可能正在为每个碰撞检查创建它们,并且在60fps时它会增加,直到GC在“停止世界”时看到临界质量,并且你看到明显的滞后。
如果是这种情况,可能的解决方案是不在频繁调用的方法中实例化任何对象。您可以创建一次矩形2D,然后更新其位置,而不是每次创建一个新的,这样您就可以避免不必要的内存分配。“为什么我的**简单**java2d空间入侵者游戏落后?”——你已经发布了375行代码,所有的代码都在一个大类中,这甚至不是整个程序——不是一个简单的程序来确定。考虑通过创建一个有用的工具来隔离你的问题,一个有用的工具可以帮助你看到你的问题赤裸裸的,和我们,因为它允许我们涉猎更少的代码,更多的释放。ant代码和runnable代码。@HoverCraftfullOfels谢谢你的回答。我将看看你说的话,然后更新我的问题。我已经实现了这一点,但我仍然反复出现打破游戏规则的延迟。延迟是否来自于从数组列表中移除死船?我已将checkCollision方法设置为始终返回fa通过注释掉rect1.intersect(rect2)实现lse。这将消除游戏中的所有延迟。这有什么原因吗?检查交叉点本身似乎会导致延迟,而不是一次又一次地创建新的矩形。是的,这可能是一个代价高昂的操作,具体取决于它的实现。要诊断它,我建议使用探查器。此问题的可能解决方案之一可能是执行所谓的限制(也称为“延迟”)。此技术不涉及检查每个帧中每个对象之间的碰撞,而是只检查单个帧标记中的一小部分对象,然后检查下一帧标记中的另一小部分对象,以便覆盖设定帧数的每个对象(即10帧,它使碰撞计算在大约1/6秒的时间内不太精确)此外,我注意到您在每个对象之间执行碰撞检查,这意味着O(n^2)二次增长。根据你有多少个实体,你可以考虑使用空间分割算法,如四叉树。我确定了问题并修正了它。