==========================================說明=====================================
1.用了寫遊戲的基本流程所以界面是持續刷新的(可能會改成遊戲)
2.迷宮生成算法是深度優先遍歷(DFS),爲了讓路線不那麼容易走通,是從終點向起點生成的。
PS1:迷宮難度不高,路線完全隨機似乎不是太好,有待改進。
PS2:至於EasyX,是個C語言可用的不錯的圖形庫,下載的話百度一下就有了。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <graphics.h>
//////////////////////////////以下是 宏定義參數 //////////////////////////////
#define WIN_WIDTH 1200 // 窗體寬度
#define WIN_HEIGHT 680 // 窗體高度
#define MAZE_WIDTH 15*34 // 迷宮寬度
#define MAZE_HEIGHT 15*17 // 迷宮高度
#define B_WIDTH 10 // 格子寬度
#define X 51 // 迷宮列數
#define Y 26 // 迷宮行數
#define N X*2+1 // 迷宮數組列數
#define M Y*2+1 // 迷宮數組行數
#define L (N-1)*(M-1) // 迷宮路徑長度
#define LX 80 // 起點座標X
#define LY 50 // 起點座標Y
#define C_BLOCK RGB(153,102,85) // 牆壁顏色
#define C_GRAY RGB(123,72,55) // 陰影顏色
#define C_GROUND RGB(50,50,50) // 地面顏色
#define C_ROOT RGB(170,170,210) // 路徑顏色
#define C_LINE YELLOW // 路徑線顏色
//////////////////////////////以下是 函數聲明 //////////////////////////////
float GetFPS(); // 計算畫面FPS(每秒幀數)
void Delay(DWORD ms); // 絕對延時
bool IsInRect(int x,int y,RECT r); // 是否在矩形內
void InitMaze(); // 初始化迷宮數組
void OutputMaze(); // 輸出迷宮
void CopyScreenBuffer(); // 複製屏幕到緩存
void DrawScreenBuffer(); // 繪製緩存到屏幕
void OutPutPath(); // 輸出路徑連線
void DrawFPS(); // 輸出幀數
int Move(int d,int x,int y); // 向指定方向移動,返回是否成功移動
void DFS_CreateMaze(int x,int y); // 深度優先生成迷宮
void FindPath(int i,int j); // 深度優先搜索迷宮路徑
void CreateMaze(); // 創建迷宮
void DrawMenuItems(); // 繪製菜單按鈕
//////////////////////////////以下是全局變量定義//////////////////////////////
DWORD* p_Screen; // 顯存指針
DWORD buff_Screen[WIN_WIDTH*WIN_HEIGHT]; // 顯示緩存
int isShowFPS=1; // 是否顯示幀數
int isShowMazePath=0; // 顯示迷宮路徑
int Maze[M][N]; // 迷宮數組 0 普通通路 1 牆 2 路徑點 3 起點 4 終點
int path_x[1000][L],path_y[1000][L]; // 路徑座標
int length[L]; // 路徑搜索長度
int lengtha,count; // 搜索計數
int mouseX; // 鼠標位置座標X
int mouseY; // 鼠標位置座標Y
bool isMouseDown; // 鼠標按下
int start_x,start_y,end_x,end_y; // 起點座標 終點座標
int temp_d; // 上一步走的方向
int Move_flag; // 移動標記
//////////////////////////////以下是輔助功能函數//////////////////////////////
// 計算畫面FPS(每秒幀數)
float GetFPS()
{
#define FPS_COUNT 8
static int i = 0;
static DWORD oldTime = GetTickCount();
static float fps;
if (i > FPS_COUNT)
{
i = 0;
int newTime = GetTickCount();
int elapsedTime = newTime - oldTime;
fps = FPS_COUNT / (elapsedTime / 1000.0f);
oldTime = newTime;
}
i++;
return fps;
}
// 絕對延時
void Delay(DWORD ms)
{
static DWORD oldTime = GetTickCount();
while(GetTickCount() - oldTime < ms)
Sleep(1);
oldTime = GetTickCount();
}
// 是否在矩形內
bool IsInRect(int x,int y,RECT r)
{
if((x>=r.left && x<=r.right) && (y>=r.top && y<=r.bottom)) return true;
else return false;
}
// 初始化迷宮數組
void InitMaze(){
for(int i=0;i<M;i++){
for(int j=0;j<N;j++){
Maze[i][j]=1;
}
}
// 設置起點和終點
Maze[1][0]=3;
Maze[M-2][N-1]=4;
}
// 輸出迷宮
void OutputMaze(){
// 清屏
setcolor(RGB(255,255,255));
setfillcolor(RGB(255,255,255));
fillrectangle(0,0,WIN_WIDTH,WIN_HEIGHT);
for(int i=0;i<M;i++){
for(int j=0;j<N;j++){
int xx=LX+j*B_WIDTH;
int yy=LY+i*B_WIDTH;
switch(Maze[i][j]){
case 0:
setcolor(C_GROUND);
setfillcolor(C_GROUND);
fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH);
break;
case 1:
setcolor(C_BLOCK);
setfillcolor(C_BLOCK);
fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH);
if(Maze[i][j]==1 && (i==M-1 || Maze[i+1][j]!=1))
{
setcolor(C_GRAY);
setfillcolor(C_GRAY);
fillrectangle(xx,yy+B_WIDTH/2,xx+B_WIDTH,yy+B_WIDTH);
}
break;
case 2:
setcolor(C_GROUND);
setfillcolor(C_GROUND);
fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH);
/*
setcolor(C_ROOT);
setfillcolor(C_ROOT);
fillellipse(xx+B_WIDTH/2-1,yy+B_WIDTH/2-1,xx+B_WIDTH/2+1,yy+B_WIDTH/2+1);
*/
break;
case 3:
setcolor(C_GROUND);
setfillcolor(C_GROUND);
fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH);
break;
case 4:
setcolor(C_GROUND);
setfillcolor(C_GROUND);
fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH);
break;
}
}
}
}
// 複製屏幕到緩存
void CopyScreenBuffer()
{
for(int i=0;i<WIN_WIDTH*WIN_HEIGHT;i++)
{
buff_Screen[i] = p_Screen[i];
}
}
// 繪製緩存到屏幕
void DrawScreenBuffer()
{
for(int i=0;i<WIN_WIDTH*WIN_HEIGHT;i++)
{
p_Screen[i]=buff_Screen[i];
}
}
// 輸出路徑連線
void OutPutPath()
{
if(isShowMazePath)
{
for(int i=0;i<length[0]-1;i++)
{
setcolor(C_LINE);
int x1=path_x[0][i]*B_WIDTH + B_WIDTH/2 + LX;
int x2=path_x[0][i+1]*B_WIDTH + B_WIDTH/2 + LX;
int y1=path_y[0][i]*B_WIDTH + B_WIDTH/2 + LY;
int y2=path_y[0][i+1]*B_WIDTH + B_WIDTH/2 + LY;
line(x1,y1,x2,y2);
}
}
}
// 輸出幀數
void DrawFPS()
{
//輸出幀數
if(isShowFPS)
{
setcolor(RED);
settextstyle(14, 0, _T("宋體"));
TCHAR s[5];
#if _MSC_VER > 1200
_stprintf_s(s, _T("%.1f"), GetFPS());
#else
_stprintf(s, _T("%.1f"), GetFPS());
#endif
outtextxy(4, 2, _T("FPS:"));
outtextxy(34, 2, s);
}
}
// 向指定方向移動,返回是否成功移動
int Move(int d,int x,int y)
{
switch(d)
{
// 向上
case 0:{
if(Maze[y-3][x]>0 && Maze[y-2][x-1]>0 && Maze[y-2][x+1]>0 && y-2>0)
{
Maze[y-2][x]=0;
Maze[y-1][x]=0;
Move_flag=1;
temp_d=0;
DFS_CreateMaze(x,y-2);
}
break;
}
// 向下
case 1:{
if(Maze[y+3][x]>0 && Maze[y+2][x-1]>0 && Maze[y+2][x+1]>0 && y+2<M)
{
Maze[y+2][x]=0;
Maze[y+1][x]=0;
Move_flag=1;
temp_d=1;
DFS_CreateMaze(x,y+2);
}
break;
}
// 向左
case 2:{
if(Maze[y][x-3]>0 && Maze[y-1][x-2]>0 && Maze[y+1][x-2]>0 && x-2>0)
{
Maze[y][x-1]=0;
Maze[y][x-2]=0;
Move_flag=1;
temp_d=2;
DFS_CreateMaze(x-2,y);
}
break;
}
// 向右
case 3:{
if(Maze[y][x+3]>0 && Maze[y-1][x+2]>0 && Maze[y+1][x+2]>0 && x+2<N)
{
Maze[y][x+1]=0;
Maze[y][x+2]=0;
Move_flag=1;
temp_d=3;
DFS_CreateMaze(x+2,y);
}
break;
}
}
return Move_flag;
}// 深度優先生成迷宮
// 深度優先生成迷宮
void DFS_CreateMaze(int x,int y)
{
//置通路
Maze[y][x]=0;
//隨機方向
int d=rand()%4;
int direct=temp_d;
int t[4]={0,0,0,0};// 已走方向標記
Move_flag=0;// 重置移動標記
// 保證隨機性的同時走滿地圖
while(t[0]!=1 || t[1]!=1 || t[2]!=1 || t[3]!=1)
{
int i=rand()%4;
if(t[i]!=1)
{
t[i]=1;
Move(i,x,y);
}
}
}
// 深度優先搜索迷宮路徑
void FindPath(int x,int y)
{
// 置路徑
Maze[y][x]=2;
path_x[count][lengtha]=x;
path_y[count][lengtha]=y;
lengtha++;
// 搜索到目的地
if(x==end_x && y==end_y)
{
//success=1;
length[count] = lengtha;
count++;
if(count>0)
{
// 複製上一次的路徑到下一次待搜索路徑中
for(int i=0;i<length[count-1];i++)
{
path_x[count][i]=path_x[count-1][i];
path_y[count][i]=path_y[count-1][i];
}
}
}
else
{
// 搜索並回溯
if(Maze[y][x+1]==0) {FindPath(x+1,y); Maze[y][x+1]=0;lengtha--;}
if(Maze[y+1][x]==0) {FindPath(x,y+1); Maze[y+1][x]=0;lengtha--;}
if(Maze[y][x-1]==0) {FindPath(x-1,y); Maze[y][x-1]=0;lengtha--;}
if(Maze[y-1][x]==0) {FindPath(x,y-1); Maze[y-1][x]=0;lengtha--;}
}
//if(success!=1)
//return success;
}
// 創建迷宮
void CreateMaze()
{
// 初始化迷宮數組
InitMaze();
isShowMazePath = 0;
// 從終點開始逆向生成迷宮
start_x=1;
start_y=1;
end_x=N-2;
end_y=M-2;
DFS_CreateMaze(end_x,end_y);
// 搜索路徑
lengtha=0;
count=0;
FindPath(start_x,start_y);
// 結果路徑複製到地圖
for(int i=0;i<length[0];i++)
{
Maze[path_y[0][i]][path_x[0][i]]=2;
}
}
// 繪製菜單按鈕
void DrawMenuItems()
{
RECT r;
r.left=860;
r.top=610;
r.right=r.left+90;
r.bottom=r.top+38;
setcolor(RGB(50,50,50));
if(IsInRect(mouseX,mouseY,r))
{
setfillcolor(RGB(220,220,220));
// 生成迷宮按鍵響應
if(isMouseDown)
{
// 創建迷宮
CreateMaze();
// 繪製一次到屏幕並複製到緩存
OutputMaze();
CopyScreenBuffer();
isMouseDown = 0;
}
}
else setfillcolor(RGB(240,240,240));
fillrectangle(r.left,r.top,r.right,r.bottom);
settextstyle(25, 9, _T("Verdana"));
setcolor(RGB(60,60,60));
outtextxy(r.left+5, r.top+5,_T(" 生成迷宮"));
r.left=1020;
r.top=610;
r.right=r.left+90;
r.bottom=r.top+38;
setcolor(RGB(50,50,50));
if(IsInRect(mouseX,mouseY,r))
{
setfillcolor(RGB(220,220,220));
// 顯示/隱藏路徑
if(isMouseDown)
{
isShowMazePath = !isShowMazePath;
isMouseDown = 0;
}
}
else setfillcolor(RGB(240,240,240));
fillrectangle(r.left,r.top,r.right,r.bottom);
settextstyle(25, 9, _T("Verdana"));
setcolor(RGB(60,60,60));
if(isShowMazePath) outtextxy(r.left+5, r.top+5,_T(" 隱藏路徑"));
else outtextxy(r.left+5, r.top+5,_T(" 顯示路徑"));
}
// 主函數
void main(){
// 置隨機數種子
srand(unsigned(time(NULL)));
// 初始化設備,加載圖片
initgraph(WIN_WIDTH, WIN_HEIGHT);
// 設置窗口標題
SetWindowText(GetHWnd(),_T("Maze v0.9 By:ls9512"));
cleardevice();
// 設置黑色背景
setbkmode(TRANSPARENT);
settextcolor(BLACK);
// 開啓雙緩衝,防止閃屏
BeginBatchDraw();
// 鼠標消息變量
MOUSEMSG mmsg;
// 獲取顯存指針
p_Screen = GetImageBuffer();
// 創建迷宮
CreateMaze();
// 繪製一次到屏幕並複製到緩存
OutputMaze();
CopyScreenBuffer();
// 主循環
while(true)
{
//處理鼠標消息
while(MouseHit())
{
mmsg = GetMouseMsg();
switch(mmsg.uMsg)
{
case WM_MOUSEMOVE: mouseX = mmsg.x; mouseY = mmsg.y; break;
case WM_LBUTTONDOWN: isMouseDown=true ;break;
case WM_LBUTTONUP: isMouseDown=false;break;
}
}
// 繪製,將緩存數據複製到屏幕提速
DrawScreenBuffer();
OutPutPath();
DrawMenuItems();
DrawFPS();
// 顯示緩存的繪製內容
FlushBatchDraw();
// 延遲,幀數控制
Delay(13);
}
// 關閉
EndBatchDraw();
closegraph();
}