精品熟人妻一区二区三区四区不卡-精品爽黄69天堂a-精品水蜜桃久久久久久久-精品丝袜国产自在线拍-精品丝袜国产自在线拍a-精品丝袜国产自在线拍免费看

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

中國象棋小游戲(C版)

freeflydom
2025年6月2日 9:25 本文熱度 305

! 此文僅展示此游戲的最簡單版本,可以實現中國象棋雙人對戰的基本功能。更多功能體驗可訪問上方鏈接。

說明:

  • #include<graphics.h> 一個在 C/C++ 中用于圖形編程的頭文件,主要用于創建和操作圖形界面。具有繪制圖形、設置顏色、鼠標和鍵盤時間處理等功能。
  • #include<conio.h> 提供了對控制臺輸入/輸出的簡單操作,如字符讀取和屏幕刷新。
  • #include<windows.h> 包含了 Windows API 的各種函數和數據結構定義,允許程序直接調用 Windows 操作系統的功能。可用于窗口創建與管理、進程和線程操作、文件操作、系統信息獲取等。
  • #include<mmsystem.h> 提供了多媒體功能,包括音頻和定時器功能。同時需要鏈接winmm.lib庫,通過#pragma comment(lib,"winmm.lib")實現

整體思路

  1. 創建圖形窗口,繪制中國象棋棋局圖案
  2. 定義棋子,并在棋局上繪制初始化棋子
  3. 實現游戲控制功能。即可通過鼠標操作實現棋子的移動以及紅黑雙方的交換操作
  4. 添加棋子的走法規則,將軍的判定以及勝負判定
  5. 添加背景音樂、走棋音效等
  6. 添加紅黑雙方計時功能
  7. 打包軟件

實現過程

  1. 創建圖形窗口,繪制中國象棋棋局圖案。

    我們先看此步驟完成后的結果。

    只要對graphics.h有所了解,創建圖形窗口并不困難。

    我們首先在主函數中直接初始化圖形窗口。

    #include<stdio.h>
    #include<graphics.h>
    int main(){
    	initgraph(800,800);
    }
    

    接下來我們需要定義一個繪制游戲的函數。首先我們可以定義一個行數、列數、間隔以及棋盤格子大小

    #define INTERVAL 50 // 定義間隔
    #define CHESS_GRID_SIZE 70 // 定義棋盤格子大小
    #define ROW 10 // 定義行數
    #define COL 9 // 定義列數
    

    接下來繪制棋局的格子

    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);
    }
    

    最后畫米字完成第一步。

    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);  
    }
    
  2. 定義棋子,并在棋局上繪制初始化棋子

    我們先看此步驟完成后的結果。

根據中國象棋游戲規則,棋子分為紅黑雙方,不同棋子有不同的走法和不同的過河標準。

于是我們首先需要定義紅黑雙方的棋子名稱,這里使用兩個指向常量的指針數組分別代表紅方棋子和黑方棋子。(用指針數組存儲每個棋子名稱字符串的起始地址,且是只讀模式。更靈活高效且易于修改和維護)

const char* redChess[7] = { "車","馬","相","仕","帥","炮","兵" };	// 紅方棋子
const char* blackChess[7] = { "車","馬","象","士","將","砲","卒" };	// 黑方棋子

每個棋子的位置由橫縱坐標確定,且每個棋子都有名稱、坐標、紅黑方、是否過河等屬性。于是我們定義一個棋子結構體如下:

//定義棋子結構體
struct Chess {
	char name[4];	// 棋子名稱一個漢字
	int x;	// 棋子x坐標
	int y;	// 棋子y坐標
	char type;	// 棋子類型(紅方還是黑方)
	bool flag;	// 棋子是否過河;
}map[ROW][COL];

接下來就需要對結構體進行初始化,在相應的位置放入相應的棋子。

//游戲初始化
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;
		}
	}
}

最后,我們在繪制圖形函數中繼續添加關于繪制棋子的代碼,同時這里以圓圈代表一個棋子。

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);
			}
		}
	}   
}
  1. 實現游戲控制功能。即可通過鼠標操作實現棋子的移動以及紅黑雙方的交換操作。

    我們先看此步驟完成后的結果。

    在實現此功能前我們需要了解#include<windows.h>頭文件。

    • MOUSEMSG:是該頭文件下的一個結構體,有以下成員
    成員變量類型描述
    uMsguint鼠標消息類型,例如 WM_MOUSEMOVE(鼠標移動)、WM_LBUTTONDOWN(左按下鍵)、WM_RBUTTONDOWN(右鍵按下)等。
    xint鼠標事件發生時的 X 坐標。
    yint鼠標事件發生時的 Y 坐標。
    timeuint鼠標事件發生時的時間戳。
    dwExtraInfouint額外信息,通常用于區分相同消息的不同實例。

    我們首先做出如下定義:

    POINT begin = { -1,-1 }, end = { -1,-1 };	// 記錄前后兩次點擊的坐標
    MOUSEMSG msg;
    bool isRedTurn = true; // 紅方先手
    

    接下來實現游戲控制功能。

    首先是檢測鼠標的點擊

    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;
    }
    

    完整的函數如下:

    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;	//重置選擇
    			}
    		}
    	}
    }
    

    最后,我們繼續在繪制函數中添加對棋子的選中效果

    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);
        }    
    }
    
  2. 添加棋子的走法規則,將軍的判定以及勝負判定

    這一步是整個象棋游戲的關鍵步驟。我們需要定義一個檢驗函數,對每一個棋子進行走法檢驗。

    首先定義該檢驗函數:

    bool CheckMove(int fromI, int fromK, int toI, int toK) {	}
    
    • 第一步需要規定禁止吃己方棋子:

      這個不能實現,只需要當判斷目標位置存在棋子且該棋子的陣營屬性和移動的棋子相同,則禁止移動即可。

      struct Chess fromChess = map[fromI][fromK];
      struct Chess toChess = map[toI][toK];
      if (toChess.type == fromChess.type && strcmp(toChess.name, "") != 0)
          return false;
      
    • 第二步檢驗車的走法

      • 車的走法就是必須橫向或縱向移動(fromI == toI 或 fromK == toK
      • 路徑上所經過的格子都必須為空即可。
      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;
      }
      
    • 第三步檢驗馬的走法

      • 馬的移動方式是日字格(橫向1格,縱向2格,或橫向2格,縱向1格)。
      • 判斷其移動是否存在蹩馬腳的情況(即相鄰格子必須為空)
      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;
      }
      
    • 第四步檢驗象的走法

      • 象的移動是田字格(橫向和縱向均移動2格)
      • 田字中間必須為空。
      • 象無法過河。
      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;
      }
      
    • 第五步檢驗士的走法

      • 士在己方九宮格內移動
      • 每次只能斜向移動一格(橫向和縱向均移動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);
      }
      
    • 第六步檢驗將的走法

      • 只能在己方九宮格內移動
      • 每次只能橫向或縱向移動一格
      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;
      }
      
    • 第七步檢驗炮的走法

      • 必須直線移動
      • 若目標為空,路徑上不能有任何子
      • 若目標位敵方棋子,路徑上必須恰好有一個棋子
      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);
          }
      }
      
    • 第八步檢驗兵的走法

      • 未過河
        • 只能向前移動一格。
        • 過河后更新標志位。
      • 已過河
        • 可向前或橫向移動一格。
      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()函數插入游戲控制函數中,當且僅當移動合法時可對棋子進行移動。

      接下來定義一個將軍狀態檢測函數。即當一方走棋后,檢驗接下來走棋一方是否處于被將軍狀態。

      // 檢查將帥是否被攻擊
      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;
      }
      

      接下來我們進行勝利條件判斷。

      當將帥面對面或者將帥被吃時,游戲結束。

      // 勝利條件判斷
      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(), "黑方勝利!", "游戲結束", MB_OK);
      		return true;
      	}
      	if (!blackExist) {
      		MessageBox(GetHWnd(), "紅方勝利!", "游戲結束", 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 勝利!將帥對面!", winner);
      			MessageBox(GetHWnd(), message, "游戲結束", MB_OK);
      			return true;
      		}
      	}
      	return false;
      }
      

      綜上,我們將這些函數插入游戲控制函數內即可。

  3. 添加背景音樂、走棋音效等

    這一步很簡單,直接給出代碼。

    //定義音效資源
    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);
    	// 設置主音量(范圍0-1000)
    	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);
    }
    
  4. 添加紅黑雙方計時功能

    我們先看此步驟完成后的結果。

    這里我們只是實現最簡單的計時功能,即雙方步時60秒,總時長10分鐘。

    首先我們需要定義時間結構體包括總時間和當前步時。

    //定義時間結構
    struct Timer {
    	int totalTime;	//總剩余時間(秒)
    	int stepTime;	//當前步剩余時間(秒)
    }redTimer, blackTimer;
    DWORD lastUpdateTime = 0;  // 記錄上次更新時間戳(毫秒)
    const int INIT_TOTAL_TIME = 600; // 初始總時間(10分鐘)
    const int INIT_STEP_TIME = 60;   // 初始步時(60秒)
    

    接著我們在游戲初始化函數中添加初始化計時器。

    void GameInit(){
    	// 初始化計時器
    	redTimer.totalTime = INIT_TOTAL_TIME;
    	redTimer.stepTime = INIT_STEP_TIME;
    	blackTimer.totalTime = INIT_TOTAL_TIME;
    	blackTimer.stepTime = INIT_STEP_TIME;
    	lastUpdateTime = GetTickCount(); // 獲取當前系統時間    
    }
    

    在游戲控制函數中添加雙方交換時步時的重置

    void GameControl(){
    	// 重置當前玩家的步時
        if (isRedTurn) {
        	redTimer.stepTime = INIT_STEP_TIME;
        }
        else {
        	blackTimer.stepTime = INIT_STEP_TIME;
        }  
    }
    

    然后我們在游戲繪制函數中將時間顯示在棋盤右側。

    void GameGraw(){
    	//設置文字樣式
    	settextstyle(20, 0, "楷體");
    	settextcolor(BLACK);
    	// 繪制計時器
    	settextstyle(20, 0, "楷體");
    	settextcolor(BLACK);
    	char redTimeStr1[50], redTimeStr2[50],blackTimeStr1[50], blackTimeStr2[50];
    	// 格式:總時間 MM:SS 步時 SS
    	sprintf(redTimeStr1, "紅方: 總 %02d:%02d",
    		redTimer.totalTime / 60, redTimer.totalTime % 60);
    	sprintf(redTimeStr2, "    步時: %02d",redTimer.stepTime);
    	sprintf(blackTimeStr1, "黑方: 總 %02d:%02d",
    		blackTimer.totalTime / 60, blackTimer.totalTime % 60);
    	sprintf(blackTimeStr2, "    步時: %02d", blackTimer.stepTime);
    	outtextxy(650, 650, redTimeStr1);
    	outtextxy(650, 675, redTimeStr2);
    	outtextxy(650, 50, blackTimeStr1);
    	outtextxy(650, 75, blackTimeStr2);
    }
    

    最后,在主函數中實現對時間的更新。

    再結合之前的函數,我們寫出主函數即可實現中國象棋的最簡化版。

    int main() {
    	initgraph(800, 800, SHOWCONSOLE);	// 初始化圖形窗口
    	SoundInit();	// 初始化音效
    	PlayBGM();	// 播放背景音樂
    	GameInit();	// 初始化游戲
    	while (true) {
    		GameControl();  // 觸發時間扣除
    		// 更新時間邏輯(每秒更新一次)
    		DWORD currentTime = GetTickCount();
    		DWORD elapsed = currentTime - lastUpdateTime;
    		if (elapsed >= 1000) { // 超過1秒
    			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(), "紅方超時,黑方勝利!", "游戲結束", MB_OK);
    				exit(0);
    			}
    			if (blackTimer.totalTime <= 0 || blackTimer.stepTime <= 0) {
    				MessageBox(GetHWnd(), "黑方超時,紅方勝利!", "游戲結束", MB_OK);
    				exit(0);
    			}
    		}
    		// 雙緩沖繪制流程
    		BeginBatchDraw();  // 開始批量繪制
    		GameDraw();
    		EndBatchDraw();    // 結束批量繪制
    	}
    	EndBatchDraw();	// 結束繪制
    	closegraph();	// 關閉圖形窗口
    	mciSendString("close all", NULL, 0, NULL); // 關閉所有音頻
    	return 0;
    }
    

    到這里中國象棋的基本功能我們就實現了。由于這是本作者的第一個項目有點小激動,于是準備先進性項目打包處理。

這里使用傳統打包方式,使用Microsoft Visual Studio Installer Projects打包。

  1. 安裝擴展插件
    • 在VS中安裝Microsoft Visual Studio Installer Projects擴展
    • 操作路徑:擴展 > 管理擴展 > 搜索安裝 > 重啟VS
  2. 創建安裝項目
    • 右鍵解決方案>添加>新建項目
    • 搜索選擇"setup Project"模板
    • 配置項目名稱
  3. 配置安裝內容
    • 主程序添加:右鍵Application Folder>Add>項目輸出>選擇主輸出
    • 資源文件處理:將"sound"音效文件夾拖入Application Folder(確保所有依賴的DLL被包含)
  4. 配置快捷方式
    • 右鍵Application>Add>文件>選擇所需的快捷方式圖案.ico文件(圖片轉換成.ico格式可以通過PS轉換,需要提前配置插件)
    • 右鍵主輸出>創建快捷方式
    • 將快捷方式拖入User's Desktop和User's Programs Menu
    • 右鍵快捷方式>屬性窗口>Icon>Browse>Browse>選擇Application Folder文件中的ico文件>OK
  5. 處理依賴項
    • 右鍵Application Folder>Add>文件>找到vcredist_x64.exe(路徑:VS安裝目錄\VC\Redist\MSVC\版本號)
  6. 生成安裝包
    • 右鍵安裝項目>生成
    • 在輸出目錄即可獲取Setup.exe和.msi文件

轉自https://www.cnblogs.com/Yygz314/p/18899387


該文章在 2025/6/2 9:25:04 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved

主站蜘蛛池模板: 国产精品福利在线观看免费不卡 | 午夜尤物禁止18点击进入 | 中文字幕亚洲一区二区三区 | 国产精品一区在线观看播放 | 精品人妻av无码系列 | 国产片免费福利片永久不卡 | 国产精品亚洲午夜一区二区三区 | 午夜性a一级毛片免费一级黄色毛片 | 欧美色欧美亚洲高清在线视频 | 中文字幕av日韩精品一区二区 | 精品国产成人国产在线观看 | 国产精品自拍视频合集 | 无码人妻精品一区二区蜜桃91 | 99久久国产综合精品女同 | 精品91专区视频在线 | 黑人与中国少妇xxxx视频 | 亚洲v无码v吞精久久 | 91福利国产在线观看香蕉 | av无码不卡一区二区三区 | 国产精品视频自拍 | a级毛片免费播放 | 国产熟妇无码a片aaa毛片视频 | 亚洲日本乱人伦中文字幕 | av天堂精品久久久久 | 亚洲欧美日韩久久精品黄色片 | 熟妇熟女乱妇乱女网站 | 免费无码鲁丝片一区二区 | 欧美日韩国产一区二区精品合集 | 日日噜噜夜夜狠狠久久丁香五月 | 丝兔女郎m开腿sm调教室 | 国产伦理片在线播放av一区 | 白丝初音未来被调教出奶水 | 无码国产福利av私拍 | 国内精品中文 | 亚洲成人在线一区二区 | 91久久精品一区二区三区 | 国产精品不卡a∨在线 | 性少妇freesexvi | 欧美性公交xxxxx | 国产亚洲国产av网站在j | 欧美日韩精品一区二区三区高清视频 |