详解从零开始---用C#制作扫雷游戏

学C#的原因其实挺简单的,因为一直对游戏挺感兴趣,查了下比较流行的游戏引擎Unity的主要开发语言是C#,所以就决定从C#入手,学学面向对象的编程方法。

以前基本都做的是嵌入式开发,做嵌入式久了,基本上只用C语言,C语言面向过程的特性在嵌入式编程这种资源极度受限的情况确实十分有利,但这种方式在面对大型软件的开发的时候就很难胜任了。编程的模式其实是一种思维习惯,习惯久了以后,想改变确实是一个艰难的过程···

说起C#,其实在大学的时候学过一个学期,说来惭愧那时候倒也没把它当一门面向对象的语言(其实是当时根本不知道面向对象是啥),感觉跟C语言也就一点语法差异,把所有的用法全部归为语法不同,说来也神奇,这种方法倒也能编程。最终学期结束的时候交上去一份用Winform开发的扫雷游戏结束了我的C#学习,在那之后就再也没碰过C#。

现在重拾C#,为了免除掉不必要的干扰,并没有直接在Unity上学习,而是仍然在VS中学习,但这次选择了比较新的WPF,而不是WInform,作为学习,第一个任务还是跟以前一样做一个扫雷游戏。

写在不怎么前面的前面:本文主要分享下程序分析过程,具体的实现方法不是本文重点,对实现有问题的朋友可以自行评论区留言索要源码或者提问^_^。

一、分析

1.游戏分析

那进入正题,应该如何完成这个游戏。忽略细枝末节的部分(如计时,显示剩余雷数,菜单栏等)不说,就单说这个游戏的主体:扫雷区。

在游戏没开始的时候,扫雷区放眼望去其实只有一个东西,那就是方块...

忽略光影效果不谈(是的,我又忽略了···),所有方块的颜色都一样,都响应相同的事件,那就是左键和右键。左键点开方块,右键给方块做个标记,认定为地雷。再继续分析,方块具有不同的种类。有的方块点开之后周围会有一大片方块一起打开。有的方块下面是地雷,点开就GameOver。还有方块下面是数字,代表着周围有多少个地雷。(果然,我又忽略了鼠标两个键同时按自动打开周围格子和第二次右键可以显示问号的功能···但其实之后会发现这个功能其实要增加也会很简单)。

所以,先来总结下扫雷游戏实现的核心:

  1. 方块会响应鼠标事件(左键按下,左键单击,右键按下,鼠标移入,鼠标移出)。
  2. 方块被点开后的效果有三种(炸弹,数字,空),其中为空的时候会自动展开周围所有的方块。
  3. 方块只能被打开一次,之后不再响应按键事件。
  4. 当插旗的方块数和地雷数相等,并且每个包含地雷的方块都被插了旗,则游戏胜利。
  5. 当包含地雷的方块被打开,则游戏失败。

2.实现技术分析

经过分析,是不是发现扫雷的的玩法其实很简单,实现的技术也不难,全是静态的没有动画的存在。

方块的表现很像一个只能按一次的按钮(事实上,在大学的时候我就是直接继承的按钮控件)。

但这一次为了能使用到更多C#相关的东西我使用了更加麻烦的自定义控件的方式。

方块有三种表现形式,为特殊性,但很显然也具有共性,所以在设计的时候,我把按钮共性抽离出来,设计成了一个抽象的基类Cube。方块有三种类型,但因为我懒,所以把其中的两种(空白和数字)合并为了NumCube类,包含地雷的为BombCube类,这两个类分别继承了Cube。

Cube的实现:

Cube类中拥有以下字段:

ImageSource cubeNormalPic
ImageSource cubeOnPic
ImageSource cubeDownPic
ImageSource cubeDisablePic
ImageSource cubeFlagPic

这5个字段是用来设置Cube在各个状态所显示的图片的(普通,鼠标进入,左键按下,失能,标记)

Bool isEnable
Bool isFlag

这两个字段就是标记Cube是否被使能和Flag

Image cubeImageHigh
Image cubeImageLow

这2个是两个image控件,作用是用来显示图片,之所以要2个图片是因为旗子图片被设计为一个叠加在Cube上的图片。

下面再来重点讲下下面2个东西:

displayCube
mouseEvent

在设计中,这是两个接口,分别用来处理鼠标事件和方块的展开。不同于直接在内部直接实现接口,将两个接口设计为Cube属性是为了能动态的修改这两个接口的实现方式,不至于每次修改都需要对Cube内的代码进行修改,且可以实现每个不同的Cube都使用不同的代码而不需要使用重写,这种方式在设计模式中也叫“策略模式”。

Cube只拥有一个方法,那就是Open,但这个方法其实也是有display接口代理实现。

public void Open()
{
 if (displayCube != null)
 {
  displayCube.Open(this);
 }
}

displayCube.Open(this)之所以要把自身传入,是因为Open方法要用到Cube自己的参数和方法。

BombCube继承自Cube

只添加了一个字段:

ImageSource bombPic

用来存储地雷图片.

NumCube 继承自Cube

Int bombNum

用来记录方块周围有多少个BombCube,当其为0的时候,NumCube就是显示为空的方块。

添加了一个组件lable用来显示数字Text。

interface的实现

分别为每种Cube设计了一种接口的实现方式,使用这种方式,若后期需要改为动画显示,也只需要实现一个动画的接口,赋值给对应的Cube就可以了。

二、实现

控件继承:

Wpf进行控件继承的时候需要注意,被继承的控件不能有xaml。

在继承的时候,xaml中需要加入如下语句:

< myTypes:Cube x:Class="扫雷.UserControl.NumCube"

xmlns=" http:// schemas.microsoft.com/w infx/2006/xaml/presentation "

xmlns:x=" http:// schemas.microsoft.com/w infx/2006/xaml "

xmlns:mc=" http:// schemas.openxmlformats.org /markup-compatibility/2006 "

xmlns:d=" http:// schemas.microsoft.com/e xpression/blend/2008 "

mc:Ignorable="d"

xmlns:myTypes="clr-namespace:扫雷.UserControl"

d:DesignHeight="18" d:DesignWidth="18">

Cube 鼠标事件的实现:

鼠标事件主要是在各个事件中实现对Cube图片的变换,例如鼠标移出事件

public void MouseLeaveCube(object sender, MouseEventArgs e)
{
 BombCube bombCube = sender as BombCube;
 if (bombCube.IsEnable)
 {
  isClicking = false;
  bombCube.cubeImageLow.Source =
  bombCube.cubeNormalPic;
 }
}

关于地雷位置的生成算法实现:

游戏很重要的一个方面是,每次地雷的位置应该不同。很容易想到应该用随机数来产生地雷的位置。这就需要随机生成N个不相同的坐标。本程序的实现方法是创建一个list<int>,之后使用随机数在0-sizeX * sizeY - 1之间随机生成一个数,检查list中是否包含该数字,若不包含则添加进list,直到list拥有N个元素停止。

List<int> BombIndexList=new List<int>();

Random ran = new Random();

do

{
 int bombIndex = ran.Next(0,sizeX * sizeY - 1);
 if(!BombIndexList.Contains(bombIndex))
 {
  BombIndexList.Add(bombIndex);
 }
 else
 {
   continue;
 }
} while (BombIndexList.Count < BombNum);
IndexList = BombIndexList;

之后根据生成的list来确定坐标上应该是NumCube还是BombCube

for (int y = 0; y < sizeY; y++)
{
 for (int x = 0; x < sizeX;x++)
 {
  //cube属性设置
  if(bombIndexList.Exists((int temp) => temp == x + y * cubeX))
  {
   cubexMatrix[x, y] =bombCubeList[bombIndex++];
  }
  else
  {
   numCubeList[numIndex].Text ="";
   cubexMatrix[x, y] =numCubeList[numIndex++];
  }
  cubexMatrix[x, y].IsFlag =false;
  cubexMatrix[x, y].Margin =new Thickness(x * 18, y * 18, 0, 0);
  cubexMatrix[x, y].IsEnable = true;
  SetCubeBombNum(cubexMatrix,cubeX, cubeY);
  bombGrid.Children.Add(cubexMatrix[x, y]);
 }
}

如何让空白Cube打开以后会打开周围的Cube:

因为这种打开方式有点类似于递归,需要有传染性(即若打开的也是空白Cube,则其也应该打开周围的Cube),所以执行该事件的时候一定要具有周围Cube的信息(即能获取到周围的控件)。

获取周围的Cube的方法有两种:

1.保存Cube自身的位置,并获取所有Cube的位置

2.保存周围Cube的信息

我使用的是第二种方式,之前Cube类中的Cubelist就是用来保存周围Cube的信息的。通过CubeList找到周围Cube,并触发他们的左键单击事件。

public void MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
  NumCube numCube = sender as NumCube;
  if (numCube.IsEnable && numCube.IsFlag == false)
  {
    // 完成在控件上点击
    if (isClicking)
    {
      isClicking = false;
      numCube.IsEnable = false;
      if (numCube.BombNum != 0)
        numCube.Text = Convert.ToString(numCube.BombNum);
      else
      {
        foreach (Cube cubeTemp in numCube.CubeList)
        {
          MouseButtonEventArgs args = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left);
          args.RoutedEvent = Cube.MouseLeftButtonDownEvent;
          cubeTemp.RaiseEvent(args);
          args.RoutedEvent = Cube.MouseLeftButtonUpEvent;
          cubeTemp.RaiseEvent(args);
        }
      }
    }
  }
}

一些小技巧:

1.可以把一些图片的修改放在属性的set内,例如disable的图片。

public bool IsEnable
{
  get { return isEnable; }
  set
  {
    isEnable = value;
    if (isEnable)
    {
      if (cubeNormalPic != null)
        cubeImageLow.Source = cubeNormalPic;
    }
    else
    {
      if (cubeDisablePic != null)
        cubeImageLow.Source = cubeDisablePic;
    }
  }
}

2.Wpf创建控件较慢,为了提升(修改宽度长度或地雷数量之后)游戏开始速度,应该预先创建控件,并把控件放入list或者arr保存,按照需求取出。

到这扫雷游戏的制作就没什么难度技术上的难度的,只需要通过百度了解一些WPF常用的事件,控件,xalm相关的知识就能做出一个扫雷游戏啦。相关源码就不发在这了,需要的朋友可以评论中找我,这次游戏制作让我对面向对象的基本编程方法的了解有了一个很大的提升,下次应该就可以在Unity中做游戏啦 哈哈。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#带你玩扫雷(附源码)

    扫雷游戏,大家都应该玩过吧!其实规则也很简单,可是我们想自己实现一个扫雷,我们应该怎么做呢? Step1: 知晓游戏原理 扫雷就是要把所有非地雷的格子揭开即胜利:踩到地雷格子就算失败.游戏主区域由很多个方格组成.使用鼠标左键随机点击一个方格,方格即被打开并显示出方格中的数字:方格中数字则表示其周围的8个方格隐藏了几颗雷:如果点开的格子为空白格,即其周围有0颗雷,则其周围格子自动打开:如果其周围还有空白格,则会引发连锁反应:在你认为有雷的格子上,点击右键即可标记雷:如果一个已打开格子周围所有的雷已

  • 详解从零开始---用C#制作扫雷游戏

    学C#的原因其实挺简单的,因为一直对游戏挺感兴趣,查了下比较流行的游戏引擎Unity的主要开发语言是C#,所以就决定从C#入手,学学面向对象的编程方法. 以前基本都做的是嵌入式开发,做嵌入式久了,基本上只用C语言,C语言面向过程的特性在嵌入式编程这种资源极度受限的情况确实十分有利,但这种方式在面对大型软件的开发的时候就很难胜任了.编程的模式其实是一种思维习惯,习惯久了以后,想改变确实是一个艰难的过程··· 说起C#,其实在大学的时候学过一个学期,说来惭愧那时候倒也没把它当一门面向对象的语言(其实

  • 详解如何利用Python制作24点小游戏

    目录 先睹为快 游戏规则(改编自维基百科) 逐步实现 Step1:制作24点生成器 Step2:定义游戏精灵类 Step3:实现游戏主循环 先睹为快 24点 游戏规则(改编自维基百科) 从1~10这十个数字中随机抽取4个数字(可重复),对这四个数运用加.减.乘.除和括号进行运算得出24.每个数字都必须使用一次,但不能重复使用. 逐步实现 Step1:制作24点生成器 既然是24点小游戏,当然要先定义一个24点游戏生成器啦.主要思路就是随机生成4个有解的数字,且范围在1~10之间,代码实现如下:

  • 详解如何使用Pyecharts制作Map3D

    基本设置 class Map3D( # 初始化配置项,参考 `global_options.InitOpts` init_opts: opts.InitOpts = opts.InitOpts() ) def add( # 系列名称,用于 tooltip 的显示,legend 的图例筛选. series_name: str, # 数据项 (坐标点名称,坐标点值) data_pair: types.Sequence, # 叠加图的类型(目前只支持 Bar3D,Line3D,Lines3D,Scat

  • C语言制作扫雷游戏(图形库)

    本文实例为大家分享了C语言制作扫雷游戏的具体代码,供大家参考,具体内容如下 游戏预览: 学习内容: 1.图形库文件的使用2.C++的使用,如类函数3.了解扫雷的规则,严谨的思维逻辑 扫雷的规则: 玩家点击一个地方,如果该地方是雷,则游戏结束:如果是空格,则会显示周围空格和周围8个格子的雷的数量,玩家依照提示逐步挖掘全部的雷,从而结束游戏. 制作步骤: 基本的库函数和常量 #include<stdio.h> #include<stdlib.h> #include<time.h&

  • 详解Android用Shape制作单边框图的两种思路和坑

    开发中遇到单/多边框的UI,简单的可以自己写shape图,复杂的一般都让设计配合制作9patch图了. 今天不说需要切图的情况,只聊简单的单/多边框,主要是实现思路. 效果很简单: 就以上图为例介绍,只有上边框,边框红色.宽1dp,其余为白色. 思路一 两层画布叠加:底层红色:上层白色: 上层白色画布下移1dp. 代码实现: <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:andro

  • 详解在docker中制作自己的JDK+tomcat镜像

    也许你和我一样,想要自己亲手制作一个热乎乎的镜像,最好自己指定JDK版本和tomcat版本.当然,这是可以的. 根据我的水平,目前有两种办法可以制作我想要的这个镜像.来,我们先说简单点的. 方式一 首先,准备好想要的jdk和tomcat,另外,我们需要创建一个Dockerfile文件,什么,你说你不知道Dockerfile是什么也不会写Dockerfile文件?哦,那也没关系吧,你Ctrl+C就好了.下面展示一个Dockerfile文件的完整内容: FROM ubuntu:14.10 MAINT

  • 详解使用JS如何制作简单的ASCII图与单极图

    ASCII图 在终端执行各种命令的时候经常会看到一些终端里显示出来的"图片",远看仿佛一张图,近看则是一个个的 ASCII码,它们 大致长这样子 而今天我们要做的则是用JS把一张给定的图片转换成这种用ASCII字符组成的"ASCII图" 先看看最终效果,假设我们给定的图片是这样子的, 这是代码处理后的结果,用了 I'mYasic 这8个字符来表示,还是可以分辨出大致的轮廓的. 单级图 而另一种图则是单极图,也就是黑白图片,还是刚刚那张图片,输出如下 基础知识 这两种

  • 详解nodejs模板引擎制作

    关于模板,我倒是用过了不少.最开始要数Java的JSP了,然后接触了PHP的smarty,再就是Python的jinja2, Django内置模板,现在刚开始看Nodejs,也发现了不少类似的模板引擎,ejs, jade等等吧. 模板带来的最直接的好处就是加速开发,前后端分离.除此之外,对于字符串的格式化同样是个比较好的应用.习惯了python中 string = "hello {}".format("郭璞") # hello 郭璞 string = "h

  • 详解C语言实现猜数字游戏

    目录 一:猜数字游戏基本介绍&对程序预期 二:程序设计思路 1.总体思路: 2.菜单部分函数: 3.游戏部分函数 4.主函数部分 5.代码主体 三:总结 前言:本文主要讲解以c语言编写猜数字游戏,目的是介绍C语言中的循环和分支的具体用法. 一:猜数字游戏基本介绍&对程序预期 .猜数字游戏,顾名思义,就是系统随机给出一个数字,玩家对这个数字进行猜测,如果玩家猜测的数字大于给出的数字,则系统会提示猜大了,如果玩家猜测的数字小于给出的数字,则系统会提示猜小了,这样逐步缩小猜测范围,直到玩家猜对为

  • C语言实现扫雷游戏详解(附源码)

    目录 1.游戏的功能 2.游戏实现的基本思路 2.1实现菜单给玩家选择 2.2初始化棋盘 2.3数组大小的问题 2.4对棋盘赋值 2.5打印棋盘 2.6布置雷 2.7排查雷 3.代码基本实现部分 3.1主函数部分 3.2 初始化棋盘 3.3对两个棋盘进行赋值 3.4打印棋盘 3.5布置雷 3.6排查雷  3.7函数声明 4.扫雷游戏的源代码 总结 1.游戏的功能 游戏的主要功能有 1:棋盘内有若干个雷 2:玩家输入要排查雷的坐标 3:在玩家输入的坐标处显示周围八个坐标有几个雷 3:若玩家将所有的

随机推荐