使用C#和MonoGame開發俄羅斯方塊遊戲

2022-05-29 18:03:33

小的時候就看到有同學使用C語言在DOS下做過一款俄羅斯方塊的遊戲,當時是啟用了DOS的圖形化模式,感覺也挺有意思。最近上海疫情封控在家,週末也稍微有點空餘時間,於是使用Visual Studio 2019,C# 9.0配合MonoGame 3.8,自己也寫了一個俄羅斯方塊的遊戲,效果如下:

screenshot

當然,光看效果圖還是不夠直觀,最好是自己能夠下載玩一下。下載地址在此:

【點選此處下載遊戲】

與此同時,程式碼開源:https://github.com/daxnet/tetris-sharp

簡介

有些內容大致介紹一下:

  1. 開發我沒有使用Visual Studio 2022和.NET 6,而是使用的Visual Studio 2019配合.NET Core 3.1,這是因為MonoGame 3.8目前對VS 2019和.NET Core 3.1的支援最好,有現成的專案模板,以及Content Pipeline的支援也不錯。如果你希望自己編譯原始碼,最好選擇Visual Studio 2019和.NET Core 3.1;後續MonoGame會完成.NET 6的支援
  2. 所有圖形資源(除了圖示),都是我自己用Paint.NET畫的。Paint.NET工具挺好用
  3. 背景音樂是以前8位元任天堂紅白機(或者是小霸王遊戲機)中《俄羅斯方塊》二代裡的背景音樂,從網路下載,僅供學習交流使用
  4. 基本按鍵:
    1. 方塊左移:A
    2. 方塊右移:D
    3. 方塊下移:S
    4. 旋轉方塊:J
    5. 暫停:確認鍵
    6. 開/關背景音樂:F12

遊戲設計和開發要點

介紹幾個開發要點吧。

方塊的定義和方塊旋轉

為了能夠支援擴充套件,我使用了XML檔案來定義各種方塊,以及每種方塊在不同的旋轉角度下的形態。這個檔案是blocks.xml,跟可執行檔案在同一個目錄下。它的結構如下:

<?xml version="1.0" encoding="utf-8" ?>
<tetris-blocks>
    <blocks>
        <block>
            <name>方塊名稱</name>
            <description>方塊描述(可以不填)</description>
            <rotations>
                <rotation>
                    <definition>旋轉形態1的描述</definition>
                </rotation>
                <rotation>
                    <definition>旋轉形態2的描述</definition>
                </rotation>
                ...
            </rotations>
        </block>
        ...
    </blocks>
</tetris-blocks>

其中:

  • 方塊名稱:給方塊取個名字,隨便什麼名字都可以
  • 方塊描述:隨便給個描述文字,可以不填
  • 旋轉形態的描述:用來表述當前這個旋轉形態的樣子,待會再介紹

玩過俄羅斯方塊的都知道,一個方塊可以有多個旋轉形態,比如L方塊,可以有4個形態:

image

對於第一個形態,我們可以用一個3*2的整型陣列來表示:

image

於是,從上到下,從左到右,這個陣列裡的值就是:1、1、1、1、0、0;如果我們把每一行的數位連起來,然後行與行之間用空格隔開,那就是111 100,這也就是這個方塊在當前這個「廠」字形態下的定義,所以rotation definition就是111 100。完整的L方塊的定義如下:

<block>
    <name>Reversed L</name>
    <rotations>
        <rotation>
            <definition>001 111</definition>
        </rotation>
        <rotation>
            <definition>11 01 01</definition>
        </rotation>
        <rotation>
            <definition>111 100</definition>
        </rotation>
        <rotation>
            <definition>10 10 11</definition>
        </rotation>
    </rotations>
</block>

現在我們往XML里加入一個新的方塊:十字方塊:

image

<block>
    <name>Cross</name>
    <rotations>
        <rotation>
            <definition>010 111 010</definition>
        </rotation>
    </rotations>
</block>

加入十字方塊後,立刻會被載入到遊戲中:

image

遊戲棋盤與方塊

遊戲中有一塊看不見的棋盤,它主要記錄著目前遊戲中有哪些地方已經堆積了方塊,並對於完全塞滿的行,遊戲棋盤會負責清理(也就是消行)。根據棋盤的大小,可以將這個資料模型定義為一個整型的二維陣列,有方塊堆砌的格子,值為1,否則為0。

在方塊下落的時候,會首先判斷其所在行的下一行中,與之下邊緣對應的方格中有沒有值為1的格子,如果有的話,當前方塊就需要被融合到棋盤中,然後發出下一個方塊。如果沒有,那麼當前方塊繼續下落。

棋盤的融合其實很簡單,方法是:掃描方塊當前旋轉形態的所有格子,如果值為1,就在棋盤的對應位置將陣列值設定為1即可。後續由MonoGame的渲染機制負責渲染棋盤就可以了。

方塊的移動

方塊並不是隨意在介面上移動的,當方塊左右移動時,要看左右方是否已有棋盤中的堆砌塊阻擋,或者是否已經到了介面的邊緣,如果有,則要阻止其左右移動。判斷方法跟上面所說的判斷方塊下移過程是否與棋盤接觸的方法類似,就看左右兩邊的邊緣是否會與棋盤或者邊界接觸,如果是,則禁止移動。

物件導向與框架的思想

整個遊戲的實現程式碼中還包含了一個框架,位於TetrisSharp.Framework名稱空間,它封裝了一些簡單遊戲的常見元件,比如Sprite、Scene、Text、ProgressBar等,以及一些簡單的諸如碰撞檢測的演演算法和一個訊息系統(比如當碰撞發生時,Sprite可以通知Scene做一些事情)。這些東西在編寫小的休閒遊戲的時候還是可以重用的。我們不推薦重複造輪子,像Unity、UR等這樣成熟的遊戲引擎其實已經有這些強大功能了,不過有興趣有精力也可以考慮自己做個遊戲引擎,畢竟MonoGame它也只是一個框架,不是遊戲引擎。

總結

這個俄羅斯方塊遊戲的一些設計思路就簡單介紹這些吧,千言萬語都在原始碼裡,歡迎大家下載參考。