C++游戲開(kāi)發(fā),超簡(jiǎn)單的入門項(xiàng)目,50行代碼搞定開(kāi)心消消樂(lè)

一提到開(kāi)發(fā)游戲,很多人都會(huì)覺(jué)得要很高深的技術(shù),有一種望塵莫及的感覺(jué)。其實(shí)要編一款小游戲也沒(méi)有想象的那么難,下面跟著小編一起來(lái)看看C++是如何一步步制作游戲的。

本文的代碼下載地址在文章末尾,有需要的同學(xué)自取。

大家應(yīng)該都玩過(guò)連連看,游戲規(guī)則不多說(shuō)了,我們先看看設(shè)計(jì)思路。

第一件事要根據(jù)游戲玩法確定程序的數(shù)據(jù)結(jié)構(gòu),不同的數(shù)據(jù)結(jié)構(gòu)決定了不同的算法設(shè)計(jì),用錯(cuò)了數(shù)據(jù)結(jié)構(gòu)可能直接讓代碼復(fù)雜好幾倍。

連連看中玩家操作的數(shù)據(jù)是一個(gè)個(gè)的圖片,多個(gè)圖片組成一個(gè)棋盤式的矩陣界面。程序根據(jù)玩家的點(diǎn)擊位置計(jì)算兩個(gè)圖片的連接路線,這要求程序以最快、最簡(jiǎn)單的方式獲得矩陣中每個(gè)格子的數(shù)據(jù)。

【源碼獲取】:

由于平臺(tái)原因,小編將完整源碼放在小編自己的C語(yǔ)言/C++交流④群:836880131!資料共享在群文件里面,有需要的可以自行添加獲取~

如果對(duì)代碼有任何建議,都可以添加小編的學(xué)習(xí)交流群,歡迎大家來(lái)討論~

用二維數(shù)組表示游戲中的矩陣界面比較合適,因?yàn)閿?shù)組可以通過(guò)索引快速訪問(wèn)數(shù)據(jù),二維數(shù)組的兩個(gè)索引剛好對(duì)應(yīng)矩陣界面的(x, y)坐標(biāo)。例如:


image

圖中矩陣數(shù)據(jù)在代碼中應(yīng)該這樣表示:

data[1][2] = 0;
data[1][3] = 1;
data[2][2] = 2;

可以用不同的數(shù)字代表不同的圖片,比如:


image

那么上面的矩陣?yán)L制的時(shí)候就會(huì)是這樣:


image

0表示空格,不顯示圖片。初始化時(shí)的空格或消除后的空格,都會(huì)被設(shè)置為0。

// 定義數(shù)組
#define WIDTH 10
#define HEIGHT 8
int data[HEIGHT][WIDTH] = { 0 };

接下來(lái)要初始化數(shù)據(jù),我打算用8種不一樣的圖片,每種6張,隨機(jī)放入矩陣中,矩陣最外一圈是空的。這個(gè)算法可以用標(biāo)準(zhǔn)庫(kù)函數(shù) std::random_shuffle 來(lái)完成。

std::random_shuffle 用于把容器內(nèi)的數(shù)據(jù)隨機(jī)打亂,因此按順序把數(shù)據(jù)放入數(shù)組中,然后調(diào)用 std::random_shuffle 就可以完成初始化。

int tmpData[HEIGHT - 2][WIDTH - 2] = { 0 };   // 定義一個(gè)8 * 6的臨時(shí)數(shù)組用于存放48個(gè)圖片數(shù)據(jù)

int picNum = 1;
int curPicCount = 0;
for (int j = 0; j < 6; ++j)
{
    for (int i = 0; i < 8; ++i)
    {
        // 填入數(shù)據(jù)
        tmpData[j][i] = picNum;
        curPicCount++;

        if (curPicCount == picCount)
        {
            picNum++; // 圖片用完,換下一種圖片
            curPicCount = 0;
        }
    }
}

// 隨機(jī)數(shù)據(jù)
std::random_shuffle((int*)tmpData, (int*)tmpData + (HEIGHT - 2) * (WIDTH - 2));

// 再填入10*8數(shù)組中
for (size_t i = 1; i < WIDTH - 1; i++)
{
    for (size_t j = 1; j < HEIGHT - 1; j++)
    {
        data[j][i] = tmpData[j - 1][i - 1];
    }
}

斷點(diǎn)運(yùn)行觀察數(shù)組數(shù)據(jù),和我們?cè)O(shè)計(jì)的一樣:


image

貼上圖片看起來(lái)還不錯(cuò):


image

現(xiàn)在來(lái)分析游戲玩法。

這個(gè)游戲的難點(diǎn)是兩個(gè)圖片連接的判定算法,要求連接線只能轉(zhuǎn)折兩次。

我的第一反應(yīng)這是一個(gè)尋路算法,要求找到轉(zhuǎn)折兩次以下的最短路徑。教科書上常見(jiàn)的廣度優(yōu)先搜索、深度優(yōu)先搜索、DijKstra算法或是游戲中常用的A星算法,稍作修改加上兩次轉(zhuǎn)折的限制都能解決這個(gè)問(wèn)題。

但是如果我用這些比較復(fù)雜的算法來(lái)教新手,顯然是在勸退。所以還是考慮找一找連線判定的算法有沒(méi)有簡(jiǎn)單的規(guī)律。

多玩幾次游戲,把不同種類的連線記錄下來(lái),總結(jié)后可以發(fā)現(xiàn)總共有3種連線類型,分別是不轉(zhuǎn)折連接、轉(zhuǎn)折一次和轉(zhuǎn)折兩次。

還是從最簡(jiǎn)單的情況開(kāi)始考慮。這是解決難題的通用方法:從最簡(jiǎn)單的情況開(kāi)始考慮,再逐步增加復(fù)雜的條件。

最簡(jiǎn)單的不轉(zhuǎn)折連接,有兩種情況,橫向連線和縱向連線:


image

這兩種情況很容易處理,橫向、豎向依次檢查每個(gè)格子是否被阻擋即可。

// 橫向是否連接
bool IsHLinked(int x1, int y1, int x2, int y2)
{
    if (y1 != y2)
    {
        // 橫向不在一條線
        return false;
    }

    int minX = std::min(x1, x2); // 找到左邊的點(diǎn)
    int maxX = std::max(x1, x2); // 找到右邊的點(diǎn)

    for (size_t i = minX +1; i < maxX - 1; i++) // 從左到右檢查中間的點(diǎn)是不是空的
    {
        if (data[y1][i] != 0)
        {
            return false;
        }
    }

    return true;
}

// 縱向是否連接
bool IsVLinked(int x1, int y1, int x2, int y2)
{
// 代碼類似
}

最后把這兩個(gè)合并就是不轉(zhuǎn)折的情況下:

// 不轉(zhuǎn)折時(shí)判斷
bool IsZeroTurnLinked(int x1, int y1, int x2, int y2)
{
    if (IsHLinked(x1, y1, x2, y2))
    {
        return true;
    }

    if (IsVLinked(x1, y1, x2, y2))
    {
        return true;
    }

    return false;
}

轉(zhuǎn)折一次:


image

轉(zhuǎn)折一次的算法也是比較明顯的,像上圖中的兩種情況,找到綠色點(diǎn)的位置,如果這個(gè)點(diǎn)可以不轉(zhuǎn)折連到兩個(gè)紅色的圖片,那么這兩個(gè)紅色的圖片就可以通過(guò)一次轉(zhuǎn)折連接。

綠點(diǎn)的位置是由兩個(gè)紅點(diǎn)決定的,只有上圖中的兩種可能。

// 轉(zhuǎn)折一次
bool IsOneTurnLinked(int x1, int y1, int x2, int y2)
{
    int tmpPointX[2] = { x1, x2 };
    int tmpPointY[2] = { y2, y1 };// 找到兩個(gè)黃色點(diǎn)的坐標(biāo)

    for (size_t i = 0; i < _countof(tmpPointX); i++)
    {
        if (IsZeroTurnLinked(tmpPointX[i], tmpPointY[i], x1, y1)
            && IsZeroTurnLinked(tmpPointX[i], tmpPointY[i], x2, y2))
        {
            return true;
        }
    }

    return false;
}

轉(zhuǎn)折兩次的情況就多了,下圖同樣是連接紅色圖片,要繞過(guò)綠色圖片。


image

轉(zhuǎn)折兩次的情況很多,這里無(wú)法一一列舉,但是仔細(xì)思考可以發(fā)現(xiàn)和轉(zhuǎn)折一次本質(zhì)上是一樣的,就是找到兩個(gè)點(diǎn),這兩個(gè)點(diǎn)可以分別和紅色圖片無(wú)轉(zhuǎn)折連接,并且這兩個(gè)點(diǎn)也可以無(wú)轉(zhuǎn)折連接。

這兩個(gè)點(diǎn)需要位于經(jīng)過(guò)紅色圖片的十字線上,并且只要確定一個(gè)了其中一個(gè)點(diǎn),就能對(duì)應(yīng)地找到另一個(gè)點(diǎn):


image

因此只要遍歷其中一個(gè)圖片的兩條十字線經(jīng)過(guò)的所有的點(diǎn),并計(jì)算出另一個(gè)圖片十字線上對(duì)應(yīng)點(diǎn)的位置,檢查這兩個(gè)點(diǎn)和兩個(gè)紅色圖片是否可以無(wú)轉(zhuǎn)折連接:

bool IsTwoTurnLinked(int x1, int y1, int x2, int y2)
{
    // 順著圖1的延長(zhǎng)線縱向遍歷所有點(diǎn)
    for (size_t j = 0; j < HEIGHT; j++)
    {
        int tmpX1 = x1;
        int tmpY1 = j;

        if (j == y1)
        {
            continue; // 與圖1重合
        }

        if (tmpX1 == x2 && tmpY1 == y2)
        {
            continue; // 與圖2重合
        }

        int tmpX2 = x2;
        int tmpY2 = tmpY1; // 另一個(gè)點(diǎn)的坐標(biāo)

        if (IsZeroTurnLinked(tmpX1, tmpY1, tmpX2, tmpY2)
            && IsZeroTurnLinked(tmpX1, tmpY1, x1, y1)
            && IsZeroTurnLinked(tmpX1, tmpY1, x2, y2))
        {
            return true;
        }
    }

    // 順著圖1的延長(zhǎng)線橫向遍歷所有點(diǎn)
    // ... 省略

    return false;
}

代碼碼完了,添上圖片和鼠標(biāo)檢測(cè)的代碼跑一跑試試。


image

右邊顯示臨時(shí)顯示字符用于測(cè)試連通性,多次測(cè)試沒(méi)有問(wèn)題。

最后加上連線,并清除被點(diǎn)擊的兩個(gè)格子,就完成了連連看的核心邏輯:


image

文明看帖,點(diǎn)贊+關(guān)注+收藏一波走起~

代碼獲取方式

由于平臺(tái)不能放文件小編將源碼放在C語(yǔ)言/C++學(xué)習(xí)交流群:836880131!群里還整理了一些個(gè)人覺(jué)得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容