java - 使用 Java 的 ColorGame(如扫雷)
问题描述
所以我的任务是在java中创建一个带有gui的游戏。游戏是这样描述的:
游戏在大小为 n × m 的区域中进行,其中 m 和 n 小于 50。每个单元格包含一组可能值中的单个颜色或字符或数字(您的选择)。该程序由三个参数 n、m、k 定义。该程序在二维字段上随机放置字符/颜色/数字。第一个颜色/字符/数字属于玩家。游戏的目的是让玩家用他们的颜色绘制整个比赛场地。通过选择源字段和目标字段来启用着色。如果它们与源字段颜色中的目标字段颜色相同,则游戏将所有相邻字段替换为目标颜色。游戏向用户显示颜色/字符/数字的列表。当用户绘制整个场地时,游戏结束,移动次数为所选场地的数量。
我创建了 gui:
公共GGGame {
Butt0nz();
theFrame();
}
public void Butt0nz() {
colors.add(Color.RED);
colors.add(Color.GREEN);
colors.add(Color.BLUE);
colors.add(Color.YELLOW);
colors.add(Color.ORANGE);
b1 = new JButton("Restart");
b1.addActionListener(this);
b2 = new JButton("Moves: "+ "0");
b3 = new JButton("Is continuation possible: ");
menubar = new JMenuBar();
file = new JMenu("File");
open = new JMenuItem("Open");
save = new JMenuItem("Save");
close = new JMenuItem("Exit");
save.addActionListener(this);
open.addActionListener(this);
close.addActionListener(this);
menubar.add(file);
file.add(open);
file.add(save);
file.add(close);
}
public Color randomColor() {
return colors.get(new Random().nextInt(colors.size()));
}
public void theFrame() {
JPanel panel = new JPanel();
setLayout(new BorderLayout());
panel.setLayout(new GridLayout(n, m));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
button[i][j] = new JButton();
button[i][j].setOpaque(true);
button[i][j].setBackground(randomColor());
panel.add(button[i][j]);
}
}
JPanel butt0n = new JPanel();
butt0n.add(b1);
butt0n.add(b2);
butt0n.add(b3);
setJMenuBar(menubar);
setTitle("Color Game");
add(butt0n, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
setSize(1000, 1000);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
'''
我只是想要一些关于如何开始游戏逻辑的建议。我读了一些东西,我明白游戏的逻辑就像扫雷一样,我知道应该实施 FloodFill 算法才能让它工作,但我只想更清楚地回答我应该如何开始。
解决方案
在我看来,您从JButton
s in a开始很好GridLayout(n, m)
,因为与将鼠标侦听器添加到具有自定义绘画的大面板相比,这将使您更干净地处理事件。
我建议将网格(即JPanel
带有sGridLayout
的JButton
)放入 aJScrollPane
以便拥有更大的按钮。例如,如果JScrollPane
使用 no,并且程序打算在分辨率为 1366x768 的屏幕中运行,并且您使用最多 1200x700 像素(为窗口边框本身留出一些像素),则在 50x50 的游戏中,这意味着 24x14 (宽度乘以高度)每个按钮的像素。如果你没问题,那么依赖屏幕的分辨率可能不太好。而是将网格放在JScrollPane
. 我不建议使用 300x300 之类的按钮(因为那会不必要地大),但可能是 30x30 或更大的按钮,这样点击起来不会太难。
另一个想法是让用户即时设置每个按钮的大小,即通过程序中的设置,例如它可以是两个滑块(一个用于宽度,一个用于高度)。
我建议不要使用数字或字符选项,因为这比设置背景颜色更难注意到。
出于显而易见的原因,我建议为每个按钮的背景使用不强烈的颜色。例如,考虑用您指定的颜色填充整个区域......这可能会使某人失明(至少我自己哈哈)。例如,您可以使用darker()
这些颜色的版本。如果这不起作用,我测试了另一种选择:添加一些透明度(即 alpha 颜色分量),以便让按钮的原始颜色显示出来,据我所知,这些颜色不是为盲人设计的。
这是用透明红色绘制一些按钮的示例代码:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(final String[] args) {
final int rows = 10, cols = 10;
final JPanel panel = new JPanel(new GridLayout(0, cols));
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j) {
final JButton button = new JButton() {
@Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
g.setColor(new Color(255, 0, 0, 50));
g.fillRect(0, 0, getWidth(), getHeight());
}
};
button.setPreferredSize(new Dimension(30, 30));
panel.add(button);
}
final JFrame frame = new JFrame("ColorGame.");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
结果在我看来不会让任何人失明(尽管它几乎是红色的):
我认为维基百科中的洪水填充算法解释得很好。因此,除了那里的材料之外,您不需要更多的东西来实现它。它还提供了一些递归伪代码来解决这个问题。
为了轻松检查结束条件,您可以使用int
背景与玩家选择的颜色相同的按钮数量的计数器/。当该数字达到等于 N 乘以 M 时,您就结束了游戏。但是不要忘记为按钮背景的每次更新更新您的计数器。如果您不想使用此计数器,例如它可能使代码不那么干净且更容易出错,那么您可以做的最简单的事情可能是在每次移动后迭代每个按钮并计算哪些按钮具有用户的颜色作为背景,但这将是低效的,尽管对于这个特定场景,我认为最多 50x50 大小的问题(即 2500 次颜色相等检查)的效率并不是很大。
为了更容易调整,我建议对例如类进行子类化JButton
,并重写该getPreferredSize()
方法以返回一个类型的共享对象,Dimension
该对象将包含用户通过设置滑块选择的值。然后每次更新此共享可变对象时,您只需调用'revalidate()
的视图(即包含按钮的面板),它将更新.JScrollPane
Viewport
JScrollPane
由于您进行了子类JButton
化,您可能希望在网格中添加按钮对应的行数和列数,因此当ActionListener
调用 时,您将知道用户单击的位置(并且只需ActionListener
对所有源/按钮使用相同的对象)。在这种情况下,您可能不需要忘记检查ActionEvent
的源属性(这是一个带有行和列 id 的按钮)而不是任何其他按钮。
我认为比 s 更合适JButton
的是使用JToggleButton
s。因此,通过这种方式,您可以让用户知道他们在源-目标按钮对中选择的第一个按钮,如果他们再次单击同一按钮,则撤消他们的初始选择。当然,选择一个切换按钮时,您需要维护对第一个切换按钮的引用,以便将其与第二个切换按钮匹配并知道从哪里开始。
这就是我到现在为止的全部内容,只是为了让你开始。如果您需要更多信息或澄清,请在评论中告诉我。
推荐阅读
- python - adding library in pycharm editor
- javascript - REACT- Storing data that needs to be updated frequently as a global variable instead of using setState() - not recommended?
- angular - 使用 ngrx 存储作为数据源的 Mat Tree Angular 材质
- c - C 结构函数语法 - 传递元素,返回结构
- python - 多线程。如何正确同时ping几个IP?
- django - OneToOne Model delete Cascade in both ways
- flutter - How to convert From DateTime to unix timestamp in Flutter or Dart in general
- html - 数据可视化:有选择地为形状设置动画,但不是 svg 组中的文本
- python - Why we need to use 'get_absolute_url()' instead of custom defined similar methods?
- c++ - 在编译时迭代检查函数是否存在