中國(guó)象棋小游戲(C版)
|
freeflydom
2025年6月2日 9:25
本文熱度 297
|
! 此文僅展示此游戲的最簡(jiǎn)單版本,可以實(shí)現(xiàn)中國(guó)象棋雙人對(duì)戰(zhàn)的基本功能。更多功能體驗(yàn)可訪問(wèn)上方鏈接。
說(shuō)明: #include<graphics.h> 一個(gè)在 C/C++ 中用于圖形編程的頭文件,主要用于創(chuàng)建和操作圖形界面。具有繪制圖形、設(shè)置顏色、鼠標(biāo)和鍵盤時(shí)間處理等功能。#include<conio.h> 提供了對(duì)控制臺(tái)輸入/輸出的簡(jiǎn)單操作,如字符讀取和屏幕刷新。#include<windows.h> 包含了 Windows API 的各種函數(shù)和數(shù)據(jù)結(jié)構(gòu)定義,允許程序直接調(diào)用 Windows 操作系統(tǒng)的功能。可用于窗口創(chuàng)建與管理、進(jìn)程和線程操作、文件操作、系統(tǒng)信息獲取等。#include<mmsystem.h> 提供了多媒體功能,包括音頻和定時(shí)器功能。同時(shí)需要鏈接winmm.lib 庫(kù),通過(guò)#pragma comment(lib,"winmm.lib") 實(shí)現(xiàn)
整體思路- 創(chuàng)建圖形窗口,繪制中國(guó)象棋棋局圖案
- 定義棋子,并在棋局上繪制初始化棋子
- 實(shí)現(xiàn)游戲控制功能。即可通過(guò)鼠標(biāo)操作實(shí)現(xiàn)棋子的移動(dòng)以及紅黑雙方的交換操作
- 添加棋子的走法規(guī)則,將軍的判定以及勝負(fù)判定
- 添加背景音樂(lè)、走棋音效等
- 添加紅黑雙方計(jì)時(shí)功能
- 打包軟件
實(shí)現(xiàn)過(guò)程創(chuàng)建圖形窗口,繪制中國(guó)象棋棋局圖案。
我們先看此步驟完成后的結(jié)果。

只要對(duì)graphics.h 有所了解,創(chuàng)建圖形窗口并不困難。
我們首先在主函數(shù)中直接初始化圖形窗口。
#include<stdio.h>
#include<graphics.h>
int main(){
initgraph(800,800);
}
接下來(lái)我們需要定義一個(gè)繪制游戲的函數(shù)。首先我們可以定義一個(gè)行數(shù)、列數(shù)、間隔以及棋盤格子大小
#define INTERVAL 50
#define CHESS_GRID_SIZE 70
#define ROW 10
#define COL 9
接下來(lái)繪制棋局的格子
void GameDraw() {
setbkcolor(RGB(252, 215, 162));
cleardevice();
setlinecolor(BLACK);
setlinestyle(PS_SOLID, 2);
setfillcolor(RGB(252, 215, 162));
fillrectangle(INTERVAL - 5, INTERVAL - 5, CHESS_GRID_SIZE * 8 + INTERVAL + 5, CHESS_GRID_SIZE * 9 + INTERVAL + 5);
for (int i = 0;i < 10;i++) {
line(INTERVAL, i * CHESS_GRID_SIZE + INTERVAL, CHESS_GRID_SIZE * 8 + INTERVAL, i * CHESS_GRID_SIZE + INTERVAL);
if (i < 9) {
line(i * CHESS_GRID_SIZE + INTERVAL, INTERVAL, i * CHESS_GRID_SIZE + INTERVAL, 9 * CHESS_GRID_SIZE + INTERVAL);
}
}
}
接著顯示“楚河漢界”文字
void GameDraw(){
fillrectangle(INTERVAL, 4 * CHESS_GRID_SIZE + INTERVAL, 8 * CHESS_GRID_SIZE + INTERVAL, 5 * CHESS_GRID_SIZE + INTERVAL);
settextcolor(BLACK);
settextstyle(50, 0, "楷體");
char river[25] = "楚 河 漢 界";
int twidth = textwidth(river);
int theight = textheight(river);
twidth = (8 * CHESS_GRID_SIZE - twidth) / 2;
theight = (CHESS_GRID_SIZE - theight) / 2;
outtextxy(INTERVAL + twidth, 4 * CHESS_GRID_SIZE + theight + INTERVAL, river);
}
最后畫(huà)米字完成第一步。
void GameDraw(){
line(3 * CHESS_GRID_SIZE + INTERVAL, INTERVAL, 5 * CHESS_GRID_SIZE + INTERVAL, 2 * CHESS_GRID_SIZE + INTERVAL);
line(5 * CHESS_GRID_SIZE + INTERVAL, INTERVAL, 3 * CHESS_GRID_SIZE + INTERVAL, 2 * CHESS_GRID_SIZE + INTERVAL);
line(3 * CHESS_GRID_SIZE + INTERVAL, 7 * CHESS_GRID_SIZE + INTERVAL, 5 * CHESS_GRID_SIZE + INTERVAL, 9 * CHESS_GRID_SIZE + INTERVAL);
line(5 * CHESS_GRID_SIZE + INTERVAL, 7 * CHESS_GRID_SIZE + INTERVAL, 3 * CHESS_GRID_SIZE + INTERVAL, 9 * CHESS_GRID_SIZE + INTERVAL);
}
定義棋子,并在棋局上繪制初始化棋子
我們先看此步驟完成后的結(jié)果。

根據(jù)中國(guó)象棋游戲規(guī)則,棋子分為紅黑雙方,不同棋子有不同的走法和不同的過(guò)河標(biāo)準(zhǔn)。
于是我們首先需要定義紅黑雙方的棋子名稱,這里使用兩個(gè)指向常量的指針數(shù)組分別代表紅方棋子和黑方棋子。(用指針數(shù)組存儲(chǔ)每個(gè)棋子名稱字符串的起始地址,且是只讀模式。更靈活高效且易于修改和維護(hù))
const char* redChess[7] = { "車","馬","相","仕","帥","炮","兵" };
const char* blackChess[7] = { "車","馬","象","士","將","砲","卒" };
每個(gè)棋子的位置由橫縱坐標(biāo)確定,且每個(gè)棋子都有名稱、坐標(biāo)、紅黑方、是否過(guò)河等屬性。于是我們定義一個(gè)棋子結(jié)構(gòu)體如下:
struct Chess {
char name[4];
int x;
int y;
char type;
bool flag;
}map[ROW][COL];
接下來(lái)就需要對(duì)結(jié)構(gòu)體進(jìn)行初始化,在相應(yīng)的位置放入相應(yīng)的棋子。
void GameInit() {
for (int i = 0;i < ROW;i++) {
int temp = 0, temp1 = 0, temp2 = 1;
for (int k = 0;k < COL;k++) {
char chessname[4] = "";
char mcolor = 'B';
if (i <= 4) {
if (i == 0) {
if (temp <= 4) temp++;
else {
temp1 = 4 - temp2;
temp2++;
}
sprintf(chessname, "%s", blackChess[temp1]);
temp1++;
}
if (i == 2 && (k == 1 || k == 7)) {
strcpy(chessname, blackChess[5]);
}
if (i == 3 && (k % 2 == 0)) {
strcpy(chessname, blackChess[6]);
}
}
else {
mcolor = 'R';
if (i == 9) {
if (temp <= 4) temp++;
else {
temp1 = 4 - temp2;
temp2++;
}
sprintf(chessname, "%s", redChess[temp1]);
temp1++;
}
if (i == 7 && (k == 1 || k == 7)) {
strcpy(chessname, redChess[5]);
}
if (i == 6 && (k % 2 == 0)) {
strcpy(chessname, redChess[6]);
}
}
map[i][k].type = mcolor;
strcpy(map[i][k].name, chessname);
map[i][k].flag = false;
map[i][k].x = k * CHESS_GRID_SIZE + INTERVAL;
map[i][k].y = i * CHESS_GRID_SIZE + INTERVAL;
}
}
}
最后,我們?cè)诶L制圖形函數(shù)中繼續(xù)添加關(guān)于繪制棋子的代碼,同時(shí)這里以圓圈代表一個(gè)棋子。
void DameDraw(){
settextstyle(40, 0, "楷體");
for (int i = 0;i < ROW;i++) {
for (int k = 0;k < COL;k++) {
if (strcmp(map[i][k].name, "") != 0) {
if (map[i][k].type == 'B') {
settextcolor(BLACK);
setlinecolor(BLACK);
}
else {
settextcolor(RED);
setlinecolor(RED);
}
fillcircle(map[i][k].x, map[i][k].y, 30);
outtextxy(map[i][k].x - 20, map[i][k].y - 20, map[i][k].name);
}
}
}
}
實(shí)現(xiàn)游戲控制功能。即可通過(guò)鼠標(biāo)操作實(shí)現(xiàn)棋子的移動(dòng)以及紅黑雙方的交換操作。
我們先看此步驟完成后的結(jié)果。

在實(shí)現(xiàn)此功能前我們需要了解#include<windows.h> 頭文件。 MOUSEMSG :是該頭文件下的一個(gè)結(jié)構(gòu)體,有以下成員
成員變量 | 類型 | 描述 |
---|
uMsg | uint | 鼠標(biāo)消息類型,例如 WM_MOUSEMOVE (鼠標(biāo)移動(dòng))、WM_LBUTTONDOWN (左按下鍵)、WM_RBUTTONDOWN (右鍵按下)等。 | x | int | 鼠標(biāo)事件發(fā)生時(shí)的 X 坐標(biāo)。 | y | int | 鼠標(biāo)事件發(fā)生時(shí)的 Y 坐標(biāo)。 | time | uint | 鼠標(biāo)事件發(fā)生時(shí)的時(shí)間戳。 | dwExtraInfo | uint | 額外信息,通常用于區(qū)分相同消息的不同實(shí)例。 |
我們首先做出如下定義:
POINT begin = { -1,-1 }, end = { -1,-1 };
MOUSEMSG msg;
bool isRedTurn = true;
接下來(lái)實(shí)現(xiàn)游戲控制功能。
首先是檢測(cè)鼠標(biāo)的點(diǎn)擊
if (MouseHit) {
msg = GetMouseMsg();
if (msg.uMsg == WM_LBUTTONDOWN) {
}
}
再轉(zhuǎn)換鼠標(biāo)坐標(biāo)為棋盤坐標(biāo)以及檢查點(diǎn)擊位置是否合法
int k = (msg.x - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
int i = (msg.y - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
if (k < 0 || k >= COL || i < 0 || i >= ROW) return;
接下來(lái)是第一次點(diǎn)擊選擇棋子
if (begin.x == -1) {
if ((isRedTurn && map[i][k].type == 'R') || (!isRedTurn && map[i][k].type == 'B')) {
begin.x = k;
begin.y = i;
}
}
最后是第二次點(diǎn)擊移動(dòng)棋子
else {
end.x = k;
end.y = i;
int flagg = 0;
if (strcmp(map[end.y][end.x].name, "") == 0) flagg++;
strcpy(map[end.y][end.x].name, map[begin.y][begin.x].name);
map[end.y][end.x].type = map[begin.y][begin.x].type;
map[end.y][end.x].flag = map[begin.y][begin.x].flag;
strcpy(map[begin.y][begin.x].name, "");
isRedTurn = !isRedTurn;
begin.x = -1;
}
完整的函數(shù)如下:
void GameControl() {
if (MouseHit {
msg = GetMouseMsg();
if (msg.uMsg == WM_LBUTTONDOWN) {
int k = (msg.x - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
int i = (msg.y - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
if (k < 0 || k >= COL || i < 0 || i >= ROW) return;
if (begin.x == -1) {
if ((isRedTurn && map[i][k].type == 'R') || (!isRedTurn && map[i][k].type == 'B')) {
begin.x = k;
begin.y = i;
}
}else {
end.x = k;
end.y = i;
int flagg = 0;
if (strcmp(map[end.y][end.x].name, "") == 0) flagg++;
strcpy(map[end.y][end.x].name, map[begin.y][begin.x].name);
map[end.y][end.x].type = map[begin.y][begin.x].type;
map[end.y][end.x].flag = map[begin.y][begin.x].flag;
strcpy(map[begin.y][begin.x].name, "");
isRedTurn = !isRedTurn;
begin.x = -1;
}
}
}
}
最后,我們繼續(xù)在繪制函數(shù)中添加對(duì)棋子的選中效果
void GameDraw(){
if (i == begin.y && k == begin.x) {
setlinecolor(BLUE);
circle(map[i][k].x, map[i][k].y, 30);
circle(map[i][k].x, map[i][k].y, 32);
}
}
添加棋子的走法規(guī)則,將軍的判定以及勝負(fù)判定
這一步是整個(gè)象棋游戲的關(guān)鍵步驟。我們需要定義一個(gè)檢驗(yàn)函數(shù),對(duì)每一個(gè)棋子進(jìn)行走法檢驗(yàn)。
首先定義該檢驗(yàn)函數(shù):
bool CheckMove(int fromI, int fromK, int toI, int toK) { }
第一步需要規(guī)定禁止吃己方棋子:
這個(gè)不能實(shí)現(xiàn),只需要當(dāng)判斷目標(biāo)位置存在棋子且該棋子的陣營(yíng)屬性和移動(dòng)的棋子相同,則禁止移動(dòng)即可。
struct Chess fromChess = map[fromI][fromK];
struct Chess toChess = map[toI][toK];
if (toChess.type == fromChess.type && strcmp(toChess.name, "") != 0)
return false;
第二步檢驗(yàn)車的走法 - 車的走法就是必須橫向或縱向移動(dòng)(
fromI == toI 或 fromK == toK ) - 路徑上所經(jīng)過(guò)的格子都必須為空即可。
if (strcmp(fromChess.name, "車") == 0 || strcmp(fromChess.name, "車") == 0) {
if (fromI != toI && fromK != toK) return false;
int step = (fromI == toI) ? (toK > fromK ? 1 : -1) : (toI > fromI ? 1 : -1);
int distance = (fromI == toI) ? abs(toK - fromK) : abs(toI - fromI);
for (int i = 1; i < distance; i++) {
int x = (fromI == toI) ? fromI : fromI + step * i;
int y = (fromI == toI) ? fromK + step * i : fromK;
if (strcmp(map[x][y].name, "") != 0) return false;
}
return true;
}
第三步檢驗(yàn)馬的走法 - 馬的移動(dòng)方式是日字格(橫向1格,縱向2格,或橫向2格,縱向1格)。
- 判斷其移動(dòng)是否存在蹩馬腳的情況(即相鄰格子必須為空)
else if (strcmp(fromChess.name, "馬") == 0 || strcmp(fromChess.name, "馬") == 0) {
int dx = abs(toK - fromK);
int dy = abs(toI - fromI);
if (!((dx == 1 && dy == 2) || (dx == 2 && dy == 1))) return false;
int blockX = fromI + (toI - fromI) / 2;
int blockY = fromK + (toK - fromK) / 2;
if (strcmp(map[blockX][blockY].name, "") != 0) return false;
return true;
}
第四步檢驗(yàn)象的走法 - 象的移動(dòng)是田字格(橫向和縱向均移動(dòng)2格)
- 田字中間必須為空。
- 象無(wú)法過(guò)河。
else if (strcmp(fromChess.name, "相") == 0 || strcmp(fromChess.name, "象") == 0) {
int dx = abs(toK - fromK);
int dy = abs(toI - fromI);
if (dx != 2 || dy != 2) return false;
int centerX = (fromI + toI) / 2;
int centerY = (fromK + toK) / 2;
if (strcmp(map[centerX][centerY].name, "") != 0) return false;
if (fromChess.type == 'B' && toI > 4) return false;
if (fromChess.type == 'R' && toI < 5) return false;
return true;
}
第五步檢驗(yàn)士的走法 - 士在己方九宮格內(nèi)移動(dòng)
- 每次只能斜向移動(dòng)一格(橫向和縱向均移動(dòng)1格)
else if (strcmp(fromChess.name, "仕") == 0 || strcmp(fromChess.name, "士") == 0) {
if (fromChess.type == 'B') {
if (toI > 2 || toK < 3 || toK > 5) return false;
} else {
if (toI < 7 || toK < 3 || toK > 5) return false;
}
return (abs(toK - fromK) == 1 && abs(toI - fromI) == 1);
}
第六步檢驗(yàn)將的走法 - 只能在己方九宮格內(nèi)移動(dòng)
- 每次只能橫向或縱向移動(dòng)一格
else if (strcmp(fromChess.name, "帥") == 0 || strcmp(fromChess.name, "將") == 0) {
if (fromChess.type == 'B') {
if (toI > 2 || toK < 3 || toK > 5) return false;
} else {
if (toI < 7 || toK < 3 || toK > 5) return false;
}
if ((abs(toK - fromK) + abs(toI - fromI)) != 1) return false;
return true;
}
第七步檢驗(yàn)炮的走法 - 必須直線移動(dòng)
- 若目標(biāo)為空,路徑上不能有任何子
- 若目標(biāo)位敵方棋子,路徑上必須恰好有一個(gè)棋子
else if (strcmp(fromChess.name, "砲") == 0 || strcmp(fromChess.name, "炮") == 0) {
if (fromI != toI && fromK != toK) return false;
int count = 0;
int step = (fromI == toI) ? (toK > fromK ? 1 : -1) : (toI > fromI ? 1 : -1);
int distance = (fromI == toI) ? abs(toK - fromK) : abs(toI - fromI);
for (int i = 1; i < distance; i++) {
int x = (fromI == toI) ? fromI : fromI + step * i;
int y = (fromI == toI) ? fromK + step * i : fromK;
if (strcmp(map[x][y].name, "") != 0) count++;
}
if (strcmp(toChess.name, "") == 0) {
return (count == 0);
} else {
return (count == 1);
}
}
第八步檢驗(yàn)兵的走法 - 未過(guò)河
- 只能向前移動(dòng)一格。
- 過(guò)河后更新標(biāo)志位。
- 已過(guò)河
else if (strcmp(fromChess.name, "卒") == 0 || strcmp(fromChess.name, "兵") == 0) {
int direction = (fromChess.type == 'B') ? 1 : -1;
if (!fromChess.flag) {
if (toI != fromI + direction || toK != fromK) return false;
if ((fromChess.type == 'B' && toI >= 5) || (fromChess.type == 'R' && toI <= 4)) {
map[fromI][fromK].flag = true;
}
} else {
bool valid = false;
if (toI == fromI + direction && toK == fromK) valid = true;
if (toI == fromI && abs(toK - fromK) == 1) valid = true;
return valid;
}
return true;
}
綜上,我們將CheckMove() 函數(shù)插入游戲控制函數(shù)中,當(dāng)且僅當(dāng)移動(dòng)合法時(shí)可對(duì)棋子進(jìn)行移動(dòng)。
接下來(lái)定義一個(gè)將軍狀態(tài)檢測(cè)函數(shù)。即當(dāng)一方走棋后,檢驗(yàn)接下來(lái)走棋一方是否處于被將軍狀態(tài)。
bool CheckGeneral() {
POINT generalPos = { -1,1 };
char targetType = isRedTurn ? 'B' : 'R';
const char* generalName = (targetType == 'B') ? "將" : "帥";
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
if (map[i][j].type == targetType &&
strcmp(map[i][j].name, generalName) == 0) {
generalPos.x = j;
generalPos.y = i;
break;
}
}
}
char enemyType = isRedTurn ? 'R' : 'B';
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
if (map[i][j].type == enemyType &&
strcmp(map[i][j].name, "") != 0) {
struct Chess temp = map[generalPos.y][generalPos.x];
strcpy(map[generalPos.y][generalPos.x].name, map[i][j].name);
bool canAttack = CheckMove(i, j, generalPos.y, generalPos.x);
map[generalPos.y][generalPos.x] = temp;
if (canAttack) return true;
}
}
}
return false;
}
接下來(lái)我們進(jìn)行勝利條件判斷。
當(dāng)將帥面對(duì)面或者將帥被吃時(shí),游戲結(jié)束。
bool CheckWin() {
bool redExist = false, blackExist = false;
POINT redGeneral = { -1, -1 }, blackGeneral = { -1, -1 };
for (int i = 0; i < ROW; i++) {
for (int k = 0; k < COL; k++) {
if (strcmp(map[i][k].name, "帥") == 0) {
redExist = true;
redGeneral.x = k;
redGeneral.y = i;
}
if (strcmp(map[i][k].name, "將") == 0) {
blackExist = true;
blackGeneral.x = k;
blackGeneral.y = i;
}
}
}
if (!redExist) {
MessageBox(GetHWnd(), "黑方勝利!", "游戲結(jié)束", MB_OK);
return true;
}
if (!blackExist) {
MessageBox(GetHWnd(), "紅方勝利!", "游戲結(jié)束", MB_OK);
return true;
}
if (redGeneral.x == blackGeneral.x) {
int minY = (redGeneral.y < blackGeneral.y) ? redGeneral.y : blackGeneral.y;
int maxY = (redGeneral.y > blackGeneral.y) ? redGeneral.y : blackGeneral.y;
bool hasBlock = false;
for (int y = minY + 1; y < maxY; y++) {
if (strcmp(map[y][redGeneral.x].name, "") != 0) {
hasBlock = true;
break;
}
}
if (!hasBlock) {
const char* winner = isRedTurn ? "黑方" : "紅方";
char message[50];
sprintf(message, "%s 勝利!將帥對(duì)面!", winner);
MessageBox(GetHWnd(), message, "游戲結(jié)束", MB_OK);
return true;
}
}
return false;
}
綜上,我們將這些函數(shù)插入游戲控制函數(shù)內(nèi)即可。
添加背景音樂(lè)、走棋音效等
這一步很簡(jiǎn)單,直接給出代碼。
MCI_OPEN_PARMS openBGM;
DWORD bgmld;
bool isMusicPlaying = false;
void SoundInit() {
mciSendString("open \"./sounds/bgm1.wav\" type mpegvideo alias bgm", NULL, 0, NULL);
mciSendString("open \"./sounds/click.wav\" alias click", NULL, 0, NULL);
mciSendString("open \"./sounds/move.wav\" alias move", NULL, 0, NULL);
mciSendString("open \"./sounds/eat.wav\" alias eat", NULL, 0, NULL);
mciSendString("open \"./sounds/check.wav\" alias check", NULL, 0, NULL);
}
void PlayBGM() {
mciSendString("play bgm repeat", NULL, 0, NULL);
mciSendString("setaudio bgm volume to 1000", NULL, 0, NULL);
isMusicPlaying = true;
}
void PlaySoundEffect(const char* alias) {
char cmd[50];
sprintf(cmd, "play %s from 0", alias);
mciSendString(cmd, NULL, 0, NULL);
}
添加紅黑雙方計(jì)時(shí)功能
我們先看此步驟完成后的結(jié)果。

這里我們只是實(shí)現(xiàn)最簡(jiǎn)單的計(jì)時(shí)功能,即雙方步時(shí)60秒,總時(shí)長(zhǎng)10分鐘。
首先我們需要定義時(shí)間結(jié)構(gòu)體包括總時(shí)間和當(dāng)前步時(shí)。
struct Timer {
int totalTime;
int stepTime;
}redTimer, blackTimer;
DWORD lastUpdateTime = 0;
const int INIT_TOTAL_TIME = 600;
const int INIT_STEP_TIME = 60;
接著我們?cè)谟螒虺跏蓟瘮?shù)中添加初始化計(jì)時(shí)器。
void GameInit(){
redTimer.totalTime = INIT_TOTAL_TIME;
redTimer.stepTime = INIT_STEP_TIME;
blackTimer.totalTime = INIT_TOTAL_TIME;
blackTimer.stepTime = INIT_STEP_TIME;
lastUpdateTime = GetTickCount();
}
在游戲控制函數(shù)中添加雙方交換時(shí)步時(shí)的重置
void GameControl(){
if (isRedTurn) {
redTimer.stepTime = INIT_STEP_TIME;
}
else {
blackTimer.stepTime = INIT_STEP_TIME;
}
}
然后我們?cè)谟螒蚶L制函數(shù)中將時(shí)間顯示在棋盤右側(cè)。
void GameGraw(){
settextstyle(20, 0, "楷體");
settextcolor(BLACK);
settextstyle(20, 0, "楷體");
settextcolor(BLACK);
char redTimeStr1[50], redTimeStr2[50],blackTimeStr1[50], blackTimeStr2[50];
sprintf(redTimeStr1, "紅方: 總 %02d:%02d",
redTimer.totalTime / 60, redTimer.totalTime % 60);
sprintf(redTimeStr2, " 步時(shí): %02d",redTimer.stepTime);
sprintf(blackTimeStr1, "黑方: 總 %02d:%02d",
blackTimer.totalTime / 60, blackTimer.totalTime % 60);
sprintf(blackTimeStr2, " 步時(shí): %02d", blackTimer.stepTime);
outtextxy(650, 650, redTimeStr1);
outtextxy(650, 675, redTimeStr2);
outtextxy(650, 50, blackTimeStr1);
outtextxy(650, 75, blackTimeStr2);
}
最后,在主函數(shù)中實(shí)現(xiàn)對(duì)時(shí)間的更新。
再結(jié)合之前的函數(shù),我們寫(xiě)出主函數(shù)即可實(shí)現(xiàn)中國(guó)象棋的最簡(jiǎn)化版。
int main() {
initgraph(800, 800, SHOWCONSOLE);
SoundInit();
PlayBGM();
GameInit();
while (true) {
GameControl();
DWORD currentTime = GetTickCount();
DWORD elapsed = currentTime - lastUpdateTime;
if (elapsed >= 1000) {
int seconds = elapsed / 1000;
if (isRedTurn) {
redTimer.totalTime -= seconds;
redTimer.stepTime -= seconds;
}
else {
blackTimer.totalTime -= seconds;
blackTimer.stepTime -= seconds;
}
lastUpdateTime = currentTime;
if (redTimer.totalTime <= 0 || redTimer.stepTime <= 0) {
MessageBox(GetHWnd(), "紅方超時(shí),黑方勝利!", "游戲結(jié)束", MB_OK);
exit(0);
}
if (blackTimer.totalTime <= 0 || blackTimer.stepTime <= 0) {
MessageBox(GetHWnd(), "黑方超時(shí),紅方勝利!", "游戲結(jié)束", MB_OK);
exit(0);
}
}
BeginBatchDraw();
GameDraw();
EndBatchDraw();
}
EndBatchDraw();
closegraph();
mciSendString("close all", NULL, 0, NULL);
return 0;
}
到這里中國(guó)象棋的基本功能我們就實(shí)現(xiàn)了。由于這是本作者的第一個(gè)項(xiàng)目有點(diǎn)小激動(dòng),于是準(zhǔn)備先進(jìn)性項(xiàng)目打包處理。
這里使用傳統(tǒng)打包方式,使用Microsoft Visual Studio Installer Projects打包。 - 安裝擴(kuò)展插件
- 在VS中安裝Microsoft Visual Studio Installer Projects擴(kuò)展
- 操作路徑:擴(kuò)展 > 管理擴(kuò)展 > 搜索安裝 > 重啟VS
- 創(chuàng)建安裝項(xiàng)目
- 右鍵解決方案>添加>新建項(xiàng)目
- 搜索選擇"setup Project"模板
- 配置項(xiàng)目名稱
- 配置安裝內(nèi)容
- 主程序添加:右鍵Application Folder>Add>項(xiàng)目輸出>選擇主輸出
- 資源文件處理:將"sound"音效文件夾拖入Application Folder(確保所有依賴的DLL被包含)
- 配置快捷方式
- 右鍵Application>Add>文件>選擇所需的快捷方式圖案.ico文件(圖片轉(zhuǎn)換成.ico格式可以通過(guò)PS轉(zhuǎn)換,需要提前配置插件)
- 右鍵主輸出>創(chuàng)建快捷方式
- 將快捷方式拖入U(xiǎn)ser's Desktop和User's Programs Menu
- 右鍵快捷方式>屬性窗口>Icon>Browse>Browse>選擇Application Folder文件中的ico文件>OK
- 處理依賴項(xiàng)
- 右鍵Application Folder>Add>文件>找到vcredist_x64.exe(路徑:VS安裝目錄\VC\Redist\MSVC\版本號(hào))
- 生成安裝包
- 右鍵安裝項(xiàng)目>生成
- 在輸出目錄即可獲取Setup.exe和.msi文件
轉(zhuǎn)自https://www.cnblogs.com/Yygz314/p/18899387
該文章在 2025/6/2 9:25:04 編輯過(guò)
|
|