一、创建项目
1.创建WPF项目,设置初始化窗口大小(初级难度):高x宽为430x350。
2.添加文件夹Images,并添加相关图片。


3.xaml中引入图片资源。

<Window.Resources>
<BitmapImage x:Key="ImgSpace" UriSource="/Images/space.bmp"/>
<BitmapImage x:Key="ImgMine" UriSource="/Images/mine.png"/>
<BitmapImage x:Key="ImgNum1_8" UriSource="/Images/num1_8.png"/>
<BitmapImage x:Key="ImgForeground" UriSource="/Images/foreImg.png"/>
<BitmapImage x:Key="ImgMineOrTimer" UriSource="/Images/mineAndTimer.png"/>
<BitmapImage x:Key="ImgBomb" UriSource="/Images/bomb.png"/>
</Window.Resources>

4.添加窗口元素
(1)菜单

<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="游戏(_G)">
<MenuItem x:Name="MenuGameStart" Header="开始游戏(_S)" Click="MenuGameStart_Click"/>
<Separator/>
<MenuItem x:Name="MenuGameExit" Header="退出游戏(_x)" Click="MenuGameExit_Click"/>
</MenuItem>
<MenuItem Header="级别(_L)">
<MenuItem Header="初级(_L)" x:Name="MenuLowLevel" IsChecked="True" Click="MenuLowLevel_Click"/>
<MenuItem Header="中级(_M)" x:Name="MenuMiddleLevel" IsChecked="False" Click="MenuMiddleLevel_Click"/>
<MenuItem Header="高级(_H)" x:Name="MenuHighLevel" IsChecked="False" Click="MenuHighLevel_Click"/>
</MenuItem>
<MenuItem Header="选项(_O)">
<MenuItem x:Name="MenuOptionsMusic" Header="音乐音效(_S)" IsCheckable="True" IsChecked="True"
Click="MenuOptionsMusic_Click"/>
<Separator/>
<MenuItem x:Name="MenuShowAllMine" Header="显示地雷(_O)" Click="MenuShowAllMine_Click"/>
<MenuItem x:Name="MenuRestoreMap" Header="继续游戏(_F)" Click="MenuRestoreMap_Click"/>
<Separator />
<MenuItem x:Name="MenuResetData" Header="重置记录(_R)" Click="MenuResetData_Click" />
</MenuItem>
<MenuItem Header="帮助(_H)">
<MenuItem x:Name="MenuHelpAbout" Header="关于(_A)..." Click="MenuHelpAbout_Click"/>
</MenuItem>
</Menu> </DockPanel>

(2)在菜单之后,</DockPanel>之前添加其他界面元素

<Grid Margin="3" DockPanel.Dock="Top">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="SkyBlue" VerticalAlignment="Center">
<Grid Margin="2 2 2 2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" HorizontalAlignment="Right" Margin="0 0 60 0" Orientation="Horizontal">
<Image x:Name="ImgClock" Width="35" Height="35" />
<Border Height="30" Width="60" CornerRadius="6" BorderThickness="1" VerticalAlignment="Center" Background="#333333">
<TextBlock x:Name="textBlockTime" Text="" VerticalAlignment="Center" HorizontalAlignment="Center"
FontSize="14" Foreground="AliceBlue"/>
</Border>
</StackPanel> <StackPanel Grid.Column="1" HorizontalAlignment="Left" Orientation="Horizontal" Margin="40 0 0 0">
<Image x:Name="ImgMineNum" Width="35" Height="35" /> <Border Height="30" Width="60" CornerRadius="6" BorderThickness="1" VerticalAlignment="Center" Background="#333333">
<TextBlock x:Name="textBlockMineNum" Text="" VerticalAlignment="Center" HorizontalAlignment="Center"
FontSize="14" Foreground="AliceBlue"/>
</Border>
</StackPanel>
</Grid>
</StackPanel>
<StackPanel Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top">
<Canvas x:Name="BackCanvas" Width="315" Height="315" Background="LightCyan" Margin="10" />
</StackPanel>
<StackPanel Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top">
<Canvas x:Name="ForeCanvas" Width="315" Height="315" Margin="10"
MouseLeftButtonDown="ForeCanvas_MouseLeftButtonDown"
MouseRightButtonDown="ForeCanvas_MouseRightButtonDown"/>
</StackPanel>
</Grid>

其中两个Image用于显示时钟和地雷数图例,其后两个TextBlock分别用于显示读秒数和剩余地雷数。
两个重叠的Canvas,分别显示底层图块和前景图块。底层图块游戏开始后是固定的,其中会显示随机分布的地雷及其周边地雷数值,而前景图块可以通过鼠标点击进行移除或标记为问号或小红旗。

二、载入图片资源

1、添加一个静态图片帮助类ImageHelper,方便以后的图片切片操作。

public static class ImageHelper
{
/// <summary>
/// BitmapSource图片源剪切操作函数
/// </summary>
/// <param name="bmpSource">等待剪切的源</param>
/// <param name="cut">剪切矩形</param>
/// <returns>已剪切的图片源</returns>
public static BitmapSource CutImage(BitmapSource bmpSource, Int32Rect cut)
{
return new CroppedBitmap(bmpSource, cut);
} /// <summary>
/// 将BitmapImage转换为BitmapSource
/// </summary>
/// <param name="bitmapImage">待转换的BitmapImage</param>
/// <returns>BitmapSource</returns>
public static BitmapSource BitmapImageToBitmapSource(BitmapImage bitmapImage)
{
ImageSource imageSource = bitmapImage;
Bitmap bitmap = ImageSourceToBitmap(imageSource);
BitmapSource bitmapSource = BitmapToBitmapImage(bitmap);
     bitmap.Dispose(); return bitmapSource;
} /// <summary>
/// 将ImageSource转为Bitmap
/// </summary>
/// <param name="imageSource"></param>
/// <returns></returns>
public static System.Drawing.Bitmap ImageSourceToBitmap(ImageSource imageSource)
{
BitmapSource m = (BitmapSource)imageSource;
Bitmap bmp = new Bitmap(m.PixelWidth, m.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
System.Drawing.Imaging.BitmapData data = bmp.LockBits(new Rectangle(System.Drawing.Point.Empty, bmp.Size),
System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); m.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);
bmp.UnlockBits(data);
return bmp;
} /// <summary>
/// 将Bitmap转为BitmapImage
/// </summary>
/// <param name="bitmap"></param>
/// <returns></returns>
public static BitmapImage BitmapToBitmapImage(Bitmap bitmap)
{
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
stream.Position = ;
BitmapImage result = new BitmapImage();
result.BeginInit();
result.CacheOption = BitmapCacheOption.OnLoad;
result.StreamSource = stream;
result.EndInit();
result.Freeze();
return result;
}
}
}

2、添加几个字段变量

主程序MainWindow.xaml.cs中添加:

private BitmapSource _bmpSpace, _bmpMine, _bmpNum1_8, _bmpForeground, _bmpMineOrTimer, _bmpBomb;
private System.Drawing.Size _cellSize = new System.Drawing.Size(, );

3、获取图片资源

private void GetImageResource()
{
BitmapImage bmpSpace = (BitmapImage)TryFindResource("ImgSpace");
BitmapImage bmpMine = (BitmapImage)TryFindResource("ImgMine");
BitmapImage bmpNum1_8 = (BitmapImage)TryFindResource("ImgNum1_8");
BitmapImage bmpForeground = (BitmapImage)TryFindResource("ImgForeground");
BitmapImage bmpMineOrTimer = (BitmapImage)TryFindResource("ImgMineOrTimer");
BitmapImage bmpBomb = (BitmapImage)TryFindResource("ImgBomb"); _bmpSpace = ImageHelper.BitmapImageToBitmapSource(bmpSpace);
_bmpMine = ImageHelper.BitmapImageToBitmapSource(bmpMine);
_bmpNum1_8 = ImageHelper.BitmapImageToBitmapSource(bmpNum1_8);
_bmpForeground = ImageHelper.BitmapImageToBitmapSource(bmpForeground);
_bmpMineOrTimer = ImageHelper.BitmapImageToBitmapSource(bmpMineOrTimer);
_bmpBomb = ImageHelper.BitmapImageToBitmapSource(bmpBomb);
}

4、将界面中用到的时钟和地图数这两张图片设置到位。

private void SetTimerAndMineImage()
{
ImgClock.Source = ImageHelper.CutImage(_bmpMineOrTimer, new Int32Rect(, , _cellSize.Width, _cellSize.Height));
ImgMineNum.Source = ImageHelper.CutImage(_bmpMineOrTimer, new Int32Rect( * _cellSize.Width, , _cellSize.Width, _cellSize.Height));
}

5、将上述两个方法添加到构造方法中。

运行程序,如图1。

三、设置状态枚举

添加一个MyEnum.cs类文件,让主cs文件看起来简练一点。内容如下:

// 游戏级别
public enum Level
{
SIMPLE,
NORMAL,
HARD
}; // 前景状态
public enum ForeState
{
NONE, // 无
NORMAL, // 正常覆盖
FLAG, // 红旗
QUESTION // 问号
}; // 底层状态
public enum BackState
{
MINE = -, // 地雷
BLANK = , // 空
}; // 游戏状态
public enum GameState
{
NONE, // 未开始
STOP, // 已停止
START, // 已开始
PAUSE // 已暂停
};

四、添加GameLevel类,定义游戏级别相关参数

public class GameLevel
{
public int _mineNum { get; set; } // 地雷数
public int _rowGrid { get; set; } // 横格子数
public int _colGrid { get; set; } // 纵格子数 public GameLevel(int currLevel, int mineNum, int rowGrid, int colGrid)
{
_mineNum = mineNum;
_rowGrid = rowGrid;
_colGrid = colGrid;
}
}

五、定义主游戏数据变量

private int[,] _backData = null;            // 底部背景数据(雷-1,0为空白,数值(1-8)周围雷数)
private int[,] _foreData = null; // 前景数据(1:正常盖住;2:红旗;3:问号)
private Image[,] _backImage = null; // 底图图片数组
private Image[,] _foreImage = null; // 上方图片数组 private GameState _gameState = GameState.NONE; private Random rnd = new Random(); // 随机数 private GameLevel _gameLevel;
private Level _level = Level.Simple;

另外还用到几个计时器,看后面的注释

// 计时
private System.Diagnostics.Stopwatch _stopWatchGame = new System.Diagnostics.Stopwatch(); // 游戏过程读秒
private DispatcherTimer _timerSetTimeText = new DispatcherTimer(); // 更新读秒文本框
private DispatcherTimer _timerWinAnim = new DispatcherTimer(); // 用于胜利时动画
private DispatcherTimer _timerBomb = new DispatcherTimer(); // 用于踩雷动画

将计时器初始化放在构造方法中

// 计时器
_timerSetTimeText.Interval = new TimeSpan(, , );
_timerSetTimeText.Tick += _timerSetTimeText_Tick; // 动画计时器
_timerWinAnim.Interval = new TimeSpan(, , , , );
_timerWinAnim.Tick += _timerWinAnim_Tick; // 用户踩雷后的动画
_timerBomb.Interval = new TimeSpan(, , , , );
_timerBomb.Tick += _timerBomb_Tick;

六、初始化游戏状态

private void InitialGameState()
{
_timerSetTimeText.Stop();
_timerWinAnim.Stop(); _stopWatchGame.Reset();
_stopWatchGame.Stop();
_gameState = GameState.NONE; MenuGamePauseOrContinue.Header = "暂停(_P)"; } private void _timerSetTimeText_Tick(object sender, EventArgs e)
{
textBlockTime.Text = ((int)_stopWatchGame.Elapsed.TotalSeconds).ToString();
} private void _timerWinAnim_Tick(object sender, EventArgs e)
{
} private void _timerBomb_Tick(object sender, EventArgs e)
{
}

后面动画计时事件方法以后再加入,这里先空着。

七、初始化游戏主数据

private void InitGameData(Level level)
{
switch (level)
{
case Level.SIMPLE:
_gameLevel = new GameLevel(, , );
break; case Level.NORMAL:
_gameLevel = new GameLevel(, , );
break; case Level.HARD:
_gameLevel = new GameLevel(, , );
break;
} // 设置窗口大小
this.Width = _cellSize.Width * _gameLevel._rowGrid + ;
this.Height = _cellSize.Height * _gameLevel._colGrid + + ; // 获取屏幕大小
double screenWidth = SystemParameters.WorkArea.Width;
double screenHeight = SystemParameters.WorkArea.Height; // 使窗口居中
this.Left = (screenWidth - this.Width) / ;
this.Top = (screenHeight - this.Height) / ; // 设置绘图控件大小
BackCanvas.Width = _gameLevel._colGrid * _cellSize.Width;
BackCanvas.Height = _gameLevel._rowGrid * _cellSize.Height;
ForeCanvas.Width = BackCanvas.Width;
ForeCanvas.Height = BackCanvas.Height; // 初始化前景和背景数据
_backData = new int[_gameLevel._colGrid, _gameLevel._rowGrid];
_foreData = new int[_gameLevel._colGrid, _gameLevel._rowGrid]; for (int y = ; y<_gameLevel._colGrid; y++)
{
for (int x=; x<_gameLevel._rowGrid; x++)
{
_backData[y, x] = (int)BackState.BLANK;
_foreData[y, x] = (int)ForeState.NORMAL;
}
} // 初始化前景和背景图片数组
_backImage = new Image[_gameLevel._colGrid, _gameLevel._rowGrid];
_foreImage = new Image[_gameLevel._colGrid, _gameLevel._rowGrid]; // 清理绘制区
BackCanvas.Children.Clear();
ForeCanvas.Children.Clear();
}

练手WPF(三)——扫雷小游戏的简易实现(上)的更多相关文章

  1. WEBGL学习笔记(七):实践练手1-飞行类小游戏之游戏控制

    接上一节,游戏控制首先要解决的就是碰撞检测了 这里用到了学习笔记(三)射线检测的内容了 以鸟为射线原点,向前.上.下分别发射3个射线,射线的长度较短大概为10~30. 根据上一节场景的建设,我把y轴设 ...

  2. 练手WPF(三)——扫雷小游戏的简易实现(下)

    十四.响应鼠标点击事件    (1)设置对应坐标位置为相应的前景状态 /// <summary> /// 设置单元格图样 /// </summary> /// <para ...

  3. 练手WPF(三)——扫雷小游戏的简易实现(中)

    八.随机布雷 /// <summary> /// 随机布地雷 /// </summary> /// <param name="mineNum"> ...

  4. Angular4 扫雷小游戏

    扫雷小游戏,可以升级过关,难度随关卡增加.但是有很明显的bug,以后有时间会继续优化! HTML: <div class="mainContent"> <div ...

  5. 扫雷小游戏PyQt5开发【附源代码】

    也没啥可介绍哒,扫雷大家都玩过. 雷的分布算法也很简单,就是在雷地图(map:二维数组)中,随机放雷,然后这个雷的8个方位(上下左右.四个对角)的数字(非雷的标记.加一后不为雷的标记)都加一. 如何判 ...

  6. 练手WPF(二)——2048游戏的简易实现(上)

    1.创建游戏界面编辑MainWindow.xaml,修改代码如下: <Window.Resources> <Style TargetType="Label"> ...

  7. web版扫雷小游戏(一)

    作为一名程序猿,平时的爱好也不多,说起游戏,我不太喜欢大型的网游,因为太耗时间,偶尔玩玩经典的单机小游戏,比如windows下自带的游戏扫雷(秀一下,高级下最高纪录110s). 现阶段正在致力于web ...

  8. 练手WPF(一)——模拟时钟与数字时钟的制作(上)

    一.Visual Studio创建一个WPF项目. 简单调整一下MainWindow.xaml文件.主要使用了两个Canvas控件,分别用于显示模拟和数字时钟,命名为AnalogCanvas.digi ...

  9. C++扫雷小游戏(基于CMD命令行)

    这个小游戏是笔者在大一C语言课程设计的时候写的,基于命令行,为了显得漂亮一些,特别加上了彩色特效~~~ 注意:Win10系统须将命令行调为旧版命令行,否则有可能会显示乱码! 代码示例: #includ ...

随机推荐

  1. Python关于去除字符串中空格的方法

    Python关于去除字符串中空格的方法 在编写程序时我们经常会遇到需要将字符串中的空格去掉的情况,通常我们可以使用下面几种解决方法: 1.strip()方法:该方法只能把字符串头和尾的空格去掉,但是不 ...

  2. core-js@3带来的惊喜

    core-js 这个名词肯定很多人没听过,今天也是在配置babelpolyfill方法发现的 起因 在使用useBuiltIns:usage按需加载polyfill时,npm run build,就出 ...

  3. Springboot整合Mybatis实现级联一对多CRUD操作

    在关系型数据库中,随处可见表之间的连接,对级联的表进行增删改查也是程序员必备的基础技能.关于Spring Boot整合Mybatis在之前已经详细写过,不熟悉的可以回顾Spring Boot整合Myb ...

  4. ABP开发框架前后端开发系列---(14)基于Winform的ABP快速开发框架

    前面介绍了很多ABP系列的文章,一步一步的把我们日常开发中涉及到的Web API服务构建.登录日志和操作审计日志.字典管理模块.省份城市的信息维护.权限管理模块中的组织机构.用户.角色.权限.菜单等内 ...

  5. Elasticsearch 6.x版本全文检索学习之集群调优建议

    1.系统设置要到位,遵照官方建议设置所有的系统参数. https://www.elastic.co/guide/en/elasticsearch/reference/6.7/setup.html 部署 ...

  6. TypeScript 装饰器的执行原理

    装饰器本质上提供了对被装饰对象 Property​ Descriptor 的操作,在运行时被调用. 因为对于同一对象来说,可同时运用多个装饰器,然后装饰器中又可对被装饰对象进行任意的修改甚至是替换掉实 ...

  7. 【LeetCode】6. Z 字形变换

    题目 将一个给定字符串根据给定的行数,以从上往下.从左到右进行 Z 字形排列. 比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下: L   C   ...

  8. PlayJava Day020

    1.异常Exception补充: ①错误(Error)指的是致命性错误,一般无法处理 ②异常以类的形式封装 程序可以处理的异常对应的类是java.lang.Exception及其子类 运行时异常对应的 ...

  9. HTML5☞canvas

    <canvas>便签用于绘制图像,图表.不过,<canvas> 元素本身并没有绘制能力(它仅仅是图形的容器) - 您必须使用脚本JavaScript来完成实际的绘图任务.既然你 ...

  10. 用XHR简单封装一个axios

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...