Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用Java编写一个台球游戏-如何定义球并移动它们(通过图形g)?_Java_Swing_Oop_Jframe_Graphics2d - Fatal编程技术网

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

用Java编写一个台球游戏-如何定义球并移动它们(通过图形g)?,java,swing,oop,jframe,graphics2d,Java,Swing,Oop,Jframe,Graphics2d,我的计划是用Java设计一个简单的游泳池游戏 OOP在这里对球最有意义。所有的球都具有相同的功能,因此,创建一个Ball类是一个好主意,该类将处理球的相对位置和其他变量,例如当球进入一个洞时,它将自身移除并增加分数。所以当它击中一个洞球时;很合适 我的问题是,我不知道如何操纵球和处理它。另外,为了移动它,我依赖于Thread.sleep而不是java.swing.timer,因为没有可用的操作执行方法 我怎样才能更轻松地移动球,并在需要时将其扔掉 覆盖在球上的绿色物体是我通过在球上画一个绿色椭圆

我的计划是用Java设计一个简单的游泳池游戏

OOP在这里对球最有意义。所有的球都具有相同的功能,因此,创建一个Ball类是一个好主意,该类将处理球的相对位置和其他变量,例如当球进入一个洞时,它将自身移除并增加分数。所以当它击中一个洞球时;很合适

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

我怎样才能更轻松地移动球,并在需要时将其扔掉

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

这是舞会的课程


我要做的是维护所有活动球的列表字段。然后每次迭代,迭代列表,更新并绘制球。当球离开时,只需将其从列表中删除即可

一个需要考虑的问题是:谁决定何时处理球?如果球外有东西,你有几个选择,其中包括:

正如我上面提到的,您可以简单地从列表中删除球。 您可以在球的其他位置设置一个死标志,然后在每次迭代中从已设置死标志的列表中删除所有球。 如果球本身决定何时离开,那么除其他外,您还有一些选择:

可能是一个boolean Ball.think方法,它更新球的状态,如果球仍然良好,则返回true;如果球已死亡,则返回false。然后,当您遍历球的列表时,调用think对所有球进行更新,并从列表中删除返回false的球。 与上面的选项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);
如果您想选择我上面提到的跳过死球而不是移除死球的选项,那么为了简洁起见,这样做更合适,因为如果球处于活动状态,isActive将返回true;如果球处于暂时死球状态,isActive将返回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);
尽管如此,您不必这样做,上面列出的所有选项都有很多理由支持,等等。例如,在您的应用程序中,如果您知道您只处理球,那么就不太需要实体接口;如果所有的物理逻辑都发生在其他地方,那么思考可能不是最方便的方式,当然,代码可以编写成将逻辑放在球上


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

我要做的是维护一个包含所有活动球的列表字段。然后每次迭代,迭代列表,更新并绘制球。当球离开时,只需将其从列表中删除即可

一个需要考虑的问题是:谁决定何时处理球?如果球外有东西,你有几个选择,其中包括:

正如我上面提到的,您可以简单地从列表中删除球。 您可以在球的其他位置设置一个死标志,然后在每次迭代中从已设置死标志的列表中删除所有球。 如果球本身决定何时离开,那么除其他外,您还有一些选择:

可能是一个boolean Ball.think方法,它更新球的状态,如果球仍然良好,则返回true;如果球已死亡,则返回false。然后,当您遍历球的列表时,调用think对所有球进行更新,并从列表中删除返回false的球。 与上面的选项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);

如果您想选择我上面提到的跳过死球而不是移除死球的选项,那么为了简洁起见,这样做更合适,因为如果球处于活动状态,isActive将返回true;如果球处于暂时死球状态,isActive将返回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);
尽管如此,您不必这样做,上面列出的所有选项都有很多理由支持,等等。例如,在您的应用程序中,如果您知道您只处理球,那么就不太需要实体接口;如果所有的物理逻辑都发生在其他地方,那么思考可能不是最方便的方式,当然,代码可以编写成将逻辑放在球上


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

这里有一些建议

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

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

不要尝试重新绘制屏幕的一部分。只需重新绘制整个屏幕就可以轻松得多,速度也相当快

不要处理任何球实例。将其移动到绘图代码可以识别但无法绘制的负位置

当你在编写动作游戏时,你的主循环应该是这样的

while (running) {
    calculateRackPosition();
    drawRack();
    Thread.sleep(100L);
}
您可以递增地移动球,然后重新绘制屏幕。这是编写任何动画的方式。此代码将在球移动时运行


您可以编写其他代码,以确定此人瞄准射击的时间。

以下是一些建议

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

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

不要尝试重新绘制屏幕的一部分。只需重新绘制整个屏幕就可以轻松得多,速度也相当快

不要处理任何球实例。将其移动到绘图代码可以识别但无法绘制的负位置

当你在编写动作游戏时,你的主循环应该是这样的

while (running) {
    calculateRackPosition();
    drawRack();
    Thread.sleep(100L);
}
您可以递增地移动球,然后重新绘制屏幕。这是编写任何动画的方式。此代码将在球移动时运行


您可以编写其他代码,以便在该人瞄准射击时使用。

编辑以添加其他选项的示例,包括保留未使用的球参考的选项。我现在已经完成了所有这些编辑。非常深思熟虑的描述性答案。正如我在对吉尔伯特答案的评论中所暗示的,您将只希望从JFrame的paintComponent中绘制,就像您几乎已经在做的一样-您应该使用paintComponent,而不是paint。绘制的一般方法是:擦除整个背景->在当前位置重新绘制所有球。这将产生预期的效果。作为一种优化,但不要过早地进行优化,您可以在脏区域和旧球位置下重新绘制背景,或者将旧球位置添加到您维护的脏区域。不过,首先,只需删除整个bg并重新绘制。在原始代码中,您在循环中的每个球下绘制bg,您在移动球后进行此操作,但您希望在移动球之前进行此操作,否则您将留下一条轨迹。同时在public static void dispose ball{ball=null;}中响应您的注释-不,这当然不起任何作用,您所做的只是将局部参数变量ball设置为null。它在该方法之外没有任何效果。编辑以添加其他选项的示例,包括保留未使用的球引用的选项。我现在已经完成了所有这些编辑。非常深思熟虑的描述性答案。正如我在对吉尔伯特答案的评论中所暗示的,您将只希望从JFrame的paintComponent中绘制,就像您几乎已经在做的一样-您应该使用paintComponent,而不是paint。绘制的一般方法是:擦除整个背景->在当前位置重新绘制所有球。这将产生预期的效果。作为一种优化,但不要过早地进行优化,您可以在脏区域和旧球位置下重新绘制背景,或者将旧球位置添加到您维护的脏区域。不过,首先,只需删除整个bg并重新绘制。在原始代码中,您在循环中的每个球下绘制bg,您在移动球后进行此操作,但您希望在移动球之前进行此操作,否则您将留下一条轨迹。同时在public static void dispose ball{ball=null;}中响应您的注释-不,这当然不起任何作用,您所做的只是将局部参数变量ball设置为null。它在该方法之外没有任何效果;其中一个例子是Java实现;其中一个例子是Java实现。
一个重要的警告:如果您直接从另一个线程进行绘制,则必须使用SwingUtilities.invokeLater或.invokeAndWait在UI线程上执行绘制-尽管您确实不希望在paintComponent之外进行绘制。正确处理这个问题有无数种选择,在评论中有太多的选择;e、 g.将更新逻辑移动到GUI线程很容易,但并不理想,在每个更新帧后发布重新绘制请求,并从球中获取GUI线程查询信息,仅存储重新绘制所需数据的线程安全快照,等等@Jason C:当绘图代码位于模型类中时,它是从绘图JPanel的paintComponent方法执行的。因此,希望它已经在EDT上了。使用drawRack运行循环时的伪代码不一定允许这样做,假设drawRack直接绘制,就好像循环在EDT上一样,当然,EDT被循环阻塞了,这就是为什么我假设循环在另一个线程上。这不是批评,只是观察可以使用javax.swing.Timer在EDT上安排重复任务,也可以使用常规的java.util.Timer,而不是在EDT上。一个重要的警告:如果您直接从另一个线程绘制,您必须使用SwingUtilities.invokeLater或.invokeAndWait在UI线程上执行绘制-尽管您确实不想在paintComponent之外进行绘制。正确处理这个问题有无数种选择,在评论中有太多的选择;e、 g.将更新逻辑移动到GUI线程很容易,但并不理想,在每个更新帧后发布重新绘制请求,并从球中获取GUI线程查询信息,仅存储重新绘制所需数据的线程安全快照,等等@Jason C:当绘图代码位于模型类中时,它是从绘图JPanel的paintComponent方法执行的。因此,希望它已经在EDT上了。使用drawRack运行循环时的伪代码不一定允许这样做,假设drawRack直接绘制,就好像循环在EDT上一样,当然,EDT被循环阻塞了,这就是为什么我假设循环在另一个线程上。这不是批评,只是观察可以使用javax.swing.Timer在EDT上安排重复任务,也可以使用常规java.util.Timer,而不是在EDT上。