视频:【课设拯救计划/直播回放】C++打地鼠游戏一小时极限开发(完整版)_哔哩哔哩_bilibili
创建几个全局变量:
cpp
IMAGE img_menuBackground; //主菜单背景图
IMAGE img_mole; //地鼠图片
IMAGE img_empty; //坑位图片
IMAGE img_hammer_idle; //默认状态锤子
IMAGE img_hammer_down; //敲击状态锤子
bool is_quit = false; //游戏是否退出
bool is_satrt = false; //游戏是否开始IMAGE img_menuBackground; //主菜单背景图
IMAGE img_mole; //地鼠图片
IMAGE img_empty; //坑位图片
IMAGE img_hammer_idle; //默认状态锤子
IMAGE img_hammer_down; //敲击状态锤子
bool is_quit = false; //游戏是否退出
bool is_satrt = false; //游戏是否开始
创建一个窗口:
cpp
int main()
{
initgraph(800, 950);
hwnd = GetConsoleWindow(); // 获取控制台窗口句柄
while (!is_quit) {
FlushBatchDraw();
}
EndBatchDraw();
return 0;
}
加载资源
void load_resource()
{
loadimage(&img_menu,_T("resources/menu.jpg"));
loadimage(&img_mole, _T("resources/mole.jpg"));
loadimage(&img_empty, _T("resources/empty.jpg"));
loadimage(&img_hammer_idle, _T("resources/hammer_idle.jpg"));
loadimage(&img_hammer_down, _T("resources/hammer_down.jpg"));
mciSendString(_T("open resources/bgm.mp3 alias bgm"), NULL, 0, NULL);
mciSendString(_T("open resources/hit.mp3 alias hit"), NULL, 0, NULL);
}
创建地图
cpp
bool map[4][4] = {
{false,false,false,false},
{false,false,false,false},
{false,false,false,false},
{false,false,false,false},
};
int idx, idy; //当前地鼠所在位置
false代表当前位置没有地鼠。
定义地鼠的坐标位置,锤子的坐标 ,砸中地鼠的得分,锤子的状态:
cpp
int idx, idy; //当前地鼠所在位置
bool is_hammer_down; //锤子是否被按下
POINT pos_hammer = { 0,0 }; //锤子的坐标
int score; //得分
逻辑部分
创建四个函数
cpp
void input_menu_scence(const ExMessage& msg)
{
}
void input_game_scence(const ExMessage& msg)
{
}
void render_menu_scence(const ExMessage& msg)
{
putimage(0, 0, &img_menu);
}
void render_game_scence(const ExMessage& msg)
{
}
void update_game_scence(const ExMessage& msg)
{
}
菜单和进入游戏之间的逻辑
cpp
int main()
{
.......
while (!is_quit) {
ExMessage msg;
while (peekmessage(&msg))
{
//如果当前未进入游戏,那么就进入菜单页面,否则就继续游戏
if (!is_start)
{
input_menu_scence(msg);
}
else
{
input_game_scence(msg);
}
}
setbkcolor(RGB(251,251,251));
cleardevice(); //清空绘图窗口
//如果当前没有开始游戏,那么就进入菜单页面,否则就继续游戏
if (!is_start)
{
render_menu_scence(msg);
}
else
{
render_game_scence(msg);
}
FlushBatchDraw();
Sleep(5);
}
}
设置按钮布局和样式以及功能
布局和样式
cpp
Button()
{
//设置按下按钮时的样式
void on_render()
{
setlinecolor(RGB(20,20,20)); //设置按钮边框为深灰色
if (is_pushed)
{
setfillcolor(RGB(185,185,185)); //设置按下按钮时背景色为灰色
}
else if (is_hovered)
{
setfillcolor(RGB(235,235,235)); //设置鼠标悬停时背景色为灰色
}
else
{
setfillcolor(RGB(205,205,205)); //设置普通状态下背景色为白色
}
fillrectangle(_rect.left, _rect.top, _rect.right, _rect.bottom); //绘制矩形
setbkmode(TRANSPARENT); //设备背景:透明
settextcolor(RGB(20,20,20)); //字体颜色
drawtext(_text.c_str(), &_rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER); //绘制文本
//水平居中 单行显示 垂直居中
}
}
调用
main.cpp
cpp
#include"button.h"
//构建三个自定义按钮类按钮对象
Button btn_play(L"开 始 游 戏", {350,450,500,500}); //开始按钮
Button btn_info(L"游 戏 介 绍", { 350,500,500,550 });
Button btn_quit(L"退 出 游 戏", { 350,550,500,600 });
int main()
{
//对三个按钮通过调用on_input()进行设置布局和样式
void input_menu_scence(const ExMessage& msg)
{
btn_play.on_input(msg);
btn_info.on_input(msg);
btn_quit.on_input(msg);
}
//绘制这三个按钮
void render_menu_scence(const ExMessage& msg)
{
putimage(0, 0, &img_menu);
btn_play.on_render();
btn_info.on_render();
btn_quit.on_render();
}
return 0;
}
按钮绑定回调函数
在初始化的时候就给按钮绑定回调函数。
button.h
cpp
class Button
{
public:
Button(const std::wstring& text,const RECT& rect )
{
//设置按钮的回调函数
void set_on_click(const std::function<void()>& func)
{
on_click = func;
}
}
main函数调用,单击按钮播放背景音乐
介绍按钮弹出信息弹窗。
退出按钮退出程序。
main.cpp
cpp
int main()
{
.....
load_resource();
btn_play.set_on_click([&] {
is_start = true;
mciSendString(_T("play bgm"),NULL,0,NULL);
});
btn_info.set_on_click([&] {
SetForegroundWindow(hwnd); // 确保当前窗口在最前面
MessageBox(hwnd,_T("用鼠标左键点击地图上的空白位置,即可放置地鼠。\n\n游戏过程中,地鼠会自动向空白位置移动,\n\n玩家需要用锤子将地鼠击中,\n\n击中地鼠后,游戏将会加1分。\n\n游戏结束时,玩家可以选择重新开始或退出游戏。"),_T("游戏说明"),MB_OK);
});
btn_quit.set_on_click([&] {
is_quit = true;
});
BeginBatchDraw();
......
}
游戏主场景
砸中地鼠得分,没砸中减分
cpp
void input_game_scence(const ExMessage& msg)
{
switch(msg.message)
{
//更新鼠标位置
case WM_MOUSEMOVE:
pos_hammer.x = msg.x;
pos_hammer.y = msg.y;
break;
//鼠标左键按下
case WM_LBUTTONDOWN:
{
is_hammer_down = true; //锤子被按下了
static const int width_mole=img_mole.getwidth();
static const int height_mole = img_mole.getwidth();
if (map[idx][idy]&&
pos_hammer.x>=idx*width_mole+ offset_map.x&&
pos_hammer.x <= (idx+1) * width_mole + offset_map.x&&
pos_hammer.y >= idy * width_mole + offset_map.y&&
pos_hammer.y <= (idy + 1) * width_mole + offset_map.y
)
{
score += 10;
map[idx][idy] = false; //地鼠被击中
mciSendString(_T("play hit"), NULL, 0, NULL); //播放击中音效
}
else
{
is_hammer_down = false; //鼠标抬起切换为锤子默认状态
score -= 10;
}
break;
//鼠标左键松开
case WM_LBUTTONUP:
{
is_hammer_down = false; //锤子松开了
}
break;
//键盘按下
default:
break;
}
}
}
游戏更新
cpp
void update_game_scence(const ExMessage& msg)
{
static int timer = 0;
timer = timer++ % 100; //每隔100帧触发一次地鼠移动
if (timer == 0)
{
map[idx][idy] = false; //当前位置没有地鼠
idx = rand() % 4; //随机生成地鼠位置
idy = rand() % 4;
while (map[idx][idy]) //如果随机生成的位置已经有地鼠,则重新生成
{
idx = rand() % 4;
idy = rand() % 4;
}
map[idx][idy] = true; //随机生成地鼠
}
}
游戏渲染
cpp
void render_game_scence(const ExMessage& msg)
{
//绘制地图
static const int width_mole = img_mole.getwidth();
std::cout<<"width_mole:"<<width_mole<<std::endl;
static const int height_mole = img_mole.getheight();
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
std::cout<<"map[i][j]:" << map[i][j] <<std::endl;
if (map[i][j]) //当前位置有地鼠
{
putimage(i * width_mole + offset_map.x, j * height_mole + offset_map.y, &img_mole); //绘制地鼠
std::cout<<"绘制地鼠成功"<<std::endl;
}
else
{
putimage(i * width_mole + offset_map.x, j * height_mole + offset_map.y, &img_empty); //绘制空白位置
std::cout << "绘制地鼠失败" << std::endl;
}
}
}
//绘制锤子
static const int width_hammer_idle = img_hammer_idle.getwidth();
static const int height_hammer__idle = img_hammer_idle.getheight();
static const int width_hammer_down = img_hammer_down.getwidth();
static const int height_hammer_down = img_hammer_down.getheight();
if (is_hammer_down) //锤子被按下
{
putimage(pos_hammer.x - width_hammer_down / 2, pos_hammer.y - height_hammer_down / 2, &img_hammer_down);
}
else
{
putimage(pos_hammer.x - width_hammer_down / 2, pos_hammer.y - height_hammer_down / 2, &img_hammer_idle);
}
//绘制游戏得分
std::cout<<"score:"<<score<<std::endl;
std::wstring score_str = L"游戏得分: "+std::to_wstring(score);
settextstyle(25,0,_T("黑体"));
settextcolor(RGB(255,15,105));
outtextxy(10, 10, score_str.c_str());
}
此时有个问题,因为我的锤子的图片是png格式的,png的透明背景,在eaxys里会被处理为黑色背底:
我们这个时候就不用putimage()函数,用我们自己定义的函数:
cpp
void transparentimage3(IMAGE* dstimg, int x, int y, IMAGE* srcimg) //新版png
{
HDC dstDC = GetImageHDC(dstimg);
HDC srcDC = GetImageHDC(srcimg);
int w = srcimg->getwidth();
int h = srcimg->getheight();
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf);
}
cpp
void render_game_scence(const ExMessage& msg)
{
//绘制地图
static const int width_mole = img_mole.getwidth();
std::cout<<"width_mole:"<<width_mole<<std::endl;
static const int height_mole = img_mole.getheight();
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
std::cout<<"map[i][j]:" << map[i][j] <<std::endl;
if (map[i][j]) //当前位置有地鼠
{
putimage(i * width_mole + offset_map.x, j * height_mole + offset_map.y, &img_mole); //绘制地鼠
std::cout<<"绘制地鼠成功"<<std::endl;
}
else
{
putimage(i * width_mole + offset_map.x, j * height_mole + offset_map.y, &img_empty); //绘制空白位置
std::cout << "绘制地鼠失败" << std::endl;
}
}
}
//绘制锤子
static const int width_hammer_idle = img_hammer_idle.getwidth();
static const int height_hammer__idle = img_hammer_idle.getheight();
static const int width_hammer_down = img_hammer_down.getwidth();
static const int height_hammer_down = img_hammer_down.getheight();
if (is_hammer_down) //锤子被按下
{
//putimage(pos_hammer.x - width_hammer_down / 2, pos_hammer.y - height_hammer_down / 2, &img_hammer_down);
transparentimage3(NULL, pos_hammer.x - width_hammer_down / 2, pos_hammer.y - height_hammer_down / 2, & img_hammer_down);
}
else
{
//putimage(pos_hammer.x - width_hammer_down / 2, pos_hammer.y - height_hammer_down / 2, &img_hammer_idle);
transparentimage3(NULL,pos_hammer.x - width_hammer_down / 2, pos_hammer.y - height_hammer_down / 2, &img_hammer_idle);
}
//绘制游戏得分
std::cout<<"score:"<<score<<std::endl;
std::wstring score_str = L"游戏得分: "+std::to_wstring(score);
settextstyle(25,0,_T("黑体"));
settextcolor(RGB(255,15,105));
outtextxy(10, 10, score_str.c_str());
}