黑神话.孔明棋

教育   2024-11-19 07:01   宁夏  
背景
时我并热衷于电脑游戏,但最近《黑神话悟空》这款游戏确实火得不行。出于好奇,我下载了一个名为《黑神话:悟空 性能测试工具》的应用,至于为何没有直接下载《黑神话:悟空》,原因颇为复杂,这里就不赘述了。
在性能测试工具的过程中,我深受启发。首先让我印象深刻的是游戏的格局和气魄。反观我平时编写的一些程序,窗口尺寸往往只有640*480,仿佛将用户拘泥在一个狭小的空间里,这样的设计又如何能成就完美的程序呢?其次,游戏中的细节处理也让我赞叹不已,无论是按钮设计、鼠标操作还是光线效果,都体现出了开发者们的匠心独运,这些都是值得我们学习和借鉴的宝贵经验。
于是,我萌生了一个念头:能否尝试模仿这些优秀作品的布局和格式,创作一款属于自己的游戏呢?
经过一周多的不懈努力,我终于完成了《黑神话:孔明棋》这款游戏的初步版本。虽然目前游戏中还有许多功能亟待完善,代码的优化程度和整体架构也有很大的提升空间,但我依然对自己的成果感到自豪。
为了让你在第一时间感受到游戏的魅力,我决定先在这里展示一下游戏的效果!
效果

源码
///////////////////////////////////////////////////// 程序名称:黑神话 孔明棋// 编译环境:Mictosoft Visual Studio 2022, EasyX_20200315(beta)// 作  者:luoyh <2864292458@qq.com>// 公 众 号:C语言研究// 版 本 号:Version 1.0.0// 最后修改:2024-11-18//#include<graphics.h>#include<conio.h>#include<math.h>#include<time.h>#include <Windows.h>#include <vector>#include <stack>#include <cstring>#include <iostream>using namespace std;
#define PI acos(-1.0)
struct Button{ int X1; // 左上角 int Y1; int X2; // 右下角 int Y2; int size; // 字体大小 LPCTSTR str; // 对应文本};
Button button1 = { 90, 635, 605, 700, 40, TEXT("开始游戏") };Button button2 = { 90, 700 + 10, 605, 765 + 10, 40, TEXT("游戏说明") };Button button3 = { 90, 765 + 20, 605, 830 + 20, 40, TEXT("退出") };bool WINDOWS(LPCTSTR str, IMAGE img, LPCTSTR str1, LPCTSTR str2);
void DrawButton(Button button, COLORREF color);void DrawButtonT(Button button, COLORREF color);void GradientFill(int left, int top, int right, int bottom, COLORREF startColor, COLORREF endColor);int RETURN();void HideSphere(float R, int alfa, int beta, int HideFlag, int X, int Y, COLORREF endColor);// 设置分辨率为2560*1440(推荐)int rW = GetSystemMetrics(SM_CXSCREEN); // 屏幕宽度 像素int rH = GetSystemMetrics(SM_CYSCREEN); // 屏幕高度 像素// 全屏void Full_Screen(); // 全屏void DrawTXT(); // 绘制void BianAN(); // 变暗淡void DrawLoge(int x, int y, int size, COLORREF color);void DrawInit(); // 绘制初始界面void DrawKMQY(int x, int y, int size); // 绘制孔明棋void BianLIANG(); // 变亮void CheckForInput();bool gameShouldContinue = false;IMAGE img(rW, rH);IMAGE img1(rW, rH);// 将图片进行模糊处理void Blur(IMAGE* pimg);void INITCircle();void MYCIRCLE(int x, int y);// str代表绘制的字符串// variable 代表每次的变量// fors 每次需要循环的次数总数// R 该圈的半径// Radian 累加的弧度void DrawCircle(TCHAR str[25], int variable, int fors, int R, double Radian, int x, int y); // 绘制一圈字符 void DrawMouse(int x, int y); // 绘制鼠标按键void DrawR(int x, int y); // 绘制撤销void BeginGame(); // 开始游戏void INstrucTionS(); // 游戏介绍bool EXIT(); // 退出// 一个圈的结构体struct TimeCircle{ int fors; // 每一圈分成的份数 int R; // 圈的半径 double NextTime; // 上一个时刻时间 double Radian; // 累加的弧度};SYSTEMTIME ti;TimeCircle TC[3];TCHAR str[25];
// 设置随机数产生方法 int getRandom(int min, int max){ return rand() % (max - min + 1) + min;}
// Point类 class Point{public: int x, y; int xSpeed, ySpeed; int r;
Point() { r = 3; // 点的半径 x = getRandom(0, getwidth() - r); y = getRandom(0, getheight() - r); xSpeed = getRandom(-3, 3); ySpeed = getRandom(-3, 3); }
void draw(){ // 更新坐标 x += xSpeed; y += ySpeed;
// 超出边界回弹 if (x > getwidth() - r) { x = getwidth() - r; xSpeed = -xSpeed; } else if (x < 0) { x = 0; xSpeed = -xSpeed; } if (y > getheight() - r) { y = getheight() - r; ySpeed = -ySpeed; } else if (y < 0) { y = 0; ySpeed = -ySpeed; }
// 绘制点 setfillcolor(RGB(200, 200, 200)); solidcircle(x, y, r); }};
// Graph类 class Graph{private: Point* points; int pointNumber; int maxDis;
public: Graph(int pointNum = 200, int maxDistance = 100) { pointNumber = pointNum; maxDis = maxDistance; points = new Point[pointNumber]; }
~Graph() { delete[] points; }
void draw(){ for (int i = 0; i < pointNumber; i++) { points[i].draw();
for (int j = i + 1; j < pointNumber; j++) { int dx = points[i].x - points[j].x; int dy = points[i].y - points[j].y; int distance = sqrt(dx * dx + dy * dy);
if (distance <= maxDis) { int NUM = 255 - static_cast<int>(distance * 255.0 / maxDis); // 确保NUM是整数 int offset = 50; setlinecolor(RGB(NUM + offset, NUM + offset, NUM + offset)); // 当NUM接近0时,颜色接近黑色;当NUM接近255时,颜色接近白色 setlinestyle(PS_SOLID, 1); line(points[i].x, points[i].y, points[j].x, points[j].y); } } } }};const int N = 7;const int INVALID = 2;const int PEG = 1;const int EMPTY = 0;
struct Position{ int x, y; Position(int _x, int _y) : x(_x), y(_y) {}};
struct Move{ Position pos; int dir; Move(Position _pos, int _dir) : pos(_pos), dir(_dir) {}};
class PegSolitaire{private: int board[N][N]; stack<Move> moveHistory; void drawBoard(int X, int Y){ setlinecolor(WHITE); for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { int Mx = rW / 2 - 360 + j * 120; int My = rH / 2 - 360 + i * 120;
if (board[i][j] == INVALID) continue;
if (board[i][j] == PEG) { if (i == Y && j == X) { HideSphere(50, 30, 15, 1, Mx, My, YELLOW); } else { HideSphere(50, 30, 15, 1, Mx, My, WHITE); } } else if (board[i][j] == EMPTY) { setlinecolor(WHITE); circle(Mx, My, 50); } } } int pegCount = 0; for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { if (board[i][j] == PEG) pegCount++; } }
HideSphere(30, 30, 15, 1, 2390, 80, WHITE); settextcolor(WHITE); settextstyle(60, 0, L"微软雅黑", 0, 0, 0, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH); TCHAR s[20]; _stprintf_s(s, _T(" X%d"), pegCount); outtextxy(2430, 60, s); }
bool legalMove(int x, int y, int dir){ int dx[] = { -2, 2, 0, 0 }; int dy[] = { 0, 0, -2, 2 }; int mx[] = { -1, 1, 0, 0 }; int my[] = { 0, 0, -1, 1 };
int nx = x + dx[dir]; int ny = y + dy[dir]; int mx1 = x + mx[dir]; int my1 = y + my[dir];
if (nx < 0 || ny < 0 || nx >= N || ny >= N) return false; if (board[nx][ny] != EMPTY) return false; if (board[mx1][my1] != PEG) return false;
return true; }
void makeMove(int x, int y, int dir){ int dx[] = { -2, 2, 0, 0 }; int dy[] = { 0, 0, -2, 2 }; int mx[] = { -1, 1, 0, 0 }; int my[] = { 0, 0, -1, 1 };
int nx = x + dx[dir]; int ny = y + dy[dir]; int mx1 = x + mx[dir]; int my1 = y + my[dir];
board[x][y] = EMPTY; board[mx1][my1] = EMPTY; board[nx][ny] = PEG;
moveHistory.push(Move(Position(x, y), dir)); }
void undoMove(){ if (moveHistory.empty()) return;
Move lastMove = moveHistory.top(); moveHistory.pop();
int x = lastMove.pos.x; int y = lastMove.pos.y; int dir = lastMove.dir;
int dx[] = { -2, 2, 0, 0 }; int dy[] = { 0, 0, -2, 2 }; int mx[] = { -1, 1, 0, 0 }; int my[] = { 0, 0, -1, 1 };
int nx = x + dx[dir]; int ny = y + dy[dir]; int mx1 = x + mx[dir]; int my1 = y + my[dir];
board[x][y] = PEG; board[mx1][my1] = PEG; board[nx][ny] = EMPTY; }
bool isGameOver(){ for (int x = 0; x < N; ++x) { for (int y = 0; y < N; ++y) { if (board[x][y] == PEG) { for (int dir = 0; dir < 4; ++dir) { if (legalMove(x, y, dir)) return false; } } } } return true; }
bool isWin(){ int pegCount = 0; for (int x = 0; x < N; ++x) { for (int y = 0; y < N; ++y) { if (board[x][y] == PEG) pegCount++; } } return pegCount == 1; }
void resetBoard(){ int initialBoard[N][N] = { {2, 2, 1, 1, 1, 2, 2}, {2, 2, 1, 1, 1, 2, 2}, {1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 0, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1}, {2, 2, 1, 1, 1, 2, 2}, {2, 2, 1, 1, 1, 2, 2} }; memcpy(board, initialBoard, sizeof(board)); while (!moveHistory.empty()) moveHistory.pop(); }
public: PegSolitaire() { resetBoard(); } Graph g; void playGame(){ INITCircle();
ExMessage msg; int DJNUM = 0; int Mx; int My; bool T = true; int x = 3; int y = 3; int dir = -1; drawBoard(x, y); bool JX = true; BeginBatchDraw(); flushmessage(); while (!isGameOver() && JX) { if (isWin()) { IMAGE Mimg(rW, rH);
// 设置绘图目标为 img 对象 SetWorkingImage(&Mimg); // 以下绘图操作都会绘制在 img 对象上面 g.draw(); drawBoard(y, x); // 每次操作后重绘棋盘 MYCIRCLE(rW / 2, rH / 2); DrawMouse(2300, 1360); DrawR(2120, 1360); // 设置绘图目标为绘图窗口 SetWorkingImage(); if (!WINDOWS(_T("恭喜你!挑战成功"), Mimg, _T("再来一局"), _T("退出"))) { BeginGame(); } break; }
// 获取坐标两次 // 第一次获取起始位置 // 第二次获取终止位置 计算方向 //ExMessage m; // 定义消息变量
while (peekmessage(&msg, EX_MOUSE | EX_KEY)) // 如果获取到了消息就执行 { switch (msg.message) { case WM_LBUTTONDOWN: if (msg.x > (rW / 2 - 420) && msg.x < (rW / 2 + 420) && msg.y>(rH / 2 - 420) && msg.y < (rH / 2 + 420)) { DJNUM++; Mx = msg.x; My = msg.y; }
break; case WM_RBUTTONUP: {
JX = false; } break; //undoMove(); 一个撤销功能 case WM_KEYDOWN: if (msg.vkcode == 0x52) { undoMove(); } break; } }
/* cout << "请输入 x, y, 方向 (0=北, 1=南, 2=西, 3=东): ";*/
if (DJNUM == 1) // 按下第一次 { y = (Mx - (rW / 2 - 420)) / 120; x = (My - (rH / 2 - 420)) / 120; // 获取起始位置 } if (DJNUM == 2) // 按下第二次 获取方向 { int EndY = (Mx - (rW / 2 - 420)) / 120; int EndX = (My - (rH / 2 - 420)) / 120; if (EndX == x && EndY == (y - 2)) { dir = 2; } if (EndX == x && EndY == (y + 2)) { dir = 3; } if (EndY == y && EndX == (x - 2)) { dir = 0; } if (EndY == y && EndX == (x + 2)) { dir = 1; } T = false; DJNUM = 0; if (dir == -1) // 乱点击的情况 { T = true; DJNUM = 0; } }
if (!T) { if (legalMove(x, y, dir)) { makeMove(x, y, dir); dir = -1; T = true; } else { /*cout << "非法移动!" << endl;*/ } } FlushBatchDraw(); cleardevice(); g.draw(); drawBoard(y, x); // 每次操作后重绘棋盘 MYCIRCLE(rW / 2, rH / 2); DrawMouse(2300, 1360); DrawR(2120, 1360); /* Sleep(20);*/
} EndBatchDraw();
if (!isWin()) { int pegCount = 0; for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { if (board[i][j] == PEG) pegCount++; } } TCHAR s[20]; _stprintf_s(s, _T("挑战失败,还剩余%d枚棋子!"), pegCount); IMAGE Mimg(rW, rH);
// 设置绘图目标为 img 对象 SetWorkingImage(&Mimg); // 以下绘图操作都会绘制在 img 对象上面 g.draw(); drawBoard(y, x); // 每次操作后重绘棋盘 MYCIRCLE(rW / 2, rH / 2); DrawMouse(2300, 1360); DrawR(2120, 1360); // 设置绘图目标为绘图窗口 SetWorkingImage(); if (!WINDOWS(s, Mimg, _T("再来一局"), _T("退出"))) { BeginGame(); } } }};
int main(){ Full_Screen(); DrawTXT(); BianAN(); // 变亮度 显示 孔明棋 DrawInit(); BianLIANG(); CheckForInput(); // 按任意键继续,不管是键盘还是鼠标。都继续 // 开始写按钮 cleardevice(); bool CTN = true; // 字体角度问题处理一下 while (CTN) { int NUM = RETURN(); // 鼠标操作 switch (NUM) { case 0:BeginGame(); break; case 1:INstrucTionS(); break; case 2:CTN = EXIT(); break; } }}
void Full_Screen() // 全屏{ HWND hwnd = initgraph(rW, rH); // 初始化绘图窗口并获取窗口句柄(以 EasyX 为例) LONG l_WinStyle = GetWindowLong(hwnd, GWL_STYLE); // 获取窗口信息 // 设置窗口信息 最大化 取消标题栏及边框 SetWindowLong(hwnd, GWL_STYLE, (l_WinStyle | WS_POPUP | WS_MAXIMIZE) & ~WS_CAPTION & ~WS_THICKFRAME & ~WS_BORDER);// 直接修改窗口样式 SetWindowPos(hwnd, HWND_TOP, 0, 0, rW, rH, 0); setbkcolor(BLACK); cleardevice(); // 设置背景颜色为黑色}
void DrawTXT(){ SetWorkingImage(&img); setlinecolor(RGB(51, 51, 51)); settextcolor(RGB(51, 51, 51)); setlinestyle(PS_SOLID | PS_ENDCAP_SQUARE | PS_JOIN_MITER, 3); rectangle(1080, 662, 1172, 776); setbkmode(TRANSPARENT); DrawLoge(1090, 680, 70, RGB(51, 51, 51)); setlinecolor(WHITE); settextcolor(WHITE); setlinestyle(PS_SOLID | PS_ENDCAP_SQUARE | PS_JOIN_MITER, 4); rectangle(1080 - 6, 662 - 6, 1172 - 6, 776 - 6); DrawLoge(1090 - 4, 680 - 4, 70, WHITE); RECT r = { 1188,656,1480 + 60,726 };
// 设置输出效果为抗锯齿 (VC6 / VC2008 / VC2010 / VC2012) LOGFONT f; gettextstyle(&f); // 获取当前字体设置 f.lfHeight = 80; // 设置字体高度为 _tcscpy_s(f.lfFaceName, _T("等线")); // 设置字体为“黑体”(高版本 VC 推荐使用 _tcscpy_s 函数) f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿 settextstyle(&f); // 设置字体样式 drawtext(_T("C语言研究"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); f.lfHeight = 45; // 设置字体高度为 settextstyle(&f); // 设置字体样式 r = { 1188,743,1480 + 60,776 }; drawtext(_T("C YUYAN YANJIU"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); Sleep(1000); SetWorkingImage();}
void BianAN() // 变暗淡{ // 获取绘图窗口和 img 对象的显示缓冲区指针 DWORD* pbWnd = GetImageBuffer(); DWORD* pbImg = GetImageBuffer(&img);
// 计算原图片每个点的颜色,实现逐渐变量的效果 int r, g, b; for (int light = 1; light <= 64; light++) { for (int i = 0; i < rW * rH; i++) { r = (GetRValue(pbImg[i]) * light) >> 6; g = (GetGValue(pbImg[i]) * light) >> 6; b = (GetBValue(pbImg[i]) * light) >> 6; pbWnd[i] = RGB(r, g, b); } Sleep(20); } Sleep(1000); for (int light = 64; light >= 1; light--) { for (int i = 0; i < rW * rH; i++) { r = (GetRValue(pbImg[i]) * light) >> 6; g = (GetGValue(pbImg[i]) * light) >> 6; b = (GetBValue(pbImg[i]) * light) >> 6; pbWnd[i] = RGB(r, g, b); } Sleep(20); }}
void DrawLoge(int x, int y, int size, COLORREF color){ double JG = size / 16.0; setlinestyle(PS_SOLID | PS_ENDCAP_SQUARE | PS_JOIN_MITER, JG); circle(x + JG * 4, y + JG * 4, 2.5 * JG); circle(x + JG * 12, y + JG * 12, 2.5 * JG); setfillcolor(color); solidrectangle(x + JG * 8, y, x + size, y + JG * 8); solidrectangle(x, y + JG * 8, x + JG * 8, y + size); setfillcolor(BLACK); solidrectangle(x + JG * 3, y + JG * 9, x + JG * 5, y + JG * 15); solidrectangle(x + JG * 2, y + JG * 10, x + JG * 3, y + JG * 11); solidrectangle(x + JG * 2, y + JG * 14, x + JG * 6, y + JG * 15); solidrectangle(x + JG * 11, y + JG, x + JG * 13, y + JG * 7); solidrectangle(x + JG * 10, y + JG * 2, x + JG * 11, y + JG * 3); solidrectangle(x + JG * 10, y + JG * 6, x + JG * 14, y + JG * 7);}
void DrawInit(){ SetWorkingImage(&img1); setbkmode(TRANSPARENT); settextcolor(WHITE); // 设置输出效果为抗锯齿 (VC6 / VC2008 / VC2010 / VC2012) LOGFONT f; gettextstyle(&f); // 获取当前字体设置 f.lfHeight = 300; // 设置字体高度为 _tcscpy_s(f.lfFaceName, _T("方正舒体")); // 设置字体为“黑体”(高版本 VC 推荐使用 _tcscpy_s 函数) f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿 settextstyle(&f); // 设置字体样式 outtextxy(300, 400, _T("孔")); outtextxy(500, 540, _T("明")); outtextxy(700, 680, _T("棋")); DrawKMQY(1700, 690, 400); SetWorkingImage();}
void DrawKMQY(int x, int y, int size){ setlinecolor(WHITE); setlinestyle(PS_SOLID | PS_ENDCAP_SQUARE | PS_JOIN_MITER, 10); circle(x, y, size); int JG = size * 2 / 9.0; int R = JG - 50; setlinestyle(PS_SOLID | PS_ENDCAP_SQUARE | PS_JOIN_MITER, 4); circle(x, y, size + 14); int initialBoard[7][7] = { {2, 2, 1, 1, 1, 2, 2}, {2, 2, 1, 1, 1, 2, 2}, {1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 0, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1}, {2, 2, 1, 1, 1, 2, 2}, {2, 2, 1, 1, 1, 2, 2} };
setfillcolor(WHITE); for (int i = 0; i < 7; ++i) { for (int j = 0; j < 7; ++j) { int X = x - JG * 3 + j * JG; int Y = y - JG * 3 + i * JG;
if (initialBoard[i][j] == 2) continue;
if (initialBoard[i][j] == 1) { solidcircle(X, Y, R); } else if (initialBoard[i][j] == 0) { circle(X, Y, R); } } }
// 设置输出效果为抗锯齿 (VC6 / VC2008 / VC2010 / VC2012) LOGFONT f; gettextstyle(&f); // 获取当前字体设置 f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿 f.lfHeight = 25; // 设置字体高度为 _tcscpy_s(f.lfFaceName, _T("楷体")); // 设置字体为“黑体”(高版本 VC 推荐使用 _tcscpy_s 函数) settextcolor(RGB(120,120,120)); settextstyle(&f); outtextxy(116, 1356, _T("© [2024] [微信公众号:C语言研究]. All rights reserved."));
outtextxy(2200, 1335, _T("Version 1.0.0"));
}
void BianLIANG() // 变亮{ // 获取绘图窗口和 img 对象的显示缓冲区指针 DWORD* pbWnd = GetImageBuffer(); DWORD* pbImg = GetImageBuffer(&img1);
// 计算原图片每个点的颜色,实现逐渐变量的效果 int r, g, b; for (int light = 1; light <= 64; light++) { for (int i = 0; i < rW * rH; i++) { r = (GetRValue(pbImg[i]) * light) >> 6; g = (GetGValue(pbImg[i]) * light) >> 6; b = (GetBValue(pbImg[i]) * light) >> 6; pbWnd[i] = RGB(r, g, b); } Sleep(30); } // 设置输出效果为抗锯齿 (VC6 / VC2008 / VC2010 / VC2012) LOGFONT f; gettextstyle(&f); // 获取当前字体设置 f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿 f.lfHeight = 30; // 设置字体高度为 _tcscpy_s(f.lfFaceName, _T("楷体")); // 设置字体为“黑体”(高版本 VC 推荐使用 _tcscpy_s 函数) settextcolor(GREEN); settextstyle(&f); outtextxy(500, 1000, _T("按任意键开始"));}
void CheckForInput(){ ExMessage msg;
// 游戏的主循环 while (true) { peekmessage(&msg, EX_MOUSE); if (msg.lbutton) { return; } else if (msg.rbutton) { return; } else if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) { return; // 退出函数 } }}
void DrawButton(Button button, COLORREF color){ setbkmode(TRANSPARENT); if (color == WHITE) { settextcolor(color); } else { GradientFill(button.X1, button.Y1, button.X2, button.Y2, WHITE, BLACK); settextcolor(color); } LOGFONT f; gettextstyle(&f); // 获取当前字体设置 f.lfHeight = button.size; // 设置字体高度为 _tcscpy_s(f.lfFaceName, _T("楷体")); // 设置字体为“黑体”(高版本 VC 推荐使用 _tcscpy_s 函数) f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿 settextstyle(&f); // 设置字体样式 outtextxy(button.X1 + 35, button.Y1 + 15, button.str);}
int RETURN(){ int NUM = 0; srand(time(NULL)); Graph g; // 定义变量,保存鼠标消息 ExMessage msg; BeginBatchDraw(); bool T = true; // 游戏的主循环 while (T) { FlushBatchDraw(); cleardevice(); g.draw(); DrawButton(button1, WHITE); DrawButton(button2, WHITE); DrawButton(button3, WHITE); DrawKMQY(1700, 690, 400); while (peekmessage(&msg, EX_MOUSE)) // 如果获取到了消息就执行 { switch (msg.message) { case WM_MOUSEMOVE: if (msg.x > button1.X1 && msg.x<button1.X2 && msg.y>button1.Y1 && msg.y < button1.Y2) { NUM = 1; } if (msg.x > button2.X1 && msg.x<button2.X2 && msg.y>button2.Y1 && msg.y < button2.Y2) { NUM = 2; } if (msg.x > button3.X1 && msg.x<button3.X2 && msg.y>button3.Y1 && msg.y < button3.Y2) { NUM = 3; } break; case WM_LBUTTONDOWN: if (msg.x > button1.X1 && msg.x<button1.X2 && msg.y>button1.Y1 && msg.y < button1.Y2) { NUM = 4; } if (msg.x > button2.X1 && msg.x<button2.X2 && msg.y>button2.Y1 && msg.y < button2.Y2) { NUM = 5; } if (msg.x > button3.X1 && msg.x<button3.X2 && msg.y>button3.Y1 && msg.y < button3.Y2) { NUM = 6; } break; } }
switch (NUM) { case 0:break; case 1: DrawButton(button1, BLACK); DrawButton(button2, WHITE); DrawButton(button3, WHITE); break; case 2: DrawButton(button1, WHITE); DrawButton(button2, BLACK); DrawButton(button3, WHITE); break; case 3: DrawButton(button1, WHITE); DrawButton(button2, WHITE); DrawButton(button3, BLACK); break; case 4:T = false; NUM = 0; break; // 开始游戏 case 5:T = false; NUM = 1; break; // 游戏说明 case 6:T = false; NUM = 2; break; // 退出 } Sleep(20); } EndBatchDraw(); return NUM;}
void GradientFill(int left, int top, int right, int bottom, COLORREF startColor, COLORREF endColor){ // 计算水平和垂直方向上的颜色变化量 double dx = right - left; double dy = bottom - top; double redStep = (GetRValue(endColor) - GetRValue(startColor)) / dx; double greenStep = (GetGValue(endColor) - GetGValue(startColor)) / dx; double blueStep = (GetBValue(endColor) - GetBValue(startColor)) / dx; // 遍历每个像素点,并设置其颜色 for (int x = left; x <= right; x++) { COLORREF currentColor = RGB(GetRValue(startColor) + (x - left) * redStep, GetGValue(startColor) + (x - left) * greenStep, GetBValue(startColor) + (x - left) * blueStep); setfillcolor(currentColor); solidrectangle(x, top, x, bottom); // 填充当前列的颜色 }}
void BeginGame() // 开始游戏{ PegSolitaire game; game.playGame();
}void INstrucTionS() // 游戏介绍{ Graph g; IMAGE img2(rW, rH); SetWorkingImage(&img2); g.draw(); DrawButton(button1, WHITE); DrawButton(button2, WHITE); DrawButton(button3, WHITE); DrawKMQY(1700, 690, 400); SetWorkingImage();
if (!WINDOWS(_T("详细玩法,见微信公众号:C语言研究"), img2, _T("确定"), _T("返回"))) { system("start https://mp.weixin.qq.com/s/5Nj-Que4TVK0LWi44alrFg"); }
}bool EXIT() // 退出{ Graph g; IMAGE img2(rW, rH); SetWorkingImage(&img2); g.draw(); DrawButton(button1, WHITE); DrawButton(button2, WHITE); DrawButton(button3, WHITE); DrawKMQY(1700, 690, 400); SetWorkingImage(); return WINDOWS(_T("确定退出至桌面吗?"), img2, _T("确定"), _T("取消"));}
void DrawCircle(TCHAR str[25], int variable, int fors, int R, double Radian, int Mx, int My){ settextcolor(variable ? HSLtoRGB((360.f / fors) * variable, 1, 0.5f) : WHITE); double a, x0, y0, w, h, x1, y1; int x, y; settextstyle(22, 0, L"微软雅黑", variable * 3600 / fors, variable * 3600 / fors, 0, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH);
a = (fors == 60) ? ((variable + 1) * PI * 2 / fors - Radian) : (variable * PI * 2 / fors); // 计算字符串弧度 a
w = textwidth(str); // 计算字符串宽 w、高 h h = textheight(str);
x1 = R * cos(a); // 计算输出字符串的左上角位置 y1 = R * sin(a);
x0 = x1 * cos(-a) - y1 * sin(-a); // 将字符串绕原点顺时针旋转 a 弧度 y0 = y1 * cos(-a) + x1 * sin(-a);
x0 -= w / 2; // 将字符串向左上偏移 w/2、h/2 y0 += h / 2; // 绘图坐标向下为正
x = (int)(x0 * cos(a) - y0 * sin(a)); // 将字符串绕原点逆时针旋转 a 弧度 y = (int)(y0 * cos(a) + x0 * sin(a));
outtextxy(int(Mx + x + 0.5), int(My - y + 0.5), str); // 绘图坐标向下为正}
void INITCircle(){ for (int i = 0; i < 3; i++) { TC[i].R = (i + 1) * 50 + rH / 2 - 200; TC[i].Radian = 0; TC[i].NextTime = 0;
switch (i) { case 0:TC[i].fors = 24; break; // 时 case 1:TC[i].fors = 60; break; // 分 case 2:TC[i].fors = 60; break; // 秒 } }}
void MYCIRCLE(int x, int y){ GetLocalTime(&ti); for (int j = 0; j < 3; j++) { if (TC[1].NextTime != ti.wMinute) { TC[1].NextTime = ti.wMinute; TC[1].Radian = 0; } else { TC[1].Radian = TC[1].Radian + (2 * PI / TC[1].fors - TC[1].Radian) / 10; }
if (TC[2].NextTime != ti.wSecond) { TC[2].NextTime = ti.wSecond; TC[2].Radian = 0; } else { TC[2].Radian = TC[2].Radian + (2 * PI / TC[2].fors - TC[2].Radian) / 10; }
for (int i = 0; i < TC[j].fors; i++) { switch (j) { case 0: _stprintf_s(str, _T("%d时"), (i + ti.wHour) % TC[j].fors); break; case 1: _stprintf_s(str, _T("%d分"), (i + ti.wMinute) % TC[j].fors); break; case 2: _stprintf_s(str, _T("%d秒"), (i + ti.wSecond) % TC[j].fors); break; } DrawCircle(str, i, TC[j].fors, TC[j].R, TC[j].Radian, x, y);; } } settextstyle(22, 0, L"微软雅黑", 0, 0, 0, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH);}
void HideSphere(float R, int alfa, int beta, int HideFlag, int X, int Y, COLORREF color){ setlinecolor(color); int i, j, k; float x[4], y[4], z[4], x1[4], y1[4], z1[4], sx[4], sy[4]; double a1, a2, b1, b2, c, d, xn, yn, zn, vn; c = alfa * PI / 180.0; d = beta * PI / 180.0; for (j = 0; j < 180; j = j + 20) { a1 = j * PI / 180.0; a2 = (j + 20) * PI / 180.0; for (i = 0; i < 360; i = i + 20) { b1 = i * PI / 180.0; b2 = (i + 20) * PI / 180.0; x[0] = R * sin(a1) * cos(b1); y[0] = R * sin(a1) * sin(b1); z[0] = R * cos(a1); x[1] = R * sin(a2) * cos(b1); y[1] = R * sin(a2) * sin(b1); z[1] = R * cos(a2); x[2] = R * sin(a2) * cos(b2); y[2] = R * sin(a2) * sin(b2); z[2] = R * cos(a2); x[3] = R * sin(a1) * cos(b2); y[3] = R * sin(a1) * sin(b2); z[3] = R * cos(a1); for (k = 0; k < 4; k++) { x1[k] = x[k] * cos(c) - y[k] * sin(c); y1[k] = x[k] * sin(c) * cos(d) + y[k] * cos(c) * sin(d) + z[k] * sin(d); z1[k] = -x[k] * sin(c) * sin(d) - y[k] * cos(c) * sin(d) + z[k] * cos(d); sx[k] = X - x1[k]; sy[k] = Y - z1[k]; } xn = (y1[2] - y1[0]) * (z1[3] - z1[1]) - (y1[3] - y1[1]) * (z1[2] - z1[0]); yn = -(x1[2] - x1[0]) * (z1[3] - z1[1]) + (x1[3] - x1[1]) * (z1[2] - z1[0]); zn = (x1[2] - x1[0]) * (y1[3] - y1[1]) - (x1[3] - x1[1]) * (y1[2] - y1[0]); vn = sqrt(xn * xn + yn * yn + zn * zn); xn = xn / vn; yn = yn / vn; zn = zn / vn; if (!HideFlag || yn >= 0.0) { moveto(sx[0], sy[0]); lineto(sx[1], sy[1]); lineto(sx[2], sy[2]); lineto(sx[3], sy[3]); lineto(sx[0], sy[0]); } } }}
// 将图片进行模糊处理void Blur(IMAGE* pimg){ DWORD* pMem = GetImageBuffer(pimg);
int r, g, b; int m; int num = 0; int width = pimg->getwidth(); int height = pimg->getheight(); int step = width - 1, f, t;

// 计算 9 格方向系数 int cell[9] = { -(width + 1), -1, width - 1, -width, 0, width, -(width - 1), 1, width + 1 };
// 逐个像素点读取计算 for (int i = width * height - 1; i >= 0; i--) { // 重置变量 r = g = b = 0; m = 9;
// 根据像素位置,决定周围格子的计算范围 if (step == width - 1) t = 5, m = 6; else t = 8;
if (step == 0) f = 3, m = 6, step = width; else f = 0;
step--;
// 累加周围格子的颜色值 for (int n = f; n <= t; n++) { // 位置定位 num = i + cell[n];
// 判断位置值是否越界 if (num < 0 || num >= width * height) m--; // 统计越界像素数 else { // 累加颜色值 r += GetRValue(pMem[num]); g += GetGValue(pMem[num]); b += GetBValue(pMem[num]); } } // 将平均值赋值该像素 pMem[i] = RGB(r / m, g / m, b / m); }}
void DrawButtonT(Button button, COLORREF color){ setbkmode(TRANSPARENT); RECT r = { button.X1, button.Y1, button.X2, button.Y2 }; if (color == WHITE) { settextcolor(color); setfillcolor(RGB(30, 30, 30)); setlinecolor(RGB(20, 20, 20)); fillrectangle(button.X1, button.Y1, button.X2, button.Y2); } else { GradientFill(button.X1, button.Y1, button.X2, button.Y2, WHITE, BLACK); settextcolor(color); }
LOGFONT f; gettextstyle(&f); // 获取当前字体设置 f.lfHeight = button.size; // 设置字体高度为 _tcscpy_s(f.lfFaceName, _T("楷体")); // 设置字体为“黑体”(高版本 VC 推荐使用 _tcscpy_s 函数) f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿 settextstyle(&f); // 设置字体样式 drawtext(button.str, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);}
void DrawMouse(int x, int y) // 绘制鼠标按键{ setfillcolor(RGB(107, 107, 107)); solidellipse(x, y, x + 30, y + 40); setfillcolor(RGB(17, 17, 17)); // 扇形 solidpie(x + 1, y + 1, x + 30 - 1, y + 40 - 1, 0, 1.57); settextcolor(RGB(107, 107, 107)); LOGFONT f; gettextstyle(&f); // 获取当前字体设置 f.lfHeight = 40; // 设置字体高度为 _tcscpy_s(f.lfFaceName, _T("楷体")); // 设置字体为“黑体”(高版本 VC 推荐使用 _tcscpy_s 函数) f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿 settextstyle(&f); // 设置字体样式 outtextxy(x + 45, y, _T("退出"));}
void DrawR(int x, int y) // 绘制撤销{ setfillcolor(RGB(107, 107, 107)); solidroundrect(x + 5, y + 5, x + 35, y + 35, 5, 5); settextcolor(RGB(17, 17, 17)); settextstyle(30, 0, _T("宋体")); RECT r = { x, y, x + 40, y + 40 }; drawtext(_T("R"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); settextcolor(RGB(107, 107, 107)); LOGFONT f; gettextstyle(&f); // 获取当前字体设置 f.lfHeight = 40; // 设置字体高度为 _tcscpy_s(f.lfFaceName, _T("楷体")); // 设置字体为“黑体”(高版本 VC 推荐使用 _tcscpy_s 函数) f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿 settextstyle(&f); // 设置字体样式 outtextxy(x + 45, y, _T("撤回"));}
bool WINDOWS(LPCTSTR str, IMAGE img, LPCTSTR str1, LPCTSTR str2){ for (int m = 0; m < 10; m++) { Blur(&img); // 显示处理后的图像 } // 绘制窗口按钮等,开始选择 setfillcolor(RGB(20, 20, 20)); solidrectangle(744, 535, 1810, 815);
LOGFONT f; gettextstyle(&f); // 获取当前字体设置 f.lfHeight = 40; // 设置字体高度为 _tcscpy_s(f.lfFaceName, _T("楷体")); // 设置字体为“黑体”(高版本 VC 推荐使用 _tcscpy_s 函数) f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿 settextstyle(&f); // 设置字体样式 RECT r = { 744,535,1810,815 }; drawtext(str, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); Button button4 = { 744, 815, 744 + 533, 815 + 65, 40, str1 }; Button button5 = { 744 + 533, 815, 1810,815 + 65, 40,str2 }; DrawButtonT(button4, WHITE); DrawButtonT(button5, WHITE); ExMessage msg; BeginBatchDraw(); bool T = true; bool R = true; int NUMS = 0; // 游戏的主循环 while (T) { FlushBatchDraw(); cleardevice(); putimage(0, 0, &img); setfillcolor(RGB(20, 20, 20)); solidrectangle(744, 535, 1810, 815); drawtext(str, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); DrawButtonT(button4, WHITE); DrawButtonT(button5, WHITE); while (peekmessage(&msg, EX_MOUSE)) // 如果获取到了消息就执行 { switch (msg.message) { case WM_MOUSEMOVE: if (msg.x > button4.X1 && msg.x<button4.X2 && msg.y>button4.Y1 && msg.y < button4.Y2) { NUMS = 1; } if (msg.x > button5.X1 && msg.x<button5.X2 && msg.y>button5.Y1 && msg.y < button5.Y2) { NUMS = 2; } break; case WM_LBUTTONDOWN: if (msg.x > button4.X1 && msg.x<button4.X2 && msg.y>button4.Y1 && msg.y < button4.Y2) { NUMS = 3; } if (msg.x > button5.X1 && msg.x<button5.X2 && msg.y>button5.Y1 && msg.y < button5.Y2) { NUMS = 4; } break; } }
switch (NUMS) { case 0:break; case 1: DrawButtonT(button4, BLACK); DrawButtonT(button5, WHITE); break; case 2: DrawButtonT(button5, BLACK); DrawButtonT(button4, WHITE); break; case 3: T = false; R = false; // 确定退出 break; case 4: T = false; R = true; break; // 开始游戏 } Sleep(20); } EndBatchDraw(); return R;}

结语

我猜你翻到最后,或许并非因为深入理解了代码的逻辑,而是对代码的总行数产生了浓厚的兴趣吧。整整1286行代码,每一行都至关重要,因为即便是错了一个微不足道的标点符号,也可能导致整个程序无法顺利运行。(想玩这个游戏但是感觉自己编译困难的同学,可以后台私信我,我给你发送已编译成功的软件)
前段时间,我再次重温了《士兵突击》这部电视剧,它总能在我迷茫时给予我力量。剧中有一句话让我至今难以忘怀:“要做有意义的事,有意义的事就是好好活,好好活就是做有意义的事。
对我而言,编写代码正是一件充满意义的事情。它不仅能助我成长,还能为初学者提供帮助,甚至激发他们的灵感。每当看到别人因我的代码而点赞,给予我肯定时,我都深感这一切的努力都是值得的,因为这正是我所追求的有意义之事。

C语言研究
写给自己的笔记,时常写写,时常看看,仅此而已。