前一篇介紹的九宮格智慧盤遊戲,程式分成兩個部份,一個是模組裡的部份 ,一個是 Userform 的部份。模組裡的是計時的 Timer 程式碼,它不是這個遊戲的重點 (不計時也可以),在此暫且略過。我想介紹的是遊戲視窗 (也就是 Userform) 裡那些方塊按鈕的原理。
製作主要的元件
開始設計的時候,首先在Excel VBE 編輯環境裡新增一個表單, 也就是 Userform,然後在這個 Userform 上做一個正方形的 CommandButton 按鈕,它的控制項名稱 (Name屬性) 為 "CommandButton1"。把 CommandButton1 的 "Font" 屬性改成粗體而且較大的字型,然後把個這按鈕複製出另外 8 個,所以就有 CommandButton1~CommandButton9 總共 9 個按鈕。把 CommandButton1 的 "Caption" 屬性,也就是按鈕上顯示的文字,改成 "1"
、CommandButton1 的 "Caption" 屬性改成 "2"......依此類推,惟有 CommandButton9 的 "Caption" 屬性改成空的(什麼都沒有)。
把這 9 個按鈕依上圖的順序排成所謂的「九宮格」,於是我們就把智慧盤的九個方塊做好了。讀到後面你就會知道,這 CommandButton1~CommandButton9 的排列位置非常重要。最後,再增加一個 CommandButton10做為 "開始/重玩" 鈕。
怎麼改變方塊按鈕的屬性?
接下來,就從 UserForm 被 show 出來的時候講起。在 UserForm_Initialize() 裡面要做的初始化工作包括:把計時與計次等等的變數歸零、用 Inputbox 請玩家輸入姓名,還有就是使那 9 個方塊按鈕無效 (呈現淡色的狀態)。
"使按鈕無效" 就是要把它的 "Enabled" 屬性設為 "False",當遊戲開始的時候,要 "使按鈕有效" 就是要把它的 "Enabled" 屬性設為 "True"。因為程式裡會重複用到,所以我寫成兩個副程式:
Sub DisableBlocks() '使那 9 個方塊按鈕無效
Dim i As Integer
For i = 1 To 9
Controls("CommandButton" & i).Enabled = False
Next i
End Sub
Sub EnableBlocks() '使那 9 個方塊按鈕有效
Dim i As Integer
For i = 1 To 9
Controls("CommandButton" & i).Enabled = True
Next i
End Sub
在兩個副程式裡,你可以看到「Controls(控制項名稱).屬性」這樣的寫法來改按鈕的屬性,這裡也示範了利用方塊按鈕名稱的特徵,以 for......next 迴圈一次解決 9 個按鈕的屬性更改。
這 9 個按鈕的名稱特徵,就是最右邊依序是 1~9 的數字,在這個遊戲的程式裡,這是非常非常重要的。我們可以在 VBE 裡,手動更改按鈕的名稱,也就是它的 "Name"屬性。事實上,可以把 "CommandButton" 統統改成 "c" 和 "x"、"Block"......etc. 都可以,只要有那個數字在,而且程式碼裡寫的和按鈕的名稱一致就可以。為了讓大家容易了解程式正在處理的控制項是按鈕,我刻意不改掉 "CommandButton" 。
怎麼把方塊的排列順序弄亂?
當玩家按下 "開始/重玩" 鈕的時候,CommandButton10_Click() 裡會做底下這幾件事:
Private Sub CommandButton10_Click() '開始/重玩 鈕
Call ResetGame '重設比賽
StartGame = True
Call EnableBlocks '使那 9 個方塊按鈕有效
Call StartTimer '開始計時
End Sub
"重設比賽" 除了把移動次數和使用時間歸零之外,最重要工作是,要把方塊的排列順序弄亂。「把方塊的排列順序弄亂」其實並沒有移動那九個按鈕,而是更改它們上面顯示的數字,也就是更改它們的 "Caption" 屬性。
在 ResetGame() 裡,For ...... Next 迴圈會執行 9 次,依次會決定一個按鈕上要顯示的數字。For ...... Next 迴圈裡要設給按鈕上的數字,是以 Rnd() 產生亂數運算出 0~8 的整數,為了避免重複,把已過用的數儲存到陣列裡,用來核對。
Sub ResetGame() '重設比賽
Dim RndNum As Integer, a(9) As Integer, i As Integer
Dim NewNum As Boolean
Randomize '亂數產生器初始化
For i = 1 To 9
Do
RndNum = Int(Rnd() * 9) '產生 0~8 的亂數
NewNum = True
If i > 1 Then '檢查 a 陣列裡是否已有此整數
For j = 1 To i - 1
If a(j) = RndNum Then NewNum = False
Next j
End If
Loop While Not NewNum '重覆此迴圈直到產生的整數是 a 陣列裡沒有的
a(i) = RndNum '把這個整數加入 a 陣列
If RndNum = 0 Then
Controls("CommandButton" & i).Caption = "" '0 號是空白方塊, 上面不要字
Else
Controls("CommandButton" & i).Caption = Str(a(i)) '把這個整數 show 在第 i 個按鈕
End If
Next i
Moves = 0 '移動次數歸零
LapseTime = 0 '使用時間歸零
Label2.Caption = CStr(Moves)
Label4.Caption = CStr(LapseTime)
End Sub
所以,ResetGame() 被呼叫之後,玩家看到的情況可能會是這樣:CommandButton1 上面顯示的是 "7"、CommandButton2 上面顯示的是 "4"、CommandButton3 上面顯示的是空白......等等,其實每個 CommandButton 都還在它的原位上沒動。
接下來,玩家就會用滑鼠指標去點他想移動的方塊。
移動?......對啦!現在你知道了吧!所謂的「移動」也只是「改
上面顯示的數字」而已!我們下回分解。
- 3樓. VBA2017/06/10 13:00
能否將這個遊戲的程式碼寄給我呢
照著您的程式碼作有些地方無法執行耶
可以麻煩您將程式寄給我嗎B10321034@yuntech.edu.tw
感信您了
(B10321034@yuntech.edu.tw) - 2樓. 路人2011/05/17 20:05關於重設盤面數字的邏輯
如果不是真的在背景模擬每一塊拼圖的移動,而只是隨機將數字印到每一個拼圖上,是否有可能會產生無解的拼圖呢?嗯,"solution 是不是一定存在? " 很有趣的問題!
我自己用經驗玩出一套方法 "似乎" 可以解所有的情況,
只是不見得是最佳 (移動次數最少的)。
很久沒玩,那套方法現在已記不清楚了!
ThisIsTheWay 於 2011/06/21 11:54回覆 - 1樓. Diane2009/03/20 12:39圖形和數字有不同嗎?
我之前都是玩5x5的,所以看到9格的就直覺以為是數讀,真是糟糕^^
如果推推遊戲是由圖形構成,對寫程式的人而言,會比較難嗎?破解的方法也是和數字一樣,一定要依序(1~24)移動到正確位置,才會是最快的解法嗎?
這裡可以連結到圖形的推推遊戲,http://slidingpuzzle.net/default.aspx?Go=H,右邊可以選擇難度
那個網站的圖形推推遊戲是依特定順序把圖形弄亂的, 所以依相反順序推回原狀就是最佳解法
我寫的數字推推樂是用亂數決定數字的位置, 所以不知最佳解是什麼!!
那個圖形的遊戲, 它的程式是真的移動圖形, 所以程式比我的複雜很多......N倍吧!
玩起來的困難度也較高, 我弄了一個黃小鴨的圖形的, 你玩玩看, 很難喔......
(因為有一塊是白的 )
ThisIsTheWay 於 2009/03/21 14:27回覆