为Minesweeper游戏动态创建一个游戏板

时间:2012-11-27 15:19:04

标签: c# xaml mvvm

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

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

Button1
Button2
Button3
Button4

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

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

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

第二个问题是我如何弄清楚按钮的neigbhours是什么。因此,如果网格是4乘5包含20个按钮。例如,我选择按钮8,它具有相邻按钮编号2,3,4,7,9,12,13和14.当列出按钮时,如何到达那些邻居按钮?

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

提前谢谢!

3 个答案:

答案 0 :(得分:4)

您可以使用ItemsPanel设置为GridUniformGrid的{​​{3}}来展示您的收藏集。我有一些ItemsControl示例ItemsControl可能对您有所帮助,因为我没有发现MDSN的示例非常有用。

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

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

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

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

此外,没有内置的方法可以绑定Grid中的行数/列数,因此我倾向于使用一些here来动态构建网格RowDefinitionsColumnDefinitions基于约束值。

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

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

模型/ 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;
}

答案 1 :(得分:1)

最近,我解决了扫雷游戏逻辑,进行了一场软件开发竞赛。 我认为他的游戏的主要问题是找到相邻的地雷和物品周围的相邻空单元。 在这两种情况下,我们必须考虑到最多每个单元格(项目)可以被八个项目(单元格)包围。 如果我们考虑一个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

答案 2 :(得分:-1)

脱离我的头顶:

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不大于按钮数量。)但是这很容易做到。