Java 乒乓球的球拍不停地抖动,没有停留在一个位置
请看我的乒乓球游戏的以下结构 gameLoop();方法Java 乒乓球的球拍不停地抖动,没有停留在一个位置,java,game-engine,interpolation,game-loop,pong,Java,Game Engine,Interpolation,Game Loop,Pong,请看我的乒乓球游戏的以下结构 gameLoop();方法 //Only run this in another Thread! private void gameLoop() { //This value would probably be stored elsewhere. final double GAME_HERTZ = 30.0; //Calculate how many ns each frame should take for o
//Only run this in another Thread!
private void gameLoop()
{
//This value would probably be stored elsewhere.
final double GAME_HERTZ = 30.0;
//Calculate how many ns each frame should take for our target game hertz.
final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
//At the very most we will update the game this many times before a new render.
//If you're worried about visual hitches more than perfect timing, set this to 1.
final int MAX_UPDATES_BEFORE_RENDER = 5;
//We will need the last update time.
double lastUpdateTime = System.nanoTime();
//Store the last time we rendered.
double lastRenderTime = System.nanoTime();
//If we are able to get as high as this FPS, don't render again.
final double TARGET_FPS = 60;
final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
//Simple way of finding FPS.
int lastSecondTime = (int) (lastUpdateTime / 1000000000);
while (running)
{
double now = System.nanoTime();
int updateCount = 0;
if (!paused)
{
//Do as many game updates as we need to, potentially playing catchup.
while( now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER )
{
updateGame();
lastUpdateTime += TIME_BETWEEN_UPDATES;
updateCount++;
}
//If for some reason an update takes forever, we don't want to do an insane number of catchups.
//If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
if ( now - lastUpdateTime > TIME_BETWEEN_UPDATES)
{
lastUpdateTime = now - TIME_BETWEEN_UPDATES;
}
//Render. To do so, we need to calculate interpolation for a smooth render.
float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES) );
//float interpolation = 1.0f;
drawGame(interpolation);
lastRenderTime = now;
//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
while ( now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES)
{
Thread.yield();
//This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
//You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
//FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
try {Thread.sleep(1);} catch(Exception e) {}
now = System.nanoTime();
}
}
}
}
if(p1_up){
if(player.equals("p1")){
p1.moveUp();
}
else
{
p2.moveUp();
}
}
else if(p1_down){
if(player.equals("p1")){
p1.moveDown();
}
else
{
p2.moveDown();
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for(int i=0;i<balls.size();i++){
paintBall(g, balls.get(i));
}
drawPaddle(g, p1);
drawPaddle(g, p2);
}
public void drawPaddle(Graphics g, Paddle p){
paddle_drawX = (int)((p.x - p.last_x)*interpolation + p.last_x);
paddle_drawY = (int)((p.y - p.last_y)*interpolation + p.last_y);
g.drawRect(paddle_drawX, paddle_drawY, 10, 50);
}
moveUp();向下移动();划桨法
public void moveUp(){
last_y = y;
last_x = x;
y -= 50.0;
}
public void moveDown(){
last_y = y;
last_x = x;
y += 50.0;
}
drawGame(插值);方法
//Only run this in another Thread!
private void gameLoop()
{
//This value would probably be stored elsewhere.
final double GAME_HERTZ = 30.0;
//Calculate how many ns each frame should take for our target game hertz.
final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
//At the very most we will update the game this many times before a new render.
//If you're worried about visual hitches more than perfect timing, set this to 1.
final int MAX_UPDATES_BEFORE_RENDER = 5;
//We will need the last update time.
double lastUpdateTime = System.nanoTime();
//Store the last time we rendered.
double lastRenderTime = System.nanoTime();
//If we are able to get as high as this FPS, don't render again.
final double TARGET_FPS = 60;
final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
//Simple way of finding FPS.
int lastSecondTime = (int) (lastUpdateTime / 1000000000);
while (running)
{
double now = System.nanoTime();
int updateCount = 0;
if (!paused)
{
//Do as many game updates as we need to, potentially playing catchup.
while( now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER )
{
updateGame();
lastUpdateTime += TIME_BETWEEN_UPDATES;
updateCount++;
}
//If for some reason an update takes forever, we don't want to do an insane number of catchups.
//If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
if ( now - lastUpdateTime > TIME_BETWEEN_UPDATES)
{
lastUpdateTime = now - TIME_BETWEEN_UPDATES;
}
//Render. To do so, we need to calculate interpolation for a smooth render.
float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES) );
//float interpolation = 1.0f;
drawGame(interpolation);
lastRenderTime = now;
//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
while ( now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES)
{
Thread.yield();
//This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
//You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
//FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
try {Thread.sleep(1);} catch(Exception e) {}
now = System.nanoTime();
}
}
}
}
if(p1_up){
if(player.equals("p1")){
p1.moveUp();
}
else
{
p2.moveUp();
}
}
else if(p1_down){
if(player.equals("p1")){
p1.moveDown();
}
else
{
p2.moveDown();
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for(int i=0;i<balls.size();i++){
paintBall(g, balls.get(i));
}
drawPaddle(g, p1);
drawPaddle(g, p2);
}
public void drawPaddle(Graphics g, Paddle p){
paddle_drawX = (int)((p.x - p.last_x)*interpolation + p.last_x);
paddle_drawY = (int)((p.y - p.last_y)*interpolation + p.last_y);
g.drawRect(paddle_drawX, paddle_drawY, 10, 50);
}
我通过全局变量在主类中启动桨的位置
public Paddle p1 = new Paddle(10, 10);
public Paddle p2 = new Paddle(950, 10);
我有以下事件侦听器来处理按键
Action handle_up_action = new AbstractAction(){
public void actionPerformed(ActionEvent e){
p1_up = true;
}
};
Action handle_up_action_released = new AbstractAction(){
public void actionPerformed(ActionEvent e){
p1_up = false;
}
};
Action handle_down_action = new AbstractAction(){
public void actionPerformed(ActionEvent e){
p1_down = true;
}
};
Action handle_down_action_released = new AbstractAction(){
public void actionPerformed(ActionEvent e){
p1_down = false;
}
};
通过
插值
,您想要实现什么?根据我的理解,它表示上一次“更新时间”与下一次“更新时间”之间经过的时间百分比。
因此,它应该每33.3毫秒从0持续前进到1
我不知道如何在彩弹
方法中使用这个插值
变量,但对于划桨,它将在p.x之间的“伪随机位置”绘制划桨;p、 y
和p.last_x;p、 最后一次(取决于两次更新之间的时间)
为了纠正这一点,根据循环逻辑,您应该了解每个游戏实体(球、桨等)必须有两种状态(位置):
-逻辑状态,在每次更新之间仅更新一次
-视觉状态,可在每次渲染时随时更新
这与您有一组点(表示逻辑状态)并且希望在这些点之间的任意位置插值(表示视觉状态)的情况相同。
您的代码如下所示
第一个解决方案
纠正桨叶抖动的最简单方法是避免插值并使用:
public void drawPaddle(Graphics g, Paddle p){
paddle_drawX = (int)p.x;
paddle_drawY = (int)p.y;
g.drawRect(paddle_drawX, paddle_drawY, 10, 50);
}
但是您的移动看起来像(视觉位置只会在每次更新之间更改一次)
第二种解决方案
你想要p.x;p、 y
为逻辑位置,但视觉位置应在p.last_x;p、 last_y
和逻辑位置(如果渲染是在输入处理和下一个UpdateName()之间完成的):必须重置p.last_x;p、 上次调用updateGame()
时。要实现这一点,请在updateGame()中调用桨的updateMovement()方法
您可以使用其他解决方案,例如使用速度变量或移动功能,以实现平滑移动、加速等。它主要是第二种解决方案的推广。它需要更大的变化,但更灵活、更强大。要实现这一点,您可能需要将最后一个“更新位置”和所有与运动相关的变量(如运动开始日期)存储在桨中。添加一个方法来检索两次更新之间的任何日期都可以调用的“视觉位置”,以及一个更新“逻辑位置”的方法,该方法称为eachupdateGame()
桨叶运动是如何启动和停止的?我编辑了我的帖子,其中包括了桨叶类和运动是如何启动的。但是,我认为我没有代码来阻止这场运动。我以为moveUp(),moveDown()方法会解决这个问题。我错了吗?谢谢@user3059427 p1_up和p1_down等的值在哪里设置?@Quilliom我编辑了一篇帖子,在哪里设置了p1_up和p1_down。我能知道我的帖子为什么被否决吗?我违反了一些规则吗?