为什么我的Java制作的PacMan克隆有时会卡顿?

3
我正在使用Eclipse编写java版的pacman游戏,有时候移动pacman/ghosts会变得缓慢,但也有时候它可以正常运行。这种情况曾经发生在我正在运行程序时,所以不是在添加代码后出现的,并且似乎也不会在游戏中的任何特定事件之后发生。我找不到任何触发器或故障产生的方法。
资源管理器显示了相同的CPU使用率(仅约50%)/内存使用率。此外,FPS似乎始终保持在200左右,无论是在卡顿期间还是在工作良好的时期。
有谁知道这是什么原因?
是否有任何我遗漏的信息可能会有用?
编辑-我基于计时器进行移动,这样做有问题吗?我将在下面发布与移动相关的代码,这里有一个好的方式可以发布整个代码吗?
Timer movement = new Timer(20, new ActionListener()//500 is time in milliseconds between
      //calls to actionPerformed as far as I know.
    {
        public void actionPerformed(ActionEvent arg0) 
        {
            if(movingUp == true)
            {
                moveUp();
            }
            else if(movingDown == true)
            {
                moveDown();
            }
            else if(movingRight == true)
            {
                moveRight();
            }
            else if(movingLeft == true)
            {
                moveLeft();
            }

        }

    });


public void moveUp()
    {
        yPos -= 1;
        this.rect.y -= 1;
    }

public void setDirUp()
    {
        movingUp = true;
        movingDown = false;
        movingRight = false;
        movingLeft = false;
    }

在主类中的公共void keyPressed方法中:
if(keyCode == KeyEvent.VK_W)
        {
            if(pacMan.isUpHittingWall == false)
            {
                pacMan.setDirUp();

                pacMan.isDownHittingWall = false;
                pacMan.isRightHittingWall = false;
                pacMan.isLeftHittingWall = false;
            }

        }

编辑2-感谢大家的帮助。我现在使用系统时间来移动,似乎已经解决了问题,因为一开始我只对吃豆人实施了这个方法,但是鬼还是很慢。现在出现了一个问题,向右和向下移动比向左或向上移动慢得多。我唯一看到的区别是向右和向下都是加,而向左和向上都是减。我该怎么办?

更新后的代码如下。

//updated movement code
public void moveUp(long timePassed)
    {
        yPos -= vy * timePassed;
        this.rect.y -= vy * timePassed;
    }

    public void moveDown(long timePassed)
    {
        yPos += vy * timePassed;
        this.rect.y += vy * timePassed;
    }

    public void moveRight(long timePassed)
    {
        xPos += vx * timePassed;
        this.rect.x += vx * timePassed;
    }

    public void moveLeft(long timePassed)
    {
        xPos -= vx * timePassed;
        this.rect.x -= vx * timePassed;
    }


//I passed timePassed through a globalInputObject because my input is handled in public //void keyPressed(KeyEvent e) and I didnt know how else to get timePassed in to the //movement method

//Here is the code in gameLoop()
                 globalInputObject.isPacManMovingUp(timePassed);
             globalInputObject.isPacManMovingDown(timePassed);
             globalInputObject.isPacManMovingRight(timePassed);
         globalInputObject.isPacManMovingLeft(timePassed);




//This is inside the GlobalInputObject
public void isPacManMovingUp(long timePassed)
    {
        if(pacMan.movingUp == true)
        {
            pacMan.moveUp(timePassed);
        }
    }

    public void isPacManMovingDown(long timePassed)
    {
        if(pacMan.movingDown == true)
        {
            pacMan.moveDown(timePassed);
        }
    }

    public void isPacManMovingRight(long timePassed)
    {
        if(pacMan.movingRight == true)
        {
            pacMan.moveRight(timePassed);
        }
    }

    public void isPacManMovingLeft(long timePassed)
    {
        if(pacMan.movingLeft == true)
        {
            pacMan.moveLeft(timePassed);
        }
    }

1
你的代码中某处存在导致延迟的问题。 - Hovercraft Full Of Eels
2
你想要关于如何使你的游戏更流畅的具体建议,还是想要优化方面的一般性建议?如果你需要具体的建议,我们需要更多的信息。 - skyuzo
@HovercraftFullOfEels:真的吗? - Blender
1
@Blender:确实。他在询问代码优化,但没有展示代码。我们不知道他是基于计时器还是系统时钟来计算移动距离,没有代码,谁知道呢? - Hovercraft Full Of Eels
3个回答

2
不要总是每次计时器运行时以恒定的距离(似乎是1像素)移动吃豆人,你应该:
  1. 将计时器设置为尽可能快地运行(例如,每毫秒一次或更少)。 编辑:如果设置得太快,则游戏可能会实际上运行得更慢,您需要进行实验。
  2. 使用系统时钟计算每帧之间经过的时间,并按比例移动吃豆人。
以上操作意味着,如果系统“卡顿”,它只会显示较少的每秒帧数,而不是实际上使所有内容移动得更慢。

#1 - 这会使游戏运行变慢。他的游戏因为输入线程与游戏线程并行执行而受到性能影响。 #2 - 说实话,这就是任何动画应该工作的方式。速度是相对于时间的。因此,一个对象每秒移动x像素,并指向y方向。通过计算自上一帧渲染以来的增量,确定对象现在应该在哪里。 - Brian Dilley

2
正如我所担心的那样,您正在基于计时器的时间块来计算移动距离。不应该这样做,因为所有计时器都可能是可变和不可靠的,特别是在小的时间块中。最好基于系统时间差来计算移动。因此,使用计时器或其他东西来运行您的“游戏循环”,但使用双倍精度知道精灵的位置和速度,并根据速度向量(数学向量而不是Java向量)*系统时间差来计算移动距离。这样,如果计时器被延迟了,例如进行垃圾回收,使时间块变大,则移动的距离将相应增加,并且看起来会更平滑。

1
你应该考虑创建一个适当的“主循环”或者有些人称之为“游戏循环”。可以参考这篇维基百科文章中的游戏结构部分。基本上,那些输入事件是从一个与主线程不同的线程中发生/调用的,并且它们直接修改了游戏对象的几何形状。相反,考虑使用以下内容作为主循环:
loop:
    process collision detection
    process animation (alters geometry of game objects)
    process input (more on this later)
    any other game specific logic
    render screen

你的进程输入可能是这样的

if (globalInputObject.movingUp==true) {
   hero.y -= 10;
}
if (globalInputObject.movingDown==true) {
   hero.y += 10;
}
if (globalInputObject.movingLeft==true) {
   hero.x -= 10;
}
if (globalInputObject.movingRight==true) {
   hero.x += 10;
}

你的输入处理程序应该长这样:

public void actionPerformed(ActionEvent evt) {
    if (evt.button==UP_BUTTON) {
        globalInputObject.movingUp=true;
    }
    if (evt.button==DOWN_BUTTON) {
        globalInputObject.movingDown=true;
    }
    if (evt.button==LEFT_BUTTON) {
        globalInputObject.movingLeft=true;
    }
    if (evt.button==RIGHT_BUTTON) {
        globalInputObject.movingRight=true;
    }

}

基本上,您在“额外”线程(输入线程)中进行的处理是最小化的,因此不会干扰主线程。此外,这种方法具有同时轻松支持多个方向(即:UP + RIGHT = 对角线)的好处。

只有超高端游戏才有多个线程(如果它们真的需要)。在游戏中处理同步对性能不利。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接