一、创建项目
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. JavPlayer:AI破坏马赛克,大量马赛克破坏版影片流出

    这是最近几个月业界讨论比较火的话题,发酵到现在, 终于可以给大家总结下最近的马赛克破坏版影片到底是怎么回事? 马赛克破坏版,简单讲就是利用AI技术,在打有马赛克影片的马赛克基础上进行修复操作, 来实现 ...

  2. MySql入门知识(一)

    概述 MySQL是一个真正多用户,多线程结构化查询语言数据库服务器.MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司.MySQL的SQL语言是用于访问数据 ...

  3. 操作系统篇之Linux命令操作和redis安装以及基本使用

    电脑操作系统 : windows7,8,10,xp,win98 操作系统 : linux ax unix 以后开发项目是部署在服务器上,服务器一般采用linux. linux的优点:系统稳定,操作速度 ...

  4. NCCL(Nvidia Collective multi-GPU Communication Library) Nvidia英伟达的Multi-GPU多卡通信框架NCCL 学习;PCIe 速率调研;

    为了了解,上来先看几篇中文博客进行简单了解: 如何理解Nvidia英伟达的Multi-GPU多卡通信框架NCCL?(较为优秀的文章) 使用NCCL进行NVIDIA GPU卡之间的通信(GPU卡通信模式 ...

  5. CodeForces - 1230D(思维+位运算)

    题意 https://vjudge.net/problem/CodeForces-1230D 要组建一个小组,要求小组中每个人都不比所有人强,当一个人懂得一个算法但是另一个不懂那么前者认为他比后者强. ...

  6. 数据库TINYINT类型 参数0 mybatis取不到值

    tinyint存储0的奇怪问题  数据库TINYINT类型   参数0  mybatis取不到值 postman 传参 audited   =0          audited   =1  两种情况 ...

  7. Vue全局过滤器的使用 运用在时间过滤 内容添加crud

    过滤器的使用 msgFormt是你自己定义的过滤器方法, Vue.filter是你自己定义的全局过滤器.没有s 过滤器要有返回值哈 用retuen     Vue.filter("msgFo ...

  8. 201871010123-吴丽丽 《面向对象程序设计(Java)》第十六周学习总结

    201871010123-吴丽丽 <面向对象程序设计(Java)>第十六周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ ...

  9. 验证登录的前世今生:session、cookie

    参考地址:彻底理解cookie,session,token 使用JSON Web Token设计单点登录系统 1.很久很久以前,Web 基本上就是文档的浏览而已, 既然是浏览,作为服务器, 不需要记录 ...

  10. 解决“QGtkStyle could not resolve GTK……”问题

    如果出现错误 QGtkStyle could not resolve GTK. Make sure you have installed the proper libraries 或者出现错误 err ...