我相信看到這個題目你會很驚訝什么,Excel居然能開發(fā)游戲沒錯,Excel的強弱取決于用戶強則強,弱則弱但本文并不是為了展示Excel使用中的巧思,主要是寫給準備學習編程但計算機基礎不多的人,或者對Excel感興趣的人
對于正在學習編程的人,尤其是從其他領域進入這個領域的人,興趣是最大的動力從事計算機編程這么久,感覺編程是一件很有意義的事情但是,我經(jīng)常聽到一些人,尤其是那些在校的學生,抱怨編程太枯燥,堅持不下去我覺得這群人一方面是方向不對,另一方面是在實際學習過程中對自己的成績沒有任何成就感,而后者往往占據(jù)了重要的原因
我認為對于編程初學者來說,選擇第一語言應該具備以下兩個特點:
1),盡可能簡單,盡可能少與底層硬件相關聯(lián)(如內(nèi)存管理等)。),調試方便,IDE接口簡單,
2)比較強大,可以開發(fā)各種插件工具,
目前業(yè)界常用的編程語言中,只有Python,Office for VBA和Java能夠同時滿足以上兩個特性不過Python雖然簡單強大,但是需要配置環(huán)境,安裝臃腫的IDE,無形中增加了初學者的學習成本,更別說Java了,剩下的就是VBA了VbA是visual basic語言的子集除了繼承了一些VB的功能,還特別封裝了一些軟件接口,使用起來很方便有人說VBA語法太隨意,這對初學者來說不是一件好事如果學了C,以后學C++就容易多了我不同意這種觀點對于前者,不同的人有不同的看法,但后者是無稽之談,因為C++是一門極其復雜的編程語言,它不僅繼承了C語言繁瑣的指針,還衍生出了多重繼承,類模板,智能指針等恐怖的編程范式所以,對于初學者來說,我不建議直接學習C++
你為什么選擇VBA語作為初學者的語言。因為除了滿足上述兩個特征之外,它還有一些其他的優(yōu)點,如:
1),簡單易用:不需要安裝開發(fā)工具,也不需要安裝環(huán)境和語言包,只要電腦里有office軟件即可。
2)應用廣泛:幾乎所有工程軟件和辦公軟件都支持用VBA進行二次開發(fā)比如財務人員發(fā)現(xiàn)Excel自己的公式有局限性,完全可以用VBA開發(fā)自己需要的控件,如果機械設計師學習VBA,可以開發(fā)一些自己需要的代碼塊,這將大大提高自己CAD的繪圖速度如果不了解VBA,很難想象Excel的重度用戶,尤其是財務人員的工作量有多可怕
3),調試簡單方便。
所以,這一次,我也選擇了VBA作為編寫演示的語言為了照顧更多的初學者,我盡可能的呈現(xiàn)了每一步的細節(jié)由于每個Excel版本都不一樣,我的電腦用的是2010版,所以我就用2010版來解釋其他版本相同,只是界面可能略有不同我相信只要你親手制作了這個游戲,你不僅會體會到Excel的強大,也會逐漸體會到編程的樂趣鑒于時間有限,內(nèi)容可能會有所疏漏希望大家指正
下面是正文:
先來看看游戲最終的大致效果圖:
我們先來思考一下俄羅斯方塊的總體架構:
1),初始化界面:創(chuàng)建廣場所需的地圖。
2)隨機生成俄羅斯方塊:俄羅斯方塊共有7種形式,每種形式由4幀組成,每個方塊對應一種顏色您可以創(chuàng)建一個數(shù)組來存儲每個正方形的坐標,然后使用另一個數(shù)組來存儲正方形的相應顏色
3),移動旋轉方塊:分左,右,下擦拭后重畫,產(chǎn)生移動旋轉的效果
4)如果沒有新的方塊產(chǎn)生,它們都以一定的速度下落一旦碰到障礙物,就不能掉下來,然后生成新的方塊
5),不斷掃描是否有行被填充如果是真的,這條線就刪了,頂?shù)袅嗣織l線有10個點
首先創(chuàng)建一個Excel文件,隨意命名。打開后,因為office默認隱藏了開發(fā)工具的狀態(tài)欄,所以我們需要選擇Excel選項gt,自定義功能區(qū)以調出,檢查并確認:
隨后,我們發(fā)現(xiàn)主界面有更多的開發(fā)工具選項:
然后,在Sheet1表格中,我們將A~K列的列寬調整為與行高大致相同,并讓它大致稱為正方形區(qū)域:
我們點擊Visual Basic菜單打開寫代碼的界面,我們插入一個代碼模塊來寫我們自己的代碼:
因為正方形有7個形狀,為了讓程序繪圖方便,我用一個三維數(shù)組來存儲所有形狀的坐標,每個形狀都有一個中心坐標,其他三個正方形的坐標都是根據(jù)中心坐標計算出來的,比如一個T型正方形:
如果中心的坐標為,剩下的三個坐標從右到左分別是(0,1),(—1,0),和(0,1)之所以把垂直方向作為X軸,是因為Excel坐標的固有屬性例如,單元格(1,2)表示單元格的第一行和第二列每個方塊的對象都有中心坐標,顏色,形狀等屬性
optixplicitdimysheetworksheetlimitcentercentowasinteger '正方形中心行DimcenterColasinteger '正方形中心列Dimcolorar ' 7種正方形DimShapeArr 7種正方形DimiColorIndexAsInteger '顏色索引dimmyblock (4,2) as integer '每個正方形的坐標數(shù)組將伴隨著正方形的移動而變化DimbIsObjectEndAsBoolean '此正方形是否降到最低點DimiScoreAsInteger '分數(shù)
考慮到每個方塊坐標不一樣,我用一個三維數(shù)組來存儲方塊坐標為了方便起見,我使用VBA自己接口的數(shù)組函數(shù)來給我的ShapeArr賦值
' Initialize by yaxi _ liurivatesubinitsetmysheet = sheets( " sheet 1 ")color = Array(3,4,5,6,7,8,9) shapearr = array (array (0,0),array (0,1),array (0,1),Array(0,—1),Array(—1),Array(0,0),Array(0,1),Array(0,—1),Array(—1,1),_Array(Array(0,0Range("B1:K20 "),Interior.Pattern=xlNoneBorders.LineStyle=xlNone邊框(xlEdgeBottom)重量=xlMedium邊框(xlEdgeRight)重量=xlMedium邊框(xlEdgeLeft)Weight=xlMediumEndWith '設置縱橫比mysheet.columns ( "a: l ")列寬= 2mesheet.rows ( "1: 30 ")
這時候我們初始化變量和函數(shù)的功能就基本實現(xiàn)了接下來,我們將編寫一個函數(shù)來生成一個新的正方形為了實現(xiàn)程序的模塊化和低耦合,我們把這個功能封裝成一個獨立的功能
由于繪圖函數(shù)DrawBlock需要根據(jù)傳遞的標記數(shù)組進行繪制,并且我們需要知道這個正方形的中心坐標在哪里以及對應的顏色,所以需要傳遞四個參數(shù),其中數(shù)組需要被尋址(ByRef)。代碼如下:
'畫一個正方形,by yaxi _ liurivatesubdrawblock integer,byvalicolasinteger)dimrowasininteger,colasinteger = 0 to 3 row = center _ row+block(I,0) col = center _ col+block (I,1) mysheet.cells (row,col)interior . colorindex = icolor ' color index my sheet . cells(row,col)borders . line style = XL continuous '帶大綱線NextEndSub
至此,繪圖功能已經(jīng)完成為了防止bug,我們需要測試它我們會定義一個入口函數(shù),Start,同時定義一個臨時的方形數(shù)組,調用DrawBlock進行測試
啟動功能代碼如下:
substartcalliniticertrow = 5 iCenterCol = 6 iColorIndex = 4 dimiasintegerfori = 0 to 3 MyBlock(I,0)= shape arr(iColorIndex)(I)(0)my block(I,1)= shape arr(iColorIndex)(I)(1)NextCallDrawBlock(iCenterRow,iCenterCol,my block,ColorArr(iColorIndex))EndSub
讓我們運行它,看看效果:
好的,檢測結果顯示完全沒有問題。
后期需要在表格頂部的固定位置隨機生成新的方塊,所以要把這個函數(shù)重新包裝成一個獨立的函數(shù)為了防止偽隨機數(shù)的產(chǎn)生,我們使用Timer作為當前種子,隨機生成0到6之間的數(shù)組,每個數(shù)組對應形狀數(shù)組和顏色數(shù)組的索引
'由yaxi _ liurivatesubgetblockrandomize(timer)dimasintegericolor index = int(7 * rnd)ice interrow = 2 icenter col = 6 fori = 0 to 3 myblock(I,0)= ShapeArr(iColorIndex)(I)(0)my block(I,1)= ShapeArr(iColorIndex)(I)(1)NextCallDrawBlock(ice interrow,iCenterCol,my block,colorrar(iColorIndex))EndSub
現(xiàn)在正方形生成了,我們要讓正方形左右移動,分成三個方向移動的方法是先擦除當前的方塊,然后按照指定的移動方向計算新的坐標,再按照新的坐標重新繪制,這樣就產(chǎn)生了移動的現(xiàn)象但是,在搬家之前,我們需要判斷自己是否可以搬家
首先我們需要寫函數(shù)CanMoveRotate來判斷它是否可以移動或者旋轉這個函數(shù)很簡單,就是我們可以通過移動或者旋轉后的坐標來判斷是否越界或者當前位置是否有其他顏色
'函數(shù)是否可以移動或旋轉,由yaxi _ liurivatefunctioncanmoverotateasinteger)作為boolean '此函數(shù)的參數(shù)是變換后的坐標 '首先判斷是否越界,DimRowAsInteger,colasintegercanmoverotate = true fori = 0 to 3 row = center _ row+block(I,0)Col=center_col+block(i,1)IfRowgt,20OrRowlt0OrColgt11或收集,2Then '出界可以移動rotate = false endifmyshet . cells(row,col)interior.patternltgt,XLONETHEN '只要有一種顏色,它就是blocking can move rotate = false endifnextendfunction
我們還需要一個函數(shù)EraseBlock來擦除當前的方塊,可以根據(jù)傳遞過來的坐標直接擦除。代碼如下:
' Erase block by yaxi _ liurivateseuberaselblocksetter)dimrowasinterer,colas interdimisinterfori = 0 to 3 row = center _ row+block(I,0) col = center _ col+block (i,1)MySheetCells(Row,Col). interior . pattern = xlNoneMySheet
我們來寫移動塊的函數(shù)MoveBlock我們規(guī)定參數(shù)direction代表方向,—1代表左,0代表下,1代表右注意,移動后需要保存當前坐標
'移動框by yaxi _ liurivatessubmoveblocksetter,byvalicolosetter,byvaldirectionassetter)dimrowasininteger,colas integer dimold _ row為整數(shù),old _ colasinteger '保存最早的中心坐標old _ row = Center _ row old _ col = center _ col '首先擦除caller seblock(Center _ row,Center _ col,block)'—1為左,1為右,0代表農(nóng)村selectcasedirectioncases =—1 Center _ col = Center _ col—1 case 然后畫ifcanmovestate (center _ row,center _ col,block)然后畫calldrawblock (center _ row,center _ col,block,icolor) 保存中心坐標ice interrow = center _ rowicentercol = center _ collsecalldrawblock(old _ row,old _ col,block,icolor) 保存中心坐標ice interrow = old _ rowicentercol = old _ colifdirection = 0 thenbibjectend = true endfendif '保存平方坐標fori = 0to3myblock (I,0) = block (I
移動塊實現(xiàn)后,我們就寫RotateBlock函數(shù),定義為逆時針旋轉就像移動函數(shù)一樣,方法是先擦除舊坐標,然后根據(jù)新坐標畫一個新方塊只是旋轉起來有點麻煩
如果一個向量逆時針旋轉90度后坐標為(—y,x),就不難計算了根據(jù)這個公式,寫出旋轉函數(shù)但需要注意的是,要提前判斷是否滿足輪換的條件
' Rotate block function by yaxi _ liurivatesubrotateBlockasinteger,byvalicorasinteger)dimiasinteger ' Erase block(center _ row,center _ col,block) dimtemparr (4,2)as integer ' save array for I = 0 to 3 temparr(I,0) = block (i,0)= block(I,1) next '重新分配旋轉后的坐標fori = 0to3block (i,0) =—temparr (i,0) icolor) '保存平方坐標Fori=0To3MyBlock(i,0)=block(i,0) myblock (I,1) = block (I,1)nextelsecalldrawblock(center _ row,center _ col,temparr,icolor) '保存平方坐標fori = 0to3 myblock (I,0) = temparr (I,0) myblock (I,1) 1)NextEndIf '保存中心坐標ice interrow = center _ rowice intercol = center _ colend sub。
這時,旋轉和移動函數(shù)已經(jīng)寫好了為了讓游戲對應鍵盤事件,我們需要在對應的工作表代碼層添加事件函數(shù)注意,這里我們需要調用Windows API我們規(guī)定鍵盤左鍵為方形向左移動對象,右鍵為方形向右移動對象(1),下鍵為方形向下移動對象(0),上鍵為方形旋轉對象()
'鍵盤事件代碼,byyaxi _ Liu # ifvba 7 and win 64 thenrivateclareptsafeffunction getkeyboardstatelib " user 32 " AsLong # elsepropivatedclairefunction getkeyboardstatelib " user 32 " AsLong # EndIfPrivateSubWorksheet _ selection change(ByValTargetAsRange)dim key code(0到255)asbytegetkeyboardstatekey code(0)if key code(38)gt,127 then ' callrotateobjectelsifkeycode(39)gt,27然后 ' right callmoveobject(1)else if keycode(40)gt,127Then,callmoveobject(0)else if keycode(37)gt,127 then ' left CallMoveObject(—1)EndIfEndSub
因為我們自己定義的MoveBlock和RotateBlock包類對象的形參,所以不能在事件響應中直接調用這里我們將再次在類模塊中封裝兩個公共的MoveObject和RotateObject函數(shù),方便事件調用
' Move object by yaxi _ liuppublicsubmoveobjectcallmoveblock(icenterrow,icentercol,myblock,color (icon index),dir) end sub
'旋轉對象by yaxi _ liuppublicsubrotateobjectcallrotateblock(ice interrow,icentercol,myblock,color (icolor index)) endsub。
至此,平方功能已經(jīng)完全實現(xiàn)。我們隨機生成一個用于測試:
為了方便起見,我們將按鈕1中的文本改為開始游戲的四個詞:
然后,開始編寫程序自動運行的代碼伴隨著俄羅斯方塊的生成,它以一定的速度下降一旦碰到障礙物,俄羅斯方塊結束,然后生成新的俄羅斯方塊,以此類推因為VBA不支持定時器,我們使用while循環(huán)方法來連續(xù)生成方塊為了避免對CPU資源的過度占用,我們在循環(huán)之間增加了一個延遲函數(shù),用于循環(huán)調用
' Delay function by yaxi _ liurivatesubdelaydimt 1 assinglet1 = timerdodoeventslopwhiletimer—t1lt,TEndSub
在下降的過程中,我們需要知道某條線是否已滿判斷的方法很簡單,只需檢查整條線是否上色即可如果滿了,我們就刪除這一行,把第一行填到這一行同時更新分數(shù)
'消除整行函數(shù)by yaxi _ liurivatesubdeletefullrowdimiasinger,jasintegerfori = 1 to 20 forj = 2 to 11 if my shet . cells(I,j)interior.colorindexlt0 thenexitforelseifj = 11 thenmysheet范圍(單元格(1,2),單元格(i—1,j))CutDestination:=MySheetRange(Cells(2,2),Cells(i,j)) ' Range( " B2:K18 ")is core = is core+10 endifnextjnextimyshet . Range( " n1 ")value = " fraction " my shet . range( " O1 ")
在Start函數(shù)中添加while循環(huán),上述兩個函數(shù)添加的代碼相同,如下所示:
'啟動函數(shù)by yaxi _ liussubstartcalnitwhile(true)callgetblocksobjectend = false '此方塊的對象是否結束while(bisobjectend = false)call delay(0.5)callmoveblock(icenterrow,iCenterCol,MyBlock,colorrar(iColorIndex),0)MySheetrange( " L21 "). selectwithmysheet . range( " B1:K20 ")邊框(xlEdgeBottom)重量=xlMedium邊框(xlEdgeRight)重量=xlMedium邊框(xlEdgeLeft)
至此,這個游戲的編寫就算完全完成了,點擊Sheet1界面上的按鈕1按鈕開始游戲讓我們再試一次左鍵代表左,右鍵代表右,上鍵代表旋轉,下鍵代表下降
哈哈,試玩結束沒問題,很完美雖然過程漫長,但值得你認真學習,希望你能從中體會到編程的樂趣如果你認為你學到了知識,我希望你能把這篇文章分享給更多的人
。鄭重聲明:此文內(nèi)容為本網(wǎng)站轉載企業(yè)宣傳資訊,目的在于傳播更多信息,與本站立場無關。僅供讀者參考,并請自行核實相關內(nèi)容。