用Java编写游戏池 - 如何定义球并移动它们(通过图形g)?

时间:2013-08-08 15:49:32

标签: java swing oop jframe graphics2d

我的计划是在Java中设计一个简单的Pool游戏。

对于球来说,OOP最有意义。所有球都具有相同的功能,因此最好制作一个能够处理球的相对位置和其他变量的Ball类,例如当它进入一个洞时,它会自动移除并增加你的分数。 。所以当它击中一个球Ball.dispose();很合适。

我的问题是我不知道如何操控球并处理它。另外为了移动它我依赖Thread.sleep而不是java.swing.timer,因为我没有可用的Action Performed方法。

如何更轻松地移动球并在需要时摆脱它?

enter image description here

覆盖球的绿色物体是通过在球上画绿色椭圆来擦除球的最后位置的方法。

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;


public class Main extends JFrame{

     Ball redball = new Ball (285, 600, 20, 20, Color.RED); 

    //variables to control redball
    private int rX = redball.getX();
    private int rY = redball.getY();
    private final int rWidth = redball.getWidth();
    private final int rHeight = redball.getHeight();

    int Force = 30; 
    int Bearing = 20; // True Bearing

 public Main (){

         setSize(600, 800);
         setVisible(true); 
         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         setTitle("Pool");

    }
     public void paint(Graphics g){
         super.paint(g);

         // draw the table
         g.setColor (Color.GREEN);
         g.fillRect(100, 100, 400, 600);
         g.setColor (Color.BLACK);
         g.drawRect(99, 99, 401, 601);


             //draw start ball
         g.setColor(redball.getColor());
         g.fillOval(rX, rY, rWidth, rHeight);



         if (Force == 30){
         for (int i = Force; i > 0;i--){
             try {
                    Thread.sleep(100);
                } catch(InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
             Force--;
             if (rY > 98 + rWidth) {
                 rY = rY - i;
                 rX = rX + (Bearing/5);
             }

             g.fillOval(rX, rY, rWidth, rHeight);
             g.setColor(Color.GREEN);
             repaint ();
             g.fillOval(rX - (Bearing/5), rY + i, rWidth, rHeight); // repaint last ball
             g.setColor(Color.RED);
             repaint ();
          }
         }



         // Ball.dispose (redball);

     }

    public static void main(String[] args) {

        new Main();
    }

以下是球类

public class Ball {

    private int x;
    private int y;
    private int width;
    private int height;
    private Color color;

    public Ball (int x, int y, int width, int height, Color color)
    {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.color = color;
    }

    public void setX (int x){
        this.x = x;
    }

    public int getX (){
        return this.x;
    }

    public void setY (int x){
        this.y = y;
    }

    public int getY (){
        return this.y;
    }

    public void setWidth (int width){
        this.width = width;
    }

    public int getWidth (){
        return this.width;
    }

    public void setHeight (int height){
        this.height = height;
    }

    public int getHeight (){
        return this.height;
    }

    public void setColor (Color color){
        this.color = color;
    }

    public Color getColor (){
        return this.color;
    }

    public static void dispose(Ball ball) {

    ball = null; // if I call this method nothing happens

    }


}

2 个答案:

答案 0 :(得分:2)

我要做的是保持所有活动球的List<Ball>字段。然后每次迭代,遍历该列表并更新并绘制球。当Ball消失时,只需将其从列表中删除即可。

考虑因素是:谁决定何时处理球?如果球外有什么东西,你有几个选择,其中包括:

  1. 如上所述,您只需从列表中删除球即可。
  2. 您可以在其他地方的球中设置“死”标志,然后在每次迭代时从列表中删除所有已设置“死”标志的球。
  3. 如果Ball本身确定何时离开,你可以选择其中一些选项:

    1. 也许是方法boolean Ball.think(),它更新球的状态,如果球仍然是好的则返回true,如果它已经死了则返回false。然后,当您遍历您的球列表时,在所有球上调用think()进行更新,并从列表中删除返回false的内容。
    2. 与上面的选项2相关,如果Ball有一个think方法或类似的东西,球可以设置自己的“死”标志(或者球外的东西可以设置它),然后主循环可以从列表中删除死球。
    3. 如果你想保持Ball实例而不是画它,你也有一些选择:

      1. 只需跳过标记为“死亡”的球的处理,而不是删除它们。
      2. 将“死”球移到另一个死球列表中。
      3. 就个人而言,我喜欢boolean think()方法,因为它很容易让您为场景中的对象指定基本界面。在这种情况下,您还可以让对象绘制自己。 E.g:

        interface Entity {
            public boolean think ();
            public void paint (Graphics g);
        }
        
        class Ball implements Entity {
            @Override public boolean think () {
                // return true if alive, false if dead
            }
            @Override public void paint (Graphics g) {
                // draw this ball
            }
        }
        
        // then in your main update loop:
        List<Entity> entities = ...;
        Iterator<Entity> eit = entities.iterator();
        while (eit.hasNext())
            if (!eit.next().think()) 
                eit.remove();
        
        // and in paint:
        for (Entity e:entities)
            e.paint(graphics);
        

        如果你想采取我上面提到的选项,只是跳过“死”球而不是去除它们,这样的事情会更合适(为了简洁而省略Ball),如果球是,则isActive()返回true如果它暂时“死”,则为活动还是错误:

        interface Entity {
            public boolean isActive ();
            public void think (); // think returns nothing
            public void paint (Graphics g);
        }
        
        // then in your main update loop:
        List<Entity> entities = ...;
        for (Entity e:entities)
            if (e.isActive())
                e.think();
        // it is the responsibility of something outside the ball to restore it to an
        // active state, since think() isn't called if !isActive(). alternatively, you 
        // could always call think(), and just don't paint inactive balls.
        
        // and in paint:
        for (Entity e:entities)
            if (e.isActive())
                e.paint(graphics);
        

        但是,你没有这样做,上面列出的所有选项都有很多参数,等等。例如,在您的应用程序中,如果您知道自己只处理Balls,则不需要Entity接口;如果所有的物理逻辑都发生在其他地方,那么think()可能不是最方便的方法(当然可以编写代码将逻辑放在Ball中)。

        正如你所看到的,有许多方法可以给猫皮肤,但我希望这里的东西有所帮助。

答案 1 :(得分:2)

以下是一些建议。

  • 创建一个包含游戏所有球的Rack类。

  • 将球的绘制代码移动到Ball类中。我意识到这个建议不是纯粹的MVC,但是当对象绘制自己时,游戏更容易编码。

  • 请勿尝试重新绘制部分屏幕。只需重新绘制整个屏幕,它就会轻松得多,而且非常快。

  • 不要处置任何Ball实例。将其移动到绘图代码识别且不绘制的某个负面位置。

当你编写一个动作游戏时,你的主循环应该看起来像这样(psudeocode)。

while (running) {
    calculateRackPosition();
    drawRack();
    Thread.sleep(100L);
}

逐步移动球,然后重新绘制屏幕。这就是你编码任何动画的方式。这些代码会在球移动时运行。

你会为这个人瞄准射击时写下其他代码。