乒乓球拍不停留在一个位置

时间:2013-12-04 02:11:37

标签: 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 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();
            }


         }
      }
   }

updateGame();方法

  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();

          }


  }

为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(内插);方法

      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);


      }

我是游戏编程的初学者,所以我对游戏循环并不了解。我在互联网上找到了上面固定的时间步骤游戏循环并将其用作我游戏的游戏循环。环使球移动平稳,但移动时桨不停留在一个地方。当我按下一个向下按键时移动我的桨,然后桨持续摇动 没有停在一个地方。桨的y坐标不断变化,如

33,45,20,59,34,59,34,59,33,59,34,58

我知道问题在于插值,因为它会不断改变值,这将改变渲染中桨的y坐标。我已经考虑了一段时间了,我不知道如何使游戏循环适用于任何动作,所以我来这里寻求帮助。我感谢任何建议/帮助!

这是我的完整Paddle课程。

   public class Paddle
   {

       float x;
       float y;      
       float last_y;
       float last_x;

      public Paddle(int x, int y)
      {

          this.x = x;
          this.y = y;
          this.last_x = x;
          this.last_y = y;

      }


      public void setNewX(int d){


      last_y = y;
      last_x = x;

      x = d;


      }


      public void setNewY(int d){

      last_y = y;
      last_x = x;

      y = d;


      }

      public void moveUp(){

          last_y = y;
          last_x = x;

          y -= 50.0;


      }




      public void moveDown(){

          last_y = y;
          last_x = x;

          y += 50.0;


      }



    }

我通过全局变量启动主类中的paddle位置。

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;

      }

  };

1 个答案:

答案 0 :(得分:1)

你想用interpolation做什么?根据我的理解,它表示上一个上一个和下一个“更新时间”之间经过的时间百分比。 所以它应该从0到1连续进展,每个33.3毫秒。

我不知道你如何在interpolation方法中使用这个paintBall变量,但是对于拨片,它会在p.x;p.y之间的“伪随机位置”绘制你的划线和p.last_x;p.last_y(取决于两者之间的时间updateGame())。

为了纠正这个问题,从你的循环逻辑中,你应该明白每个游戏实体(球,桨,......)必须有两个状态(位置): - 逻辑状态,仅在每个TIME_BETWEEN_UPDATES更新 - 可视状态,可在每次渲染时随时更新。

就像你有一组点(代表逻辑状态)并且想要在这些点之间插入任何内容(重新表示视觉状态)一样。 您的代码就像this

第一个解决方案

纠正桨叶抖动的最简单方法是避免插值并使用:

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);
}

但您的动作看起来像this(视觉位置只会更改每个TIME_BETWEEN_UPDATES

第二个解决方案

您希望p.x;p.y成为逻辑位置,但如果在输入处理和下一个updateGame()之间进行渲染,则视觉位置应在p.last_x;p.last_y和逻辑位置之间进行插值:您调用p.last_x;p.last_y时必须重置updateGame()。要实现此目的,请在updateGame()内调用paddles的updateMovement()方法。

public void updateMovement(){
    last_y = y;
    last_x = x;
}

您可以使用其他解决方案,例如使用速度变量或移动功能,以便获得平稳的移动,加速度等。它主要是第二种解决方案的概括。它需要更大的变化,但它更灵活,更强大。要实现此目的,您可能希望在桨中存储最后的“更新位置”,以及所有与运动相关的变量,例如移动开始日期。添加一个方法来检索可以使用两次更新之间的任何日期调用的“视觉位置”,以及一种更新称为每个updateGame()的“逻辑位置”的方法。