/* -------------------------------------- ■ 墙壁 1 ☆ 目的地 3 ★ 箱子 4 ○ 箱子到达目的地 3+4=7 ♀ 人 5 人到达目的地 8 空格 路 0 1.设计关卡(定义关卡,判断关卡 2.找素材,用贴图的办法做一个可视化的推箱子游戏 */ #include #include #include #include #include #include #include #include #include #pragma comment(lib,"winmm.lib") //静态库资源 #define ROW 12 #define COL 12 using namespace std; const int WALL = 1; const int BOX = 4; const int TARGET = 3; const int PLAYER = 5; const int EMPTY = 0; const int _SIZE = 12; int DUISHU = 3; const std::vector> dir = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} }; class MyMap { public: int Map[_SIZE][_SIZE]{ WALL }; private: int X1{}, X2{}, Y1{}, Y2{};//分别为1、2步之前的坐标 int X{}, Y{}, Direction{};//当前坐标、方向 int PX{}, PY{};//玩家坐标 int StartX{}, StartY{};//记录起点,用于生成箱子 bool test() {//一定程度避免随机数整烂活; int test{}; for (int i = 0; i < _SIZE; i++) { for (int j = 0; j < _SIZE; j++) { if (Map[i][j] == 3)test += 1; if (Map[i][j] == 4)test += 10; if (Map[i][j] == 5)test += 100; } } return (test == 100 + 11 * DUISHU); } bool _near(int StartX, int StartY, int X, int Y) { return (StartX == X + 1 && StartY == Y || StartX == X - 1 && StartY == Y || StartY == Y + 1 && StartX == X || StartY == Y - 1 && StartX == X); } void update() { X2 = X1; Y2 = Y1; X1 = X; Y1 = Y; } void change(int x, int y) { if (Map[x][y] == WALL) { Map[x][y] = EMPTY; } } void through() {//打通箱子周边,使得每个箱子都可推动 int dx = StartX - PX, dy = StartY - PY; if (dx == 0) { change(StartX + 2 * (StartX > _SIZE / 2) - 1, StartY); change(StartX + 2 * (StartX > _SIZE / 2) - 1, StartY + 1); change(StartX + 2 * (StartX > _SIZE / 2) - 1, StartY - 1); } if (dy == 0) { change(StartX, StartY + 2 * (StartX > _SIZE / 2) - 1); change(StartX - 1, StartY + 2 * (StartX > _SIZE / 2) - 1); change(StartX + 1, StartY + 2 * (StartX > _SIZE / 2) - 1); } } public: MyMap() { for (int i = 0; i < _SIZE; i++) { for (int j = 0; j < _SIZE; j++) { Map[i][j] = WALL; } } }//初始化 bool generatemap(int Complexity) { int time{}; do {//主体 for (int i = 0; i < _SIZE; i++) { for (int j = 0; j < _SIZE; j++) { Map[i][j] = WALL; } }//初始化 for (int a = 0; a < DUISHU; a++)//箱子与目标的对数 { do { X = rand() % (_SIZE - 4) + 2; Y = rand() % (_SIZE - 4) + 2; time++; if (time > 100) { std::cout << "error" << std::endl; return 0; }//防死循环 } while (Map[X][Y] != WALL);//在墙上生成起点 time = 0; StartX = X; StartY = Y;//记录起点,用于生成箱子 do { int _time{};//实际运动次数 for (int i = 0; i < Complexity; i++) { int temp = rand() % 6; switch (temp) { case 0: Direction++; break; case 1: Direction--; }//控制直走和转弯的概率,不要频繁转弯且不会掉头 Direction = (4 + Direction) % 4;//防止负数 if (//各种不宜移动的情况 X + dir[Direction].first <= 2 || X + dir[Direction].first >= _SIZE - 3 || Y + dir[Direction].second <= 2 || Y + dir[Direction].second >= _SIZE - 3 || Map[X + dir[Direction].first][Y + dir[Direction].second] == BOX ) { continue; } update(); X += dir[Direction].first; Y += dir[Direction].second; change(X, Y); change(X - 2 * dir[Direction].first, Y - 2 * dir[Direction].second); if (_time > 0) { change( dir[Direction].first == 0 ? X2 : X - 2 * dir[Direction].first, dir[Direction].second == 0 ? Y2 : Y - 2 * dir[Direction].second ); } if (_time == 0) { PX = X - 2 * dir[Direction].first; PY = Y - 2 * dir[Direction].second; }//生成玩家 _time++; } Map[StartX][StartY] = BOX; Map[X][Y] = TARGET; } while (_near(StartX, StartY, X, Y)); through(); } Map[PX][PY] = PLAYER; } while (test() == 0); for (int i = 0; i < _SIZE; i++)//修复墙壁 { for (int j = 0; j < _SIZE; j++) { if (i == 0 || j == 0 || i == _SIZE - 1 || j == _SIZE - 1) { Map[i][j] = WALL; } } } return 1; } void printmap() { std::cout << std::endl; for (int i = 0; i < _SIZE; i++) { for (int j = 0; j < _SIZE; j++) { std::cout << Map[i][j] << ' '; } std::cout << std::endl; } } }; //用三维数组特定的数字描绘出这个地图 int cas = 0;//地图编号 int _count = 0;//已走步数 wchar_t result[10] = { L"0" };//用来显示步数 int temp_map[ROW][COL] = { 0 };//用来暂存地图,以备重置 char c[10] = { 0 }; int map[4][ROW][COL] = { 0 }; //{ // 1,1,1,1,1,1,1,1, // 1,3,4,0,0,4,3,1, // 1,0,1,3,0,1,0,1, // 1,0,1,4,0,1,0,1, // 1,0,0,5,0,0,0,1, // 1,0,1,0,0,1,0,1, // 1,3,4,0,0,4,3,1, // 1,1,1,1,1,1,1,1, // // 1,1,1,1,1,1,1,1, // 1,3,4,0,0,4,3,1, // 1,0,1,3,0,1,0,1, // 1,0,1,4,0,1,0,1, // 1,3,4,5,0,0,0,1, // 1,0,1,0,0,1,0,1, // 1,3,4,0,0,4,3,1, // 1,1,1,1,1,1,1,1, // // 1,1,1,1,1,1,1,1, // 1,3,4,0,0,4,3,1, // 1,0,1,3,0,1,0,1, // 1,0,1,4,0,1,0,1, // 1,3,4,0,0,4,3,1, // 1,0,1,0,0,1,0,1, // 1,3,4,5,0,4,3,1, // 1,1,1,1,1,1,1,1, // // 1,1,1,1,1,1,1,1, // 1,3,4,0,0,4,3,1, // 1,0,1,3,0,1,3,1, // 1,3,1,4,0,1,4,1, // 1,4,0,0,0,5,0,1, // 1,0,0,0,0,1,0,1, // 1,3,4,0,0,4,3,1, // 1,1,1,1,1,1,1,1, // //}; IMAGE img[6]; //6张图片,6个名字 IMAGE* bacground=new IMAGE; void loadResource() { loadimage(img + 0, L"0.bmp", 50, 50); loadimage(img + 1, L"1.bmp", 50, 50); loadimage(img + 2, L"3.bmp", 50, 50); loadimage(img + 3, L"4.bmp", 50, 50); loadimage(img + 4, L"5.bmp", 50, 50); loadimage(img + 5, L"7.bmp", 50, 50); loadimage(bacground, L"背景.png", 50, 50); } //绘制地图 void drawGraph() { for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { //算贴图的坐标 int x = 50 * j; int y = 50 * i; switch (map[cas][i][j]) { case 0: //一个汉字符号占用两个位置 //printf(" "); putimage(x, y, img + 0); break; case 1: putimage(x, y, img + 1); //printf("■"); break; case 3: putimage(x, y, img + 2); //printf("☆"); break; case 4: putimage(x, y, img + 3); //printf("★"); break; case 5: case 8: putimage(x, y, img + 4); //printf("人"); break; case 7: putimage(x, y, img + 5); //printf("●"); break; } } //printf("\n"); } } void drawGraph1()//绘制背景图 { for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { int x = 50 * j; int y = 50 * i; putimage(x, y, bacground); } } } //玩游戏 void keyDown() { int userKey = _getch(); //不可见输入 //定位:找到人的位置 int i = 0; int j = 0; for (i = 1; i < ROW; i++) { for (j = 1; j < COL; j++) { if (map[cas][i][j] == 5 || map[cas][i][j] == 8) { goto NEXT; } } } NEXT: //我们这个游戏用什么按键去玩 switch (userKey) { case 'W': case 'w': case 72: if (map[cas][i - 1][j] == 0 || map[cas][i - 1][j] == 3) { map[cas][i][j] -= 5; map[cas][i - 1][j] += 5; _count++; } if (map[cas][i - 1][j] == 4 || map[cas][i - 1][j] == 7) { if (map[cas][i - 2][j] == 0 || map[cas][i - 2][j] == 3) { map[cas][i][j] -= 5; map[cas][i - 1][j] += 1; map[cas][i - 2][j] += 4; _count++; } } break; case 's': case 'S': case 80: if (map[cas][i + 1][j] == 0 || map[cas][i + 1][j] == 3) { map[cas][i][j] -= 5; map[cas][i + 1][j] += 5; _count++; } if (map[cas][i + 1][j] == 4 || map[cas][i + 1][j] == 7) { if (map[cas][i + 2][j] == 0 || map[cas][i + 2][j] == 3) { map[cas][i][j] -= 5; map[cas][i + 1][j] += 1; map[cas][i + 2][j] += 4; _count++; } } break; case 'a': case 'A': case 75: if (map[cas][i][j - 1] == 0 || map[cas][i][j - 1] == 3) { //a+=1 a=a+1 复合赋值运算符 map[cas][i][j] -= 5; map[cas][i][j - 1] += 5; _count++; } if (map[cas][i][j - 1] == 4 || map[cas][i][j - 1] == 7) { if (map[cas][i][j - 2] == 0 || map[cas][i][j - 2] == 3) { map[cas][i][j] -= 5; map[cas][i][j - 1] += 1; map[cas][i][j - 2] += 4; _count++; } } break; case 'd': case 'D': case 77: if (map[cas][i][j + 1] == 0 || map[cas][i][j + 1] == 3) { map[cas][i][j] -= 5; map[cas][i][j + 1] += 5; _count++; } if (map[cas][i][j + 1] == 4 || map[cas][i][j + 1] == 7) { if (map[cas][i][j + 2] == 0 || map[cas][i][j + 2] == 3) { map[cas][i][j] -= 5; map[cas][i][j + 1] += 1; map[cas][i][j + 2] += 4; _count++; } } break; case 'z'://按下z重置 case 'Z': for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { map[cas][i][j] = temp_map[i][j]; } } _count = 0; for (int k = 0; k < 10; k++) { result[k] = 0; } result[0] = L'0'; drawGraph(); setbkmode(TRANSPARENT); settextcolor(BLACK); settextstyle(20, 0, _T("微软雅黑")); outtextxy(145, 55, L"已走步数:"); outtextxy(230, 55, result); break; case 'x': case 'X': cas = 3; for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { map[cas][i][j] = 0; } } break; } } //胜负的判断: int gameOver() { //地图上没有箱子就可以结束 for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { if (map[cas][i][j] == 4) { return 0; } } } return 1; } class Button { private: int x; // 按钮左上角x坐标 int y; // 按钮左上角y坐标 int width; // 按钮宽度 int height; // 按钮高度 float scale; // 缩放比例,用于实现鼠标悬停效果 bool isMouseOver; // 表示鼠标是否在按钮上方 bool has_been_clicked;//表示此按钮是否被点击过(主要用于难度选择) wstring text; // 按钮文本 public: Button(int x, int y, int width, int height, const wstring& text) : x(x), y(y), width(width), height(height), text(text), scale(1.0f), isMouseOver(false),has_been_clicked(false) { } // 检查鼠标是否在按钮上方 void checkMouseOver(int mouseX, int mouseY) { isMouseOver = (mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height); if (isMouseOver) { scale = 1.0f; // 鼠标悬停时缩放按钮 } else { scale = 1.0f; // 恢复按钮原始大小 } } // 检查鼠标点击是否在按钮内,并执行函数 bool checkClick(int mouseX, int mouseY) { if (mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height) { has_been_clicked_convert(); isMouseOver = false; scale = 1.0f; return true; } return false; } void has_been_clicked_convert() { if (has_been_clicked == false) has_been_clicked = true; else has_been_clicked = false; } bool get_has_been_clicked() { return has_been_clicked; } // 绘制按钮 void draw() { int scaledWidth = width * scale; // 缩放后的按钮宽度 int scaledHeight = height * scale; // 缩放后的按钮高度 int scaledX = x + (width - scaledWidth) / 2; // 缩放后的按钮x坐标 int scaledY = y + (height - scaledHeight) / 2; // 缩放后的按钮y坐标 if (isMouseOver || has_been_clicked) { setlinecolor(RGB(0, 120, 215)); // 鼠标悬停时按钮边框颜色 setfillcolor(RGB(229, 241, 251)); // 鼠标悬停时按钮填充颜色 } else { setlinecolor(RGB(173, 173, 173)); // 按钮边框颜色 setfillcolor(RGB(225, 225, 225)); // 按钮填充颜色 } fillrectangle(scaledX, scaledY, scaledX + scaledWidth, scaledY + scaledHeight); // 绘制按钮 settextcolor(BLACK); // 设置文本颜色为黑色 setbkmode(TRANSPARENT); // 设置文本背景透明 settextstyle(20 * scale, 0, _T("微软雅黑")); // 设置文本大小和字体 //居中显示按钮文本 int textX = scaledX + (scaledWidth - textwidth(text.c_str())) / 2; // 计算文本在按钮中央的x坐标 int textY = scaledY + (scaledHeight - textheight(_T("微软雅黑"))) / 2; // 计算文本在按钮中央的y坐标 outtextxy(textX, textY, text.c_str()); // 在按钮上绘制文本 } }; void play_easy() { const wchar_t* mapping = L"0123456789"; _count = 0; std::srand(static_cast(std::time(0))); MyMap _map{}; DUISHU = 2; for (int i = 0; i < 4; i++) { while (!_map.generatemap(50)) { if (_map.generatemap(50)) { break; } } for (int j = 0; j < ROW; j++) { for (int k = 0; k < COL; k++) { map[i][j][k] = _map.Map[j][k]; } } } for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { temp_map[i][j] = map[cas][i][j]; } } /*Button* restart = new Button{ 100, 300, 100, 25, L"重置" };*/ /*ExMessage msg;*/ while (1) { drawGraph(); setbkmode(TRANSPARENT); settextcolor(BLACK); settextstyle(20, 0, _T("微软雅黑")); outtextxy(500, 570, L"已走步数:"); outtextxy(100, 570, L"重置:Z"); outtextxy(200, 570, L"主菜单:X"); int q = 0; int temp = _count; while (temp != 0) { c[q] = mapping[temp % 10]; temp /= 10; q++; } if (q != 0) { for (int j = 0; j < q; j++) { result[j] = c[q - j - 1]; } } outtextxy(570, 570, result); if (gameOver())//如果游戏结束 { cas++; //变换关卡 _count = 0; for (int i = 0; i < 10; i++) result[i] = 0; result[0] = { L'0' }; for (int i = 0; i < 10; i++) c[i] = 0; if (cas != 4) { Sleep(500); drawGraph(); setbkmode(TRANSPARENT); settextcolor(BLACK); settextstyle(20, 0, _T("微软雅黑")); outtextxy(500, 570, L"已走步数:"); outtextxy(570, 570, result); outtextxy(100, 570, L"重置:Z"); outtextxy(200, 570, L"主菜单:X"); for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { temp_map[i][j] = map[cas][i][j]; } } } else if (cas == 4) { cas = 0; break; } } FlushBatchDraw(); // 将缓冲区内容显示在屏幕上 Sleep(10); keyDown();//循环按下按键 //system("cls"); } /*delete restart;*/ } void play_normal() { const wchar_t* mapping = L"0123456789"; _count = 0; std::srand(static_cast(std::time(0))); MyMap _map{}; DUISHU = 4; for (int i = 0; i < 4; i++) { while (!_map.generatemap(30)) { if (_map.generatemap(30)) { break; } } for (int j = 0; j < ROW; j++) { for (int k = 0; k < COL; k++) { map[i][j][k] = _map.Map[j][k]; } } } for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { temp_map[i][j] = map[cas][i][j]; } } while (1) { drawGraph(); setbkmode(TRANSPARENT); settextcolor(BLACK); settextstyle(20, 0, _T("微软雅黑")); outtextxy(500, 570, L"已走步数:"); outtextxy(100, 570, L"重置:Z"); outtextxy(200, 570, L"主菜单:X"); int q = 0; int temp = _count; while (temp != 0) { c[q] = mapping[temp % 10]; temp /= 10; q++; } if (q != 0) { for (int j = 0; j < q; j++) { result[j] = c[q - j - 1]; } } outtextxy(570, 570, result); if (gameOver())//如果游戏结束 { cas++; //变换关卡 _count = 0; for (int i = 0; i < 10; i++) result[i] = 0; result[0] = { L'0' }; for (int i = 0; i < 10; i++) c[i] = 0; if (cas != 4) { Sleep(500); drawGraph(); setbkmode(TRANSPARENT); settextcolor(BLACK); settextstyle(20, 0, _T("微软雅黑")); outtextxy(500, 570, L"已走步数:"); outtextxy(570, 570, result); outtextxy(100, 570, L"重置:Z"); outtextxy(200, 570, L"主菜单:X"); for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { temp_map[i][j] = map[cas][i][j]; } } } else if (cas == 4) { cas = 0; break; } } FlushBatchDraw(); // 将缓冲区内容显示在屏幕上 Sleep(10); keyDown();//循环按下按键 //system("cls"); } } void play_hard() { const wchar_t* mapping = L"0123456789"; _count = 0; std::srand(static_cast(std::time(0))); MyMap _map{}; DUISHU = 6; for (int i = 0; i < 4; i++) { while (!_map.generatemap(8)) { if (_map.generatemap(8)) { break; } } for (int j = 0; j < ROW; j++) { for (int k = 0; k < COL; k++) { map[i][j][k] = _map.Map[j][k]; } } } for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { temp_map[i][j] = map[cas][i][j]; } } while (1) { drawGraph(); setbkmode(TRANSPARENT); settextcolor(BLACK); settextstyle(20, 0, _T("微软雅黑")); outtextxy(500, 570, L"已走步数:"); outtextxy(100, 570, L"重置:Z"); outtextxy(200, 570, L"主菜单:X"); int q = 0; int temp = _count; while (temp != 0) { c[q] = mapping[temp % 10]; temp /= 10; q++; } if (q != 0) { for (int j = 0; j < q; j++) { result[j] = c[q - j - 1]; } } outtextxy(570, 570, result); if (gameOver())//如果游戏结束 { cas++; //变换关卡 _count = 0; for (int i = 0; i < 10; i++) result[i] = 0; result[0] = { L'0' }; for (int i = 0; i < 10; i++) c[i] = 0; if (cas != 4) { Sleep(500); drawGraph(); setbkmode(TRANSPARENT); settextcolor(BLACK); settextstyle(20, 0, _T("微软雅黑")); outtextxy(500, 570, L"已走步数:"); outtextxy(570, 570, result); outtextxy(100, 570, L"重置:Z"); outtextxy(200, 570, L"主菜单:X"); for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { temp_map[i][j] = map[cas][i][j]; } } } else if (cas == 4) { cas = 0; break; } } FlushBatchDraw(); // 将缓冲区内容显示在屏幕上 Sleep(10); keyDown();//循环按下按键 //system("cls"); } } int main() { loadResource();//定义加载资源函数 //mciSendString(L"open 1.mp3", 0, 0, 0);//加载音乐 //mciSendString(L"play 1.mp3 repeat", 0, 0, 0);//循环播放音乐资源 initgraph(50 * ROW, 50 * COL); Button* start = new Button{ 150, 400, 100, 25, L"开始" }; Button* easy = new Button{ 100, 300, 100, 25, L"简单" }; Button* normal = new Button{ 250, 300, 100, 25, L"中等" }; Button* hard = new Button{ 400, 300, 100, 25, L"困难" }; Button* exit = new Button{ 350, 400, 100, 25, L"退出" }; while (1) { if (start->get_has_been_clicked()) start->has_been_clicked_convert(); if (easy->get_has_been_clicked()) easy->has_been_clicked_convert(); if (normal->get_has_been_clicked()) normal->has_been_clicked_convert(); if (hard->get_has_been_clicked()) hard->has_been_clicked_convert(); if (exit->get_has_been_clicked()) exit->has_been_clicked_convert(); drawGraph1(); setbkmode(TRANSPARENT); settextcolor(BLACK); settextstyle(50, 0, _T("微软雅黑")); outtextxy(250, 150, L"推箱子"); ExMessage msg; while (true) { if (peekmessage(&msg)) // 检查是否有消息 { int mouseX = msg.x; // 获取鼠标x坐标 int mouseY = msg.y; // 获取鼠标y坐标 if (msg.message == WM_LBUTTONDOWN) { if (start->checkClick(mouseX, mouseY)) { if (easy->get_has_been_clicked()) { play_easy(); break; } else if (normal->get_has_been_clicked()) { play_normal(); break; } else if(hard->get_has_been_clicked()) { play_hard(); break; } else { } } else if (easy->checkClick(mouseX, mouseY)) { if (normal->get_has_been_clicked()) normal->has_been_clicked_convert(); if (hard->get_has_been_clicked()) hard->has_been_clicked_convert(); } else if (normal->checkClick(mouseX, mouseY)) { if (easy->get_has_been_clicked()) easy->has_been_clicked_convert(); if (hard->get_has_been_clicked()) hard->has_been_clicked_convert(); } else if (hard->checkClick(mouseX, mouseY)) { if (normal->get_has_been_clicked()) normal->has_been_clicked_convert(); if (easy->get_has_been_clicked()) easy->has_been_clicked_convert(); } else if (exit->checkClick(mouseX, mouseY)) { return 0; } } else if (msg.message == WM_MOUSEMOVE) { start->checkMouseOver(mouseX, mouseY); easy->checkMouseOver(mouseX, mouseY); normal->checkMouseOver(mouseX, mouseY); hard->checkMouseOver(mouseX, mouseY); exit->checkMouseOver(mouseX, mouseY); } } start->draw(); // 绘制当前页面内容 easy->draw(); normal->draw(); hard->draw(); exit->draw(); FlushBatchDraw(); // 将缓冲区内容显示在屏幕上 Sleep(10); } } delete start; delete easy; delete normal; delete hard; delete exit; delete bacground; closegraph();//关闭窗口 return 0; }