当前位置: 移动技术网 > IT编程>开发语言>.net > 练手WPF(三)——扫雷小游戏的简易实现(上)

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

2019年05月30日  | 移动技术网IT编程  | 我要评论

千言万语快播,性感黑丝网袜,流着眼泪说再见

一、创建项目
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 = 0; 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(35, 35);

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(0, 0, _cellsize.width, _cellsize.height));
    imgminenum.source = imagehelper.cutimage(_bmpmineortimer, new int32rect(1 * _cellsize.width, 0, _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 = -1,      // 地雷
    blank = 0,      // 空
};

// 游戏状态
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(0, 0, 1);
_timersettimetext.tick += _timersettimetext_tick;

// 动画计时器
_timerwinanim.interval = new timespan(0, 0, 0, 0, 300);
_timerwinanim.tick += _timerwinanim_tick;

// 用户踩雷后的动画
_timerbomb.interval = new timespan(0, 0, 0, 0, 200);
_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(9, 9, 10);
            break;

        case level.normal:
            _gamelevel = new gamelevel(16, 16, 40);
            break;

        case level.hard:
            _gamelevel = new gamelevel(30, 16, 99);
            break;
    }

    // 设置窗口大小
    this.width = _cellsize.width * _gamelevel._rowgrid + 40;
    this.height = _cellsize.height * _gamelevel._colgrid + 20 + 100;

    // 获取屏幕大小
    double screenwidth = systemparameters.workarea.width;
    double screenheight = systemparameters.workarea.height;

    // 使窗口居中
    this.left = (screenwidth - this.width) / 2;
    this.top = (screenheight - this.height) / 2;

    // 设置绘图控件大小
    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 = 0; y<_gamelevel._colgrid; y++)
    {
        for (int x=0; 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();
}

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网