2

我的计划是用 Java 设计一个简单的桌球游戏。

OOP 在这里对球最有意义。所有的球都具有相同的功能,因此最好创建一个 Ball 类来处理球的相对位置和其他变量,例如当它进入一个洞时,它会自行移除并增加你的分数. 所以当它碰到一个洞 Ball.dispose(); 会很合适。

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

我怎样才能更轻松地移动球并在需要时摆脱它?

在此处输入图像描述

覆盖球的绿色东西是我通过在球上画一个绿色椭圆来擦除球的最后位置的方法。

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

    }


}
4

2 回答 2

2

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

一个考虑因素是:谁来决定何时处理球?如果球之外的东西确实如此,您有几个选择,其中包括:

  1. 正如我上面提到的,您可以简单地从列表中删除球。
  2. 您可以在其他地方的 Ball 中设置一个“dead”标志,然后在每次迭代中从列表中删除所有设置了“dead”标志的 Balls。

如果球本身决定什么时候离开,你有几个选择,除其他外,还有:

  1. 也许是一个方法boolean Ball.think(),它更新球的状态,如果球仍然是好的,则返回真,如果球死了,则返回假。然后,当您遍历您的球列表时,您会调用think()所有这些球进行更新,并从返回 false 的列表中删除这些球。
  2. 与上面的选项 2 相关,如果 Ball 有think方法或类似的东西,则该球可以设置自己的“死”标志(或 Ball 外部的东西可以设置它),然后主循环可以从列表中删除死球。

如果你想保留 Ball 实例而不是绘制它,你也有一些选择:

  1. 只需跳过标记为“死”的球的处理,而不是将它们移除。
  2. 将“死”球移动到不同的死球列表。

就个人而言,我喜欢这种boolean think()方法,因为它允许您轻松地为场景中的对象指定基本接口。在这种情况下,您也可以让对象自己绘制。例如:

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 或 false如果它暂时“死”:

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

不过,您不必这样,上面列出的所有选项都有很多论据,等等。Entity例如,在您的应用程序中,如果您知道您只处理球,则不需要太多接口;think()如果您的所有物理逻辑都发生在其他地方,并且可能不是最方便的方法(尽管当然可以编写代码以将逻辑放在 Ball 中)。

正如你所看到的,有很多方法可以给猫剥皮,但我希望这里能有所帮助。

于 2013-08-08T15:55:02.170 回答
2

这里有一些建议。

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

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

  • 不要尝试重新绘制屏幕的一部分。重绘整个屏幕要容易得多,而且速度非常快。

  • 不要释放任何 Ball 实例。将其移动到您的绘图代码识别但不绘制的某个负位置。

当您编写动作游戏时,您的主循环应该看起来像这样(伪代码)。

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

您逐步移动球,然后重绘屏幕。这就是您编写任何动画的方式。此代码将在球移动时运行。

您可以为该人瞄准射击时编写其他代码。

于 2013-08-08T15:58:50.090 回答