C++打地鼠游戏一小时极限开发

视频:【课设拯救计划/直播回放】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());
}
相关推荐
唐棣棣28 分钟前
期末速成C++【类和对象】
开发语言·c++
染指111035 分钟前
49.第二阶段x86游戏实战2-鼠标点击call深追二叉树
汇编·c++·windows·游戏安全·反游戏外挂·游戏逆向
浅陌sss1 小时前
Unity性能优化---使用SpriteAtlas创建图集进行批次优化
游戏·unity·游戏引擎
奶油泡芙9312 小时前
Powering the Hero (easy version)为英雄提供力量(简单版)
c++
伍贰什丿2 小时前
C语言学习day22:URLDownloadToFile函数/开发文件下载工具
c语言·c++·学习
Cooloooo2 小时前
Treap树堆【东北大学oj数据结构8-4】C++
开发语言·数据结构·c++
向宇it3 小时前
【从零开始入门unity游戏开发之——C#篇06】变量类型转化和异常捕获
开发语言·游戏·unity·c#·游戏引擎
wks198912153 小时前
线程sleep的时候会释放锁吗
开发语言·c++·算法
伍贰什丿3 小时前
C语言学习day22:ReadProcessMemory函数/游戏内存数据读取工具开发
c语言·开发语言·学习·游戏