Java游戏中对象应该如何处理?

4

编辑:我刚刚删除了整篇文章并重新制定了更通用的问题。

我想做一个简单的策略游戏:地图、单位。

地图:一个类。单位:另一个类,自己绘制。

简单的问题:

  1. 一个单位应该如何在地图上重绘自己。
  2. 一个单位应该是一个JPanel或类似的Swing组件(只是为了能够将它们作为一个具有自己的鼠标处理程序的实体来管理),还是可以是其他东西,但不要忽视它应该是一个具有自己的操作处理程序和字段的自主对象。
  3. 这个地图-单位模型对于一个简单的游戏来说是否正确,可以帮助我以一种有趣的方式学习Java和OOP基础知识。

就是这样!


你为什么要使用Swing来做这件事呢?你可以使用Java2D或者更简单的框架,比如Processing来完成它。 - Jack
1
我正在使用JAVA2D进行绘图... 我使用Swing对象来创建它们的实体! - Gabriel
2个回答

2
有两种方法。
一种是创建一个地图类Map,将其作为主要的JPanel,维护单位的集合,但保持Unit类为非Swing。当调用Map的paint()方法时,通过调用每个可见Unit中的一个方法来要求每个Unit重新绘制自身。在这种情况下,Unit类处理绘画和计算,而Map处理每个Unit的点击和事件。如果需要,它可以将消息传递给Unit;但是,如果单元本身不需要知道这些事情,那么这种方法就能很好地工作。Unit类轻巧而经济。您还可以决定Unit是否知道其在Map中的位置。
另一种方法是将每个单元作为一个JComponent,并单独处理其自己的事件。缺点是每个Unit实例都会为绘制占用大量数据。因此,如果有成百上千个单位,而只有几个被绘制出来,这样的效率并不高。优点是每个单元都可以处理自己的GUI事件,无需进行转换等操作。这也假设在游戏中实际单位与屏幕上的图形单位之间存在1:1的映射关系。如果一个Unit需要知道周围有哪些其他Units,某些事件处理程序在Unit中实现起来也比较棘手!
第三种方法是拥有一个纯数据的Unit类,包含有关单位的游戏信息,如(1)中所述,但该类具有在需要时创建JComponent的方法。例如,JComponent可以是Unit的内部类——它可以访问Unit的数据。如果一个单元需要在两个地方显示(例如,在2个屏幕或视图上),这种方法非常好。
补充以解决评论问题:
在第一种方法中,Map(主要的JPanel)实现了鼠标处理程序,并依次询问每个Unit是否被“点击”。这允许您执行更复杂的操作,例如,使两个单元重叠/位于顶部,并且两者都可以响应单击。
此外,例如,它们可能不是矩形的,或者可能用alpha通道绘制(如果Units是JComponents,则默认情况下会将其整个矩形上的任何鼠标事件捕获为自己的)。如果您的单元不重叠并且位于自己的矩形中,则JComponent自己的鼠标处理程序就足够了。
如果使用第一种方法,Map可以询问每个Unit是否包含一个被点击的点,然后Map可以处理“选择”过程。请记住,单独的单位将无法计算出其他单位是否被选择——选择可能涉及取消选择另一个单位。在这种情况下,选择操作实际上是Map操作,而不是Unit操作,尽管它也可能改变Unit的外观或功能。
如果您为单位使用单独的JComponents,您可以覆盖contains(Point)以确定鼠标是否命中该项,但这不会让其他单位也响应点击。但是每个单位都可以“选择自己”(设置在绘图时使用的标志),然后通知地图(它需要使用getParent或预设属性找到它)。
在决定此设计之前,您需要知道以下关键事项: 1. 每个游戏是否需要超过一个“地图”面板? 2. 是否需要显示的单位对象多于要显示的单位对象? 3. 是否需要多次显示单位? 如果答案是肯定的,建议按照3中所建议的将“数据”类与“视图”类分开。 接下来,一个单位需要“做什么”,以及为了做到这一点,它需要了解地图和其他单位的情况?例如,在这种情况下,移动通常由Map类完成,因为它取决于Map和其他单位;绘制最好由Unit完成,因为可能有许多具有不同数据结构的单位子类型需要绘制;正如您指出的那样,单位选择操作位于中间某处。如果您看看Swing如何实现这一点(例如ButtonGroup、ListSelectionModel),则“选择”本身可以被认为是一个单独的类。

嗯,看起来选项1是可行的。如果我不将Unit扩展为JComponent,则会失去鼠标处理程序属性……或者我错了? - Gabriel
通过点击地图,一个单位应该如何知道它已被选中?这种交互方式是我不太清楚的... - Gabriel
我如何将Map中的事件传递到Unit?目前,我考虑使用一个线程来检查地图上的点击点(每50毫秒一次),然后在Unit对象数组中获取该坐标,并且如果单位在地图上的实际位置与点击点匹配(在一定比例内),则被选中。有更好的方法吗?我只是在头脑风暴。我现在才开始使用线程。另外:所有关于绘制地图的逻辑都应该在一个大的paint(Graphics g)方法中,对吗?例如,在上述线程中提到的数组循环。 - Gabriel
我认为当点击地图时,可以通过这样做来避免创建一个循环数组的线程。 - Gabriel
事件的目的是为了避免定期检查新事件的线程。当创建您的Map时,应调用“addMouseListener”。Java将自动使用相对于地图的单击坐标调用鼠标侦听器,然后您可以将事件传递给for循环中的每个单位,或者检查每个单位是否包含该点。如果使用方法(2),则不需要这样做,每个Unit都可以在其自己的构造函数中调用“addMouseListener”。 - Sanjay Manohar

0
我几年前制作了一个木棍跳棋游戏,它基本上只是一个扩展MouseListener和MouseMotionListener的JPanel。这些木棍是在paint方法中绘制的原始圆形,但是您可以通过获取光标位置并数学计算出它所落入的方格来跟踪它们的着陆位置。这与存储棋盘上每个方格的1或0的数组相对应,具体取决于是否有任何东西存在。您还可以通过找到当前光标位置然后在实现MouseMotionListener时从mouseDragged方法中调用repaint()来拖动每个棋子。
我建议一开始就这样做。创建一个任意大小的数组,并使用该数组跟踪单位。然后每次移动时,只需检查该数组并在paint方法中重新绘制您的单位即可。如果您正在使用鼠标移动,则可以在mouseDown上获取单位的当前位置,然后使用mouseDragged执行我之前提到的操作,在mouseUp上获取最终位置,并在mouseUp中根据合法移动等进行计算。
在paint方法中,您将只需循环遍历定义地图的数组,并且如果当前位置上的数组中有一个单位,则执行g.fillOval(x,y,x_dimension,y_dimension)之类的操作。该数组可以将其元素设置为0,表示当前位置没有单位,或者设置为1,表示当前位置是第1个团队的单位,设置为2等等。 paint方法将根据这些数字绘制相应的图案(为每种类型更改颜色或其他操作)。
希望这有点意义。

如果我希望每个单元自我感知(例如,在被点击时),你会如何实现呢? - Gabriel
不必将基本信息存储在整数数组中,而是可以使用单元对象数组。只需创建一个名为“Unit”的类,然后在该类中包含有关每个单元的所有要存储的信息即可。你甚至可以保留一个类似的整数数组,以便检查某个东西是单位还是地形的正方形。这取决于你想如何编写代码。 - Cameron
在每个单元对象内,您可以拥有一个布尔值来表示它是否处于活动状态(当您单击该坐标时打开),然后在某个地方进行检查,然后可能会发生某种自动化操作。再次强调,这取决于您。拥有对象数组对于这种类型的东西非常强大,因为您可以做很多事情。 - Cameron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接