Win32游戏制作之---SwordsMan
Posted Loving_初衷
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Win32游戏制作之---SwordsMan相关的知识,希望对你有一定的参考价值。
前言
今天是NBA总决赛的最后一场,还是抢七大战,伟大的James终于圆梦了,比赛之中突然发现詹姆斯胡子都有点白了,原来原先那些陪伴自己众多篮球明星都已经老了,Kobe也退役了,他们每个人都有梦想并且为之奋斗,詹姆斯为了自己的梦想奋斗了这么多年才圆梦,那么自己也应该努力实现自己心中的梦想,当然只是想实现自己心中的一个小小的梦想而已,不是每个人都可以说出为自己家乡带来总冠军那样的豪言壮语。
---这句话送给伟大的詹姆斯,也送给我自己,更送给所有在梦想道路上前进的人
前段时间由于忙着准备学校的考试,没有时间好好做自己想做的事情,再加上今天早上还有NBA总决赛,所以好久没有写博客,考试考完之后,我就开始了自己做了一个小游戏---SwordsMan,当然这只是我给取得一个名字,并不是那个SwordsMan,和真正的SwordsMan游戏还是有很大的差距的。
游戏之中的场景图是我用Photoshop拼接而成,自己也感觉做的不好,大家看看就好。游戏中的小鸟以及Boss小龙也是用Photoshop制作的,下面来看一下实际的效果图:
游戏初始状态:
游戏技能释放:
每次用技能击中小鸟,会获得积分,当积分达到100的时候,Boss就会出现,如图:
当然Boss会释放技能,来看一下:
Boss的技能是一组动态图,范围是以它为中心的150个像素之内,当然,Boss会先判断玩家的位置,然后在释放技能,不过这里只是初步判断一下,人工智能是一个很大的话题。现在总算明白为什么为什么AI难以设计了,以前还总是嫌弃AI不行,比如我在玩三国志系列游戏的时候总是觉得AI不够智能,现在总算明白AI的难度之大。大家在玩dota或者LoL的时候,可能会感觉电脑弱(不过话说我从来没有感觉到dota之中的AI弱,我表示经常打不过电脑o(╯□╰)o)。
其实我感觉最难的还是碰撞检测,我在代码之中也是做了一些比较简单地检测,其实这一块真的挺难的,首先你得想到各种情况,哪怕是边边角角碰到了,可能也是一种有效的碰撞。这样的碰撞检测在FPS类型的游戏之中显得格外的重要,比如子弹是否击中人,以及击中人的什么部位。这些方面,国外的做的很好,尤其是一些3A级大作。在他们的游戏引擎之中就有专门的检测机制。
下面来看一下代码,为了节省篇幅,我给出重要的代码,生成窗口的代码就省略了。
//头文件
#pragma once
#include "GameEngine.h"
#include <Windows.h>
#include <tchar.h> //使用swprintf_s函数所需的头文件
#include "resource.h"
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code)&0x8000)?1:0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code)&0x8000)?0:1)
struct SwordBullets //代表主人公的剑气
{
int x, y; //人物剑气的坐标
bool isExist; //剑气是否存在
};
struct BirdMaster
{
int x, y; //代表经过的小鸟的坐标
bool isExist; //表示小鸟是否存在
int picNum; //图的标号(一共5个)
HBITMAP hbird; //小鸟的位图句柄
};
struct BossMaster
{
int x, y; //代表boss的贴图坐标
int picNum; //boss图的标号(一共6个)
int ipicNum; //boss技能图的标号
HBITMAP hBoss; //boss的位图句柄
int boss_blood; //boss的血量
HBITMAP hBossSkill; //boss技能句柄
int Skill_x, Skill_y; //boss的技能坐标
int boss_x, boss_y; //boss的中心坐标
};
int g_iX = 0, g_iY = 0; //鼠标所在位置
<pre name="code" class="cpp"> //全局设备句柄、全局内存DC句柄、以及缓冲DC
HDC g_hdc = NULL, g_mdc = NULL, g_bufdc = NULL;
//定义五个位图句柄,分别用来存储背景、人物、剑气、失败、胜利
HBITMAP g_hBackGround = NULL, g_hSwordMan = NULL, g_hSwordBlade = NULL, g_hGameOver = NULL, g_hvictory = NULL;
int g_iXSwordMan = 0, g_iYSwordMan = 0; //人物所在位置记录当前人物的坐标,也就是贴图坐标
int g_iBGOffset = 0, g_iBulletNum = 0, g_ibirdNum = 0; //g_iBGOffset为滚动背景所要裁剪的区域宽度,g_iBulletNum记录剑气的数量
SwordBullets Bullet[30];
BirdMaster Bird[10];
BossMaster Boss;
int g_Score = 0; //玩家得分
int g_life = 0; //玩家血量
bool g_Isboss = false; //boss是否存在
//时间计数
DWORD g_tPre = 0, g_tNow = 0;
GameEngine * g_pGame = NULL;
//游戏中检测函数,(传参看似没有必要,主要是为了便于日后代码的维护)
bool Is_in_Randge(BirdMaster &bir);
bool Is_hit_bird(BirdMaster &bir, SwordBullets &bullet);
void bird_hit_man(BirdMaster &bir);
void Boss_appear();
void Boss_Initalize();
void Is_hit_boss(BossMaster &bos, SwordBullets &bullet);
bool Boss_hit_man(BossMaster &bos);
#pragma
#include "Swordsman.h"
BOOL GameInitalize(HINSTANCE hInstance) //游戏初始化函数
{
PlaySound(L"仙剑三·原版主题曲", NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
try
{
g_pGame = new GameEngine(hInstance, L"SwordsMan", L"SwordMan", IDI_SWORDMAN, IDI_SWORDMAN_SM);
}
catch (...)
{
return FALSE;
}
g_pGame->SetFrameDelay(20);
g_life = 100;
g_Score = 0;
Boss_Initalize(); //初始化boss的一些信息,满足一定条件出现
return TRUE;
}
void GameStart(HWND hWindow) //游戏开始函数
{
HBITMAP bmp;
g_hdc = GetDC(hWindow);
g_mdc = CreateCompatibleDC(g_hdc);
g_bufdc = CreateCompatibleDC(g_hdc);
bmp = CreateCompatibleBitmap(g_hdc, WINDOW_WIDTH, WINDOW_HEIGHT);
g_iX = 300;
g_iY = 100;
g_iXSwordMan = 300;
g_iYSwordMan = 100;
SelectObject(g_mdc, bmp); //选择空位图
//加载各种位图
g_hSwordMan = (HBITMAP)LoadImage(NULL, L"GameMedia\\\\swordman.bmp", IMAGE_BITMAP, 317, 283, LR_LOADFROMFILE);
g_hSwordBlade = (HBITMAP)LoadImage(NULL, L"GameMedia\\\\swordblade.bmp", IMAGE_BITMAP, 100, 26, LR_LOADFROMFILE);
g_hBackGround = (HBITMAP)LoadImage(NULL, L"GameMedia\\\\bg.bmp", IMAGE_BITMAP, WINDOW_WIDTH, WINDOW_HEIGHT, LR_LOADFROMFILE);
g_hGameOver = (HBITMAP)LoadImage(NULL, L"GameMedia\\\\gameover.bmp", IMAGE_BITMAP, 1086, 396, LR_LOADFROMFILE);
g_hvictory = (HBITMAP)LoadImage(NULL, L"GameMedia\\\\victory.bmp", IMAGE_BITMAP, 800, 600, LR_LOADFROMFILE);
POINT pt, lt, rb;
RECT rect;
//设置光标的位置
pt.x = 300;
pt.y = 100;
ClientToScreen(hWindow, &pt);
SetCursorPos(pt.x, pt.y);
ShowCursor(false); //隐藏鼠标
GetClientRect(hWindow, &rect);
lt.x = rect.left;
lt.y = rect.top;
rb.x = rect.right;
rb.y = rect.bottom;
ClientToScreen(hWindow, <);
ClientToScreen(hWindow, &rb);
rect.left = lt.x;
rect.top = lt.y;
rect.right = rb.x;
rect.bottom = rb.y;
//限制鼠标的移动区域
ClipCursor(&rect);
GamePaint(hWindow);
}
void GamePlay()
{
GamePaint(g_pGame->GetWindow());
if (g_ibirdNum <= 10)
{
g_tPre = GetTickCount();
if (g_tPre - g_tNow > 1500)
{
for (int j = 0; j < 10; j++)
{
if (!Bird[j].isExist)
{
Bird[j].x = 0;
Bird[j].y = rand() % WINDOW_HEIGHT;
Bird[j].isExist = true;
Bird[j].picNum = 0;
Bird[j].hbird = (HBITMAP)LoadImage(NULL, L"GameMedia\\\\bird.bmp", IMAGE_BITMAP, 1250, 360, LR_LOADFROMFILE);
g_ibirdNum++;
g_tNow = g_tPre;
break;
}
}
}
}
}
void GamePaint(HWND hWindow) //游戏绘制函数
{
//先在内存DC之中贴上背景图
SelectObject(g_bufdc, g_hBackGround);
BitBlt(g_mdc, 0, 0, g_iBGOffset, WINDOW_HEIGHT, g_bufdc, WINDOW_WIDTH - g_iBGOffset, 0, SRCCOPY);
BitBlt(g_mdc, g_iBGOffset, 0, WINDOW_WIDTH - g_iBGOffset, WINDOW_HEIGHT, g_bufdc, 0, 0, SRCCOPY);
TCHAR str[20] = {};
//计算剑侠的贴图坐标,设定每次进行贴图时,剑侠都会慢慢以10个像素的单位向目标接近
if (g_iX > g_iXSwordMan) //若当前鼠标X坐标大于人物的坐标,则前进(往左)
{
g_iXSwordMan += 10;
if (g_iXSwordMan > g_iX)
{
g_iXSwordMan = g_iX;
}
}
else //若当前的鼠标X坐标小于人物的坐标,则后退(往右)
{
g_iXSwordMan -= 10;
if (g_iXSwordMan < g_iX)
{
g_iXSwordMan = g_iX;
}
}
if (g_iY > g_iYSwordMan)
{
g_iYSwordMan += 10;
if (g_iYSwordMan > g_iY)
{
g_iYSwordMan = g_iY;
}
}
else
{
g_iYSwordMan -= 10;
if (g_iYSwordMan < 0)
{
g_iYSwordMan = 0;
}
}
//贴上剑侠的图
SelectObject(g_bufdc, g_hSwordMan);
TransparentBlt(g_mdc, g_iXSwordMan, g_iYSwordMan, 317, 283, g_bufdc, 0, 0, 317, 283, RGB(0, 0, 0));
//剑气(子弹)的贴图,先判断剑气(子弹)数目“g_iBulletNum”的值是否为“0”。若不为0,则对剑气(子弹)数组中各个还存在的剑气(子弹)按照其所在的坐标(b[i].x,b[i].y)循环进行贴图操作
SelectObject(g_bufdc, g_hSwordBlade);
if (g_iBulletNum != 0)
{
for (int i = 0; i < 30; i++)
{
if (Bullet[i].isExist)
{
//检测有无击中boss
Is_hit_boss(Boss, Bullet[i]);
if (Boss.boss_blood <= 0) //若boss死亡,就贴出胜利的图片
{
g_Isboss = false;
SelectObject(g_bufdc, g_hvictory);
TransparentBlt(g_mdc, 250, 50, 800, 600, g_bufdc, 0, 0, 800, 600, RGB(0, 0, 0));
g_pGame->SetGameState(TRUE);
}
//依次判断有无击中小鸟
for (int j = 0; j < 10; j++)
{
if (Is_hit_bird(Bird[j], Bullet[i]))
{
Bullet[i].isExist = false;
g_iBulletNum--;
Bird[j].isExist = false;
g_ibirdNum--;
g_Score += 10;
if (g_Score >= 100)
{
g_Isboss = true;
}
}
}
if (Bullet[i].isExist)
{
TransparentBlt(g_mdc, Bullet[i].x - 70, Bullet[i].y + 100, 100, 33, g_bufdc, 0, 0, 100, 26, RGB(0, 0, 0));
}
//设置下一个剑气(子弹)的坐标。剑气(子弹)是从右向左发射的,因此,每次其X轴上的坐标值递减10个单位,这样贴图会产生往左移动的效果。而如果剑气(子弹)下次的坐标已超出窗口的可见范围(h[i].x<0),那么剑气(子弹)设为不存在,并将剑气(子弹)总数g_iBulletNum变量值减1.
Bullet[i].x -= 10;
if (Bullet[i].x < 0)
{
g_iBulletNum--;
Bullet[i].isExist = false;
}
}
}
}
//判断小鸟的数量“g_ibirdNum”的值是否为“0”。若不为0,则对剑气(子弹)数组中各个还存在的剑气(子弹)按照其所在的坐标,循环进行贴图操作
if (g_ibirdNum != 0)
{
for (int i = 0; i < 10; i++)
{
if (Bird[i].isExist)
{
if (Bird[i].picNum >= 5)
{
Bird[i].picNum = 0;
}
SelectObject(g_bufdc, Bird[i].hbird);
BitBlt(g_mdc, Bird[i].x, Bird[i].y, 250, 180, g_bufdc, Bird[i].picNum * 250, 180, SRCAND);
BitBlt(g_mdc, Bird[i].x, Bird[i].y, 250, 180, g_bufdc, Bird[i].picNum * 250, 0, SRCPAINT);
if (Is_in_Randge(Bird[i]))
{
if (Bird[i].y < g_iYSwordMan)
{
Bird[i].y += 10;
}
else
{
Bird[i].y -= 10;
}
Bird[i].x += 10;
}
else
{
Bird[i].x += 10; //设定小鸟的前进路线
}
if (Bird[i].x > WINDOW_WIDTH)
{
Bird[i].isExist = false;
g_ibirdNum--;
}
Bird[i].picNum++;
bird_hit_man(Bird[i]);
}
}
}
//判断boss是否存在
if (g_Isboss)
{
Boss_appear();
}
//创建字体
HFONT hfont;
hfont = CreateFont(35, 20, 0, 0, 300, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("微软雅黑"));
//把字体选入g_mdc
SelectObject(g_mdc, hfont);
//设置背景颜色透明
SetBkMode(g_mdc, TRANSPARENT);
//设置文字颜色
SetTextColor(g_mdc, RGB(255, 0, 0));
//在右上角进行文字输出
swprintf_s(str, L"玩家血量(life): %d%%", g_life);
TextOut(g_mdc, 850, 0, str, wcslen(str));
swprintf_s(str, L"玩家得分(score): %d", g_Score);
TextOut(g_mdc, 50, 0, str, wcslen(str));
//贴上图
BitBlt(g_hdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, g_mdc, 0, 0, SRCCOPY);
//背景的移动
g_iBGOffset += 5;
if (g_iBGOffset >= WINDOW_WIDTH) //如果大于窗口宽度,就变为0
{
g_iBGOffset = 0;
}
}
void GameActivate(HWND hWindow) //游戏被激活
{
HDC hdc;
RECT rect;
GetClientRect(hWindow, &rect);
hdc = GetDC(hWindow);
DrawText(hdc, L"Welcome to back", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
ReleaseDC(hWindow, hdc);
}
void GameDeactivate(HWND hWindow) //游戏未被激活
{
HDC hdc;
RECT rect;
GetClientRect(hWindow, &rect);
hdc = GetDC(hWindow);
DrawText(hdc, L"Pause", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
ReleaseDC(hWindow, hdc);
}
void GameEnd() //游戏结束函数
{
//资源的清理
DeleteObject(g_hBackGround);
DeleteDC(g_bufdc);
DeleteDC(g_mdc);
ReleaseDC(g_pGame->GetWindow(), g_hdc);
}
void HandleKeys() //按键处理函数
{
if (KEY_DOWN(VK_ESCAPE)) //按下Esc退出
{
DestroyWindow(g_pGame->GetWindow());
return;
}
if (KEY_DOWN(VK_UP)) //按下up(向上的箭头)玩家向上移动
{
g_iYSwordMan -= 50;
if (g_iYSwordMan < 0)
{
g_iYSwordMan = 0;
}
}
if (KEY_DOWN(VK_SHIFT)) //按下shift进行加速
{
g_iBGOffset += 15;
}
if (KEY_DOWN(VK_LBUTTON)) //按下鼠标左键
{
for (int i = 0; i < 30; i++)
{
if (!Bullet[i].isExist)
{
Bullet[i].x = g_iXSwordMan;
Bullet[i].y = g_iYSwordMan + 30;
Bullet[i].isExist = true;
g_iBulletNum++;
break;
}
}
}
if (KEY_DOWN(VK_DOWN)) //按下up(向上的箭头)玩家向上移动
{
g_iYSwordMan += 50;
if (g_iYSwordMan > WINDOW_HEIGHT)
{
g_iYSwordMan = WINDOW_HEIGHT;
}
}
}
bool Is_in_Randge(BirdMaster &bir) //判断玩家是否在小鸟的视野半径内,若是,则飞向玩家
{
if (bir.isExist)
{
if (bir.x < g_iXSwordMan)
{
if (((bir.x - g_iXSwordMan)*(bir.x - g_iXSwordMan) + (bir.y - g_iYSwordMan)*(bir.y - g_iYSwordMan)) <= 90000) //检测是否在一个圆内
{
return true;
}
}
return false;
}
else
{
return false;
}
}
bool Is_hit_bird(BirdMaster &bir, SwordBullets &bullet) //判断技能有没有击中小鸟
{
if (bir.isExist)
{
if ((bullet.y + 13 > bir.y + 25) && (bullet.y + 13 < bir.y + 45))
{
if ((bullet.x < bir.x + 220) && (bullet.x > bir.x + 125))
{
return true;
}
}
}
return false;
}
void bird_hit_man(BirdMaster &bir) //小鸟与玩家的碰撞
{
if (bir.isExist)
{
if (((bir.y + 45) <= g_iYSwordMan + 250) && ((bir.y + 45) >= g_iYSwordMan + 80))
{
if (((bir.x + 220) > (g_iXSwordMan + 35)) && ((bir.x + 220) < (g_iXSwordMan + 125)))
{
bir.isExist = false;
g_life -= 10;
if (g_life == 0)
{
SelectObject(g_bufdc, g_hGameOver); //玩家死亡,贴出失败的图片
BitBlt(g_mdc, 400, 100, 543, 396, g_bufdc, 543, 0, SRCAND);
BitBlt(g_mdc, 400, 100, 543, 396, g_bufdc, 0, 0, SRCPAINT);
g_pGame->SetGameState(TRUE);
}
}
}
}
}
void Boss_appear() //Boss出现
{
if (Boss.picNum >= 6)
{
Boss.picNum = 0;
}
SelectObject(g_bufdc, Boss.hBoss); //贴上Boss的图片
BitBlt(g_mdc, Boss.x, Boss.y, 220, 200, g_bufdc, Boss.picNum * 220, 200, SRCAND);
BitBlt(g_mdc, Boss.x, Boss.y, 220, 200, g_bufdc, Boss.picNum * 220, 0, SRCPAINT);
Boss.picNum++;
if (((Boss.boss_x - g_iXSwordMan)*(Boss.boss_x - g_iXSwordMan) + (Boss.boss_y - g_iYSwordMan)*(Boss.boss_y - g_iYSwordMan))<360000)
{
if (Boss.ipicNum >= 6)
{
Boss.ipicNum = 0;
}
if (Boss.Skill_x - Boss.x >= 380)
{
Boss.Skill_x = Boss.x + 200;
}
if (Boss.Skill_y - Boss.y >= 280)
{
Boss.Skill_y = Boss.y + 100;
}
SelectObject(g_bufdc, Boss.hBossSkill); //贴上Boss的技能图
BitBlt(g_mdc, Boss.Skill_x, Boss.Skill_y, 150, 150, g_bufdc, Boss.ipicNum * 150, 150, SRCAND);
BitBlt(g_mdc, Boss.Skill_x, Boss.Skill_y, 150, 150, g_bufdc, Boss.ipicNum * 150, 0, SRCPAINT);
Boss.ipicNum++;
if (Boss_hit_man(Boss))
{
g_life -= 20;
if (g_life <= 0) //判断玩家是否还有血量,若没有,则贴出失败的图片
{
g_life = 0;
SelectObject(g_bufdc, g_hGameOver);
BitBlt(g_mdc, 400, 100, 543, 396, g_bufdc, 543, 0, SRCAND);
BitBlt(g_mdc, 400, 100, 543, 396, g_bufdc, 0, 0, SRCPAINT);
g_pGame->SetGameState(TRUE);
}
}
}
else
{
if (Boss.x >= g_iXSwordMan - 400)
{
Boss.x -= rand() % 40;
}
else
{
if (Boss.y > g_iYSwordMan)
{
Boss.y -= 15;
if (Boss.x < g_iXSwordMan)
{
Boss.x += 10;
}
else
{
Boss.x -= 20;
}
}
else if (Boss.y < g_iYSwordMan)
{
Boss.y += 15;
if (Boss.x < g_iXSwordMan)
{
Boss.x += 10;
}
else
{
Boss.x -= 20;
}
}
else
{
if (Boss.x < g_iXSwordMan)
{
Boss.x += 10;
}
else
{
Boss.x -= 20;
}
}
}
}
}
void Boss_Initalize() //Boss的初始化
{
g_Isboss = false;
Boss.boss_blood = 100;
Boss.hBoss = (HBITMAP)LoadImage(NULL, L"GameMedia\\\\boss.bmp", IMAGE_BITMAP, 1320, 400, LR_LOADFROMFILE); //加载boss位图
Boss.hBossSkill = (HBITMAP)LoadImage(NULL, L"GameMedia\\\\bossSkill.bmp", IMAGE_BITMAP, 900, 300, LR_LOADFROMFILE); //加载boss技能图
Boss.picNum = 0;
Boss.ipicNum = 0;
Boss.x = 0;
Boss.y = rand() % WINDOW_HEIGHT;
Boss.Skill_x = Boss.x + 200; //技能设置的初始位置
Boss.Skill_y = Boss.y + 100;
Boss.boss_x = Boss.x + 150; //Boss的中心位置
Boss.boss_y = Boss.y + 100;
}
bool Boss_hit_man(BossMaster &bos) //判断Boss是否碰撞到玩家,或者Boss技能是否击中玩家
{
bos.Skill_x += 30;
if (bos.Skill_y + 75 < g_iYSwordMan)
{
bos.y += 5;
bos.Skill_y += 20;
}
else if (bos.Skill_y + 75 > g_iYSwordMan)
{
bos.y -= 5;
bos.Skill_y -= 20;
}
else
{
bos.y = bos.y;
bos.Skill_y = bos.Skill_y;
}
//判断boss是否碰到玩家
if ((bos.boss_y <= g_iYSwordMan + 250) && (bos.boss_y >= g_iYSwordMan + 80))
{
if ((bos.boss_x > (g_iXSwordMan + 35)) && (bos.boss_x < (g_iXSwordMan + 125)))
{
g_life -= 15;
}
}
//判断boss技能是否击中玩家
if ((bos.Skill_y + 75 <= g_iYSwordMan + 250) && ((bos.Skill_y + 75) >= g_iYSwordMan + 80))
{
if (((bos.Skill_x + 75) >(g_iXSwordMan + 35)) && ((bos.Skill_x + 75) < (g_iXSwordMan + 125)))
{
return true;
}
}
return false;
}
void Is_hit_boss(BossMaster &bos, SwordBullets &bullet) //玩家是否击中Boss
{
if ((bullet.y + 13 > (bos.boss_y - 30)) && (bullet.y + 13 < bos.boss_y + 15))
{
if ((bullet.x < bos.boss_x + 50) && (bullet.x > bos.boss_x - 30))
{
bos.boss_blood -= 15;
g_Score += 20;
bullet.isExist = false; //子弹击中之后消失
g_iBulletNum--;
}
}
}
在代码之中基本都有注释,这里就不分析了,Boss有500点血量,还真不是那么简单地:
不过要想简单也很容易,把Boss血量降到1就可以了,O(∩_∩)O,来一张胜利作为结束:
当然这只是一个很简单的游戏,大家可以看看,也欢迎大家给出一些建议!(PS:窗口生成那一部分WIN32的代码是一个固定的格式,为了节省篇幅所以没有贴出)
以上是关于Win32游戏制作之---SwordsMan的主要内容,如果未能解决你的问题,请参考以下文章
在井字游戏 C++ 中使用“新游戏”按钮,win32 应用程序初学者