大一假期幾天的實訓認識課,用 C++ 做了兩個小遊戲,一個是推箱子,一個是貪喫蛇,讓我對編程實現二維遊戲有了點基礎認識,下面是貪喫蛇遊戲的一點簡要認識。
運行環境:vs2012
用到結構體,枚舉enum, if條件語句等
首先,我們要用到地圖Map當然指的是遊戲地圖,我們使用0,1來構造,1爲牆(障礙物),0爲可通過空間,大小隨心,如圖:
當然,0,1也用於後面的牆的圖案定義以及蛇的撞牆判定,這裏方塊牆(也可以其它圖案)我們使用搜狗輸入法裏的符號大全:
enum用來定義幾個選項,如遊戲菜單界面,遊戲控制區,遊戲輸贏判定,enum枚舉c++學習過程中大家都學過,例如:
enum enumType {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
枚舉量Monday、Tuesday等的值默認分別爲0-6,我們可以顯式的設置枚舉量的值:
enum enumType {Monday=1, Tuesday=2, Wednesday=3, Thursday=4, Friday=5, Saturday=6, Sunday=7};但指定的值必須是整數。
還有結構體struct:結構體可以將原本意義屬於一個整體的數據組合在一起。
唯一有一個新知識:按鍵控制
函數GetAsyncKeyState確定在調用它時某個按鍵處於彈起還是按下的,以及此按鍵是否在上一次調用GetAsyncKeyState之後(“又”)按下過(重複也算按下)。
這句話的完整意思是:預定義了一個KEYDOWN參數爲vk_code 他的定義的含義是判斷一個鍵是否被按下(GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0如果按下了就是1,沒有按下就是0
然後其它地方用的時候直接用KEYDOWN(vk_code)判斷這個鍵是否按下,相反彈起來是1按下是0。
清屏函數:system(“cls”);執行c++的一些比較大的程序時,可能會需要執行很多功能,但一次一次功能執行過後,會留下以前的字符佔據着屏幕,看起來特別亂,也影響接下來的功能調用,這個函數可以在每執行完一次功能,選擇回到一開始的初始界面。
作用是清除程序先前顯示的內容,但不會清除先前調用程序功能而實現的對內存的改變.而由於是清除先前顯示的內容,所以對此函數的寫入位置要考慮一下,避免連初始界面也給清空了。
若想調用此函數,在程序代碼的頭文件上,要加上#include <stdlib.h> 纔可以使用。
我們把它放在循環中,最合適不過,不斷把循環出的界面清屏,利用視覺暫留,造成動畫效果。
延時函數:Sleep(1);//延時函數,減慢蛇的速度,便於控制,我的代碼只有一關,所以直接自己隨意改時間,覺得若是好多關卡,可以寫一個函數,每關改變括號內的數值,改變遊戲難度。
要加上頭文件 #include<time.h>
最後就是不知爲何,筆記本運行會出現好多莫名其妙的點閃啊閃,在學校機房運行卻沒有。
運行部分截圖如下:
菜單界面:
遊戲開始界面:
遊戲中界面:這張截圖考手速
撞牆之後:
食物必須隨機出現,所以使用隨機函數生成食物座標,蛇頭控制,蛇的移動是核心,還有蛇的長度增加,蛇移動時身體要跟着蛇頭,所以我們總是要備份前一個點的位置,將其賦給後一點,實現蛇的移動,還有用判斷語句實現喫完食物蛇的長度加一。
接下來附上代碼(含註釋):
// GREED Snake.cpp : 定義控制檯應用程序的入口點。
//
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include<time.h>//蛇走的太快,利用此頭文件的函數讓他慢下來
using namespace std;
#define KEY_DOWN(vk_code) GetAsyncKeyState(vk_code)&0x8000?1:0
int g_arrMap1[15][20]={
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};
enum
{
E_MENU_START,
E_MENU_SETTING,
E_MENU_EXIT
};
enum
{
E_GAME_MENU,
E_GAME_MAP,
E_GAME_WIN,
E_GAME_LOSE
};
enum
{
E_DIR_NONE,
E_DIR_DOWN,
E_DIR_UP,
E_DIR_LEFT,
E_DIR_RIGHT
};
struct SData
{
SData()
{
nRow = 0;
nCol = 0;
nRowBk = 0;
nColBk = 0;
}
SData(int nInRow,int nInCol)
{
nRow = nInRow;
nCol = nInCol;
nRowBk = nInRow;
nColBk = nInCol;
}
void backUp()//備份函數
{
nRowBk = nRow;
nColBk = nCol;
}
void restore()//座標釋放函數
{
nRow = nRowBk;
nCol = nColBk;
}
int nRow;
int nCol;
int nRowBk;
int nColBk;
};
int _tmain(int argc, _TCHAR* argv[])
{ Sleep(1);//延時函數,減慢蛇的速度
int nMenuState = E_MENU_START;//菜單狀態
int nGameState = E_GAME_MENU;//遊戲狀態
//定義蛇:
SData arrSnake[15]={};
//蛇頭座標初始化
arrSnake[0] = SData(2,2);
//蛇長度初始化
int nlength = 1;
int foodnumber=1;
//定義蛇頭的移動方向,
int nDir = E_DIR_NONE;
//食物座標
SData sFood = SData( rand()%13+1, rand()%18+1) ; //食物隨機出現
while(true)
{
system("cls");
if(E_GAME_MENU == nGameState)
{
if(KEY_DOWN(VK_RETURN))//菜單數據更新按下enter鍵進入遊戲地圖
{
if(E_MENU_START == nMenuState)/*按下enter鍵時要判斷當前箭頭是否指示在遊戲開始的位置
判斷nMenuState的值是否是0,是則切換,不是則不管*/
{nGameState = E_GAME_MAP;}
else if(E_MENU_EXIT==nMenuState)//退出程序運行框
{
exit(0);
}
}
if(KEY_DOWN(VK_UP))
{
nMenuState--;
if(nMenuState<E_MENU_START)
{
nMenuState = E_MENU_EXIT;
}
}
else if(KEY_DOWN(VK_DOWN))
{
nMenuState++;
if(nMenuState > E_MENU_EXIT)
{
nMenuState = E_MENU_START;
}
}
//菜單渲染
if(E_MENU_START == nMenuState)
{
cout <<"->遊戲開始"<< endl;
cout <<" 遊戲設置"<< endl;
cout <<" 遊戲結束"<< endl;
}
else if(E_MENU_SETTING == nMenuState)
{
cout <<" 遊戲開始"<< endl;
cout <<"->遊戲設置"<< endl;
cout <<" 遊戲結束"<< endl;
}
else if(E_MENU_EXIT == nMenuState)
{
cout <<" 遊戲開始"<< endl;
cout <<" 遊戲設置"<< endl;
cout <<"->遊戲結束"<< endl;
}
}
//切地圖
else if(nGameState == E_GAME_MAP)
{
if(KEY_DOWN(VK_ESCAPE))//按下esc鍵返回菜單
{
nGameState = E_GAME_MENU;
}
//蛇頭的座標的備份
arrSnake[0].nRowBk = arrSnake[0].nRow;
arrSnake[0].nColBk = arrSnake[0].nCol;
//控制蛇頭的自動
if(KEY_DOWN(VK_UP))
{
nDir = E_DIR_UP;
}
if(KEY_DOWN(VK_DOWN))
{
nDir = E_DIR_DOWN;
}
if(KEY_DOWN(VK_LEFT))
{
nDir = E_DIR_LEFT;
}
if(KEY_DOWN(VK_RIGHT))
{
nDir = E_DIR_RIGHT;
}
if(nDir == E_DIR_UP)
{
arrSnake[0].nRow--;
}
if(nDir == E_DIR_DOWN)
{
arrSnake[0].nRow++;
}
if(nDir == E_DIR_LEFT)
{
arrSnake[0].nCol--;
}
if(nDir == E_DIR_RIGHT)
{
arrSnake[0].nCol++;
}
//喫食物
if(arrSnake[0].nRow == sFood.nRow && arrSnake[0].nCol == sFood.nCol)
{
nlength++;
sFood = SData(rand()%13+1 ,rand()%18+1);
//foodnumber++;
}
//身體跟隨蛇頭
for(int i = 1;i < nlength;i++)
{
//備份當前這一節座標
arrSnake[i].backUp();
//arrSnake[i].nRowBk = arrSnake[i].nRow;
//arrSnake[i].nColBk = arrSnake[i].nCol;
//把這一節的座標給下一節
arrSnake[i].nRow = arrSnake[i-1].nRowBk;
arrSnake[i].nCol = arrSnake[i-1].nColBk;
//在這一節座標中輸出
}
//遊戲勝負的判斷
//撞牆則失敗
if(1 == g_arrMap1[arrSnake[0].nRow][arrSnake[0].nCol] )
{
nGameState = E_GAME_LOSE;
}
for(int i=1;i<15;i++)
{
if(arrSnake[0].nRow == arrSnake[1].nRow && arrSnake[0].nCol == arrSnake[1].nCol)
{
nGameState = E_GAME_LOSE;
break;
}
}
//地圖渲染
for(int i=0;i<15;i++)
{
for(int j=0;j<20;j++)
{
bool bDrawSnake = false;
for(int m=0; m<15; m++)
{
if(i == arrSnake[m].nRow && j == arrSnake[m].nCol)
{
bDrawSnake = true;
break;
}
}
if(1 == g_arrMap1[i][j])
{
cout<<"■";
}
else if(bDrawSnake)
{
cout<<"□";
}
else if(i == sFood.nRow && j == sFood.nCol) //輸出食物的位置
{
cout<<"●";
}
else
{
cout<<" ";
}
}
cout<<endl;
}
}
else if ( nGameState == E_GAME_LOSE )
{
if(KEY_DOWN(VK_ESCAPE))
{
nGameState = E_GAME_MENU; //按向下鍵 返回遊戲菜單
}
cout<<" 很遺憾,你輸了這場遊戲!"<<endl;
cout<<"按Esc鍵退至菜單並進行新遊戲吧!";
}
}
return 0;
}
假期實訓認識之貪喫蛇。其實一開始不會蛇頭自動走時,是仿照推箱子寫的手動貪喫蛇,牽着蛇鼻子走,哈哈。
推箱子傳送:
推箱子C++實現