为扫雷游戏动态创建游戏板

Posted

技术标签:

【中文标题】为扫雷游戏动态创建游戏板【英文标题】:Dynamicly create a playboard for Minesweeper game 【发布时间】:2012-11-15 06:08:14 【问题描述】:

为了学习 C#、XAML,尤其是 MVVM,我开始编写扫雷游戏程序。我制作的第一个版本没有 MVVM 部分,我使用 C# 代码创建并添加了按钮,而不是通过 MVVM 方式在 XAML 中进行。现在我尝试将 MVVM 模式应用到游戏中。

我制作了一个自己的用户控件,其中包含一个代表雷区部分的按钮。这个控件还有一个 ViewModel 和一个 Model Class 来存储一些状态数据和处理一些命令。在一个测试项目中,我创建了 4 个自己的用户控件,并尝试将它们放在一个网格中,其中按钮形成一个 2 x 2 按钮的正方形。在后面的代码中,按钮被放置在 ObservableCollection 对象中。我假设在这个对象中,按钮的列出和索引如下:

Button1
Button2
Button3
Button4

但在演示网格中,我希望按钮显示为

Button1 | Button2
--------+--------
Button3 | Button4

问题是:我如何动态地做到这一点?因为在我的测试项目中我使用 4 个按钮进行测试,但在我想使用这个的项目中,按钮的数量可能会根据玩家选择的游戏难度而有所不同。

第二个问题是我如何弄清楚按钮的邻居是什么。因此,如果网格是 4 x 5 包含 20 个按钮。例如,我选择按钮 8,它的相邻按钮编号为 2、3、4、7、9、12、13 和 14。当按钮被列出时,我怎样才能到达这些相邻按钮?

我希望我的问题足够清楚。

提前谢谢你!

【问题讨论】:

使用统一网格并通过将字段 xpos、ypos 添加到单元格来计算单元格邻居,然后在统一子节点上使用 linq 查询它们 【参考方案1】:

您可以使用将ItemsPanel 设置为GridUniformGrid 的ItemsControl 来显示您的收藏。我有一些 ItemsControl 示例 here 可能会对您有所帮助,因为我不认为 MDSN 的示例很有帮助。

如果您可以将RowsColumns 属性绑定到ViewModel 中的属性,那么UniformGrid 将是最简单的,但是这要求您的所有单元格大小相同,我不记得是否RowsColumns 属性是参与或不参与绑定系统的 DependencyProperties。

<ItemsControl ItemsSource="Binding MyCollection">
    <!-- ItemsPanelTemplate -->
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="Binding ColumnCount" 
                         Rows="Binding RowCount" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

如果这对您不起作用,您可以使用Grid 作为ItemsPanel,并在ItemContainerStyle 中设置Grid.RowGrid.Column 绑定。

这将要求您在ObservableCollection 中的每个单元格对象上都有属性来说明该单元格所在的行/列,但是我怀疑您无论如何都需要这些来确定单击命令中的相邻单元格等内容.

此外,没有内置方法可以绑定Grid 中的行数/列数,所以我倾向于使用一些custom attached properties 来动态构建Grid 的RowDefinitionsColumnDefinitions一个绑定值。

因此,如果您使用 Grid,您的最终结果可能如下所示:

<ItemsControl ItemsSource="Binding Cells">
    <!-- ItemsPanelTemplate -->
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <!-- This is assuming you use the attached properties linked above -->
            <Grid local:GridHelpers.RowCount="Binding Height"
                  local:GridHelpers.ColumnCount="Binding Width" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <!-- ItemContainerStyle -->
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Grid.Column" 
                    Value="Binding ColumnIndex" />
            <Setter Property="Grid.Row" 
                    Value="Binding RowIndex" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

Model/ViewModel 看起来像这样

public class GameViewModel

    // These should be full properties w/ property change notification
    // but leaving that out for simplicity right now
    public int Height;
    public int Width;
    public ObservableCollection<CellModel> Cells;


public class CellModel

    // Same thing, full properties w/ property change notification
    public int ColumnIndex;
    public int RowIndex;
    public bool IsVisible;
    public bool IsMine;
    public int NumberAdjacentMines;

【讨论】:

感谢您的出色回答!如果我在我的测试项目中得到它,我会尝试。 确实如此。它帮助解决了我的问题。再次谢谢你! :)【参考方案2】:

最近我解决了扫雷游戏逻辑,用于一种软件开发竞赛。 我认为他的游戏的主要问题是找到相邻的地雷和物品周围的相邻空单元。 在这两种情况下,我们都必须考虑到每个单元格(项目)最多可以被八个项目(单元格)包围。 如果我们考虑一个 4x4 网格(矩阵)一个由 X,Y 坐标定义的单元 C,它将被以下单元包围:

C1 (X-1,Y-1)
C2 (X,Y-1)
C3 (X+1,Y-1)
C4 (X-1,Y)
C5 (X+1,Y)
C6 (X-1,Y+1)
C7 (X,Y+1)
C8 (X+1,Y+1)

换句话说,我们可以通过平移 x 和 y 轴上的一个点来找到一个项目的相邻单元格。 我通过将游戏的呈现(前端)与游戏逻辑(后端)分开来开发了一个类集。 实际上,通过使用此策略,您可以在不同的 .Net 环境中使用它:我们不会“锁定”在 UI 元素或组件周围。 你可以在这里下载我的项目: https://github.com/alchimya/csharp-minesweeper-sdk

【讨论】:

【参考方案3】:

在我的头顶:

List<Button> myButtons = new List<Button>();
Button myFirstButton = new Button(/*make first button*/);
myButtons.Add(myFirstButton);

void addButtonX ()
//add new button along x axis
    Button tempButt = new Button();        
    Button lastButt = myButtons[myButtons.count - 1];
    tempButt.posX = lastButt.posX + lastButt.sizeX;
    tempButt.posY = lastButt.posY;
    //other code for buttons
    myButtons.Add(tempButt);

void addButtonY ()
//add new button along y axis
    Button tempButt = new Button();
    Button lastButt = myButtons[myButtons.count - 1];
    tempButt.posX = lastButt.posX;
    tempButt.posY = lastButt.posY + lastButt.sizeY;
    //other code for buttons
    myButtons.Add(tempButt);

要跟踪周围的按钮,您需要知道网格有多少按钮宽,例如,如果网格有 20 个按钮宽,则按钮将如下所示:

[B-20-1][B-20][B-20+1]
[B-1]   [B]   [B+1]
[B+20-1][B+20][B+20+1]

其中 B = 点击按钮。

当然,您必须检查是否有任何值超出网格(例如,B-20-1 不小于 0,B+20+1 不大于按钮的数量。),但是这很容易做到。

【讨论】:

-1 首先:对于创建来说不是一个好的解决方案(使用嵌套的 for 更好!)当然代码不适用于 wpf!此外,找出另一个按钮旁边的按钮也不是一个好的解决方案。 你可以使用嵌套的 fors,这就是为什么 addButtonX 和 addButtonY 是函数的原因。它不应该是详尽的代码,只是如何完成它的框架。至于找出相邻的按钮有什么问题? 如果使用嵌套fors,则不需要两个方法。那只是重复的代码。看看这位女士的回答,它更好地解释了如何归档问题所有者想要的内容。当然,她有工作示例,并为给定任务使用 WPF 的内置功能。

以上是关于为扫雷游戏动态创建游戏板的主要内容,如果未能解决你的问题,请参考以下文章

为无尽的跑步游戏创建动态大小的 SKSpriteNode 平台

C - 未知维度的2D游戏板的动态分配

我对扫雷的理解

Weex 版扫雷游戏开发

C语言实现扫雷

棋牌游戏服务器如何动态创建房间?