這篇文章主要介紹了詳解從零開始---用C#製作掃雷遊戲,非常具有實用價值,需要的朋友可以參考下
學C#的原因其實挺簡單的,因為一直對遊戲挺感興趣,查了下比較流行的遊戲引擎Unity的主要開發語言是C#,所以就決定從C#入手,學學物件導向的程式設計方法。
以前基本上都做的是嵌入式開發,做嵌入式久了,基本上只用C語言,C語言面向過程的特性在嵌入式程式設計這種資源極度受限的情況確實十分有利,但這種方式在面對大型軟體的開發的時候就很難勝任了。程式設計的模式其實是一種思考習慣,習慣久了以後,想改變確實是一個艱難的過程···
說起C#,其實在大學的時候學過一個學期,說來慚愧那時候倒也沒把它當一門物件導向的語言(其實是當時根本不知道物件導向是啥),感覺跟C語言也就一點文法差異,把所有的用法全部歸為文法不同,說來也神奇,這種方法倒也能編程。最後學期結束的時候交上去一份用Winform開發的掃雷遊戲結束了我的C#學習,在那之後就再也沒碰過C#。
現在重拾C#,為了免除不必要的干擾,並沒有直接在Unity上學習,而是仍然在VS中學習,但這次選擇了比較新的WPF,而不是WInform,作為學習,第一個任務還是跟以前一樣做一個掃雷遊戲。
寫在不怎麼前面的前面:本文主要分享下程序分析過程,具體的實現方法不是本文重點,對實現有問題的朋友可以自行評論區留言索要源碼或者提問^_^。
一、分析
1.遊戲分析
那進入正題,該如何完成這個遊戲。忽略細枝末節的部分(如計時,顯示剩餘雷數,選單列等)不說,就單說這個遊戲的主體:掃雷區。
在遊戲沒開始的時候,掃雷區放眼望去其實只有一個東西,那就是方塊...
忽略光影效果不談(是的,我又忽略了···),所有方塊的顏色都一樣,都回應相同的事件,那就是左鍵和右鍵。左鍵點開方塊,右鍵給方塊做個標記,認定為地雷。再繼續分析,方塊有不同的種類。有的方塊點開之後周圍會有一大片方塊一起打開。有的方塊下面是地雷,點開就GameOver。還有方塊下面是數字,代表周圍有多少個地雷。 (果然,我又忽略了滑鼠兩個按鍵同時按自動開啟周圍格子和第二次右鍵可以顯示問號的功能···但其實之後會發現這個功能其實要增加也會很簡單)。
所以,先來總結下掃雷遊戲實現的核心:
#方塊會回應滑鼠事件(左鍵按下,左鍵單擊,右鍵按下,滑鼠移入,滑鼠移出)。
方塊被點開後的效果有三種(炸彈,數字,空),其中為空的時候會自動展開周圍所有的方塊。
方塊只能被開啟一次,之後不再回應按鍵事件。
當插旗的方塊數和地雷數相等,並且每個包含地雷的方塊都被插了旗,則遊戲勝利。
當包含地雷的方塊被打開,則遊戲失敗。
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
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中做游戏啦 哈哈。
以上是如何使用C#來製作掃雷遊戲的圖文代碼教程的詳細內容。更多資訊請關注PHP中文網其他相關文章!