本文使用了EasyX和GDI部分函数,主要使用C语言面向过程编程方式,使用了部分C++语法,如引用,easyx里的类和对象也是C++的,但我没有使用自定义类。
本文涉及到普通贴图、透明背景贴图、图像旋转、缩放、及旋转缩放后使用掩码图实现透明贴图、图像移动、图像逐步显示、鼠标光标位置、按键信息处理等。本篇只制作了植物大战僵尸开始的加载界面,由于部分素材找不到,或者暂时偷懒,所以有些显示效果就暂时没有做,后续还会逐渐进行优化和添加效果。
- vs2022项目文件,包括素材
百度云:
通过网盘分享的文件:vs_pvz
链接: https://pan.baidu.com/s/1jiyavbXLgLFBCGbl2njxyw?pwd=g8n6 提取码: g8n6
csdn:
正在审核 - 主要参考了一篇文章和一个问答,其他内容主要参考easyx的官网和微软learn网站
https://qa.codebus.cn/question/1318
https://blog.csdn.net/Solahalo/article/details/127598433 - 自己创建项目需要将项目属性的字符集改成使用多字节字符集,并配置好easyx环境。

- 部分效果图:


- 直接贴代码,有较完整的注释,但本人语文不好,有的地方可能讲得不清楚,后面会慢慢改。
#include<graphics.h>
#include<conio.h>
#include<time.h>
#pragma comment(lib,"MSIMG32.LIB")
int start_game();
void loadbar(IMAGE& loadbar_grass, IMAGE* sodrollcap, int w);
void stretchimage(IMAGE& img_bk, IMAGE& img_mask, int dst_x, int dst_y, int dst_w, int dst_h, int src_w, int src_h);
void transparentimage(IMAGE* dstimg, int x, int y, IMAGE* srcimg);
void transparentimage(IMAGE* dstimg, int x, int y, int w, int h, IMAGE* srcimg);
void transparentimage(IMAGE* dstimg, int x, int y, int dst_w, int dst_h, int src_w, int src_h, IMAGE* srcimg);
int main()
{
initgraph(800, 600);// 初始化图形窗口,分辨率为800x600
BeginBatchDraw();//开始双缓冲
start_game();
//cleardevice();
EndBatchDraw();//结束双缓冲
_getch();//函数执行完毕后按任意键继续
closegraph();//关闭绘图窗口
/*如果将_getch()放在closegraph()后需要将loadbar()函数的两个IMAGE对象设置成局部静态对象或全局对象
不然程序结束时会报错,应该是因为全局和静态变量会自动初始化。
Copilot的部分分析翻译:
在拉伸图像中,在 img_bk 和 img_mask 上调用 GetImageHDC,它们是在加载栏中创建并由 rotateimage 填充的本地 IMAGE 对象。
如果 rotateimage 失败或 IMAGE 对象未正确初始化,则 GetImageHDC 可能会返回 null 或无效的 HDC,从而在传递给
StretchBlt 时导致访问冲突。rotateimage可能无法正确初始化或填充 IMAGE 对象img_bk和img_mask,可能是由于源图像无效、
分配失败或 rotateimage错误造成的。这会导致在 StretchBlt 中使用无效的设备上下文 (HDC) ,从而导致访问冲突。*/
return 0;
}
//对游戏开始界面进行绘制和交互的函数
int start_game()
{
IMAGE title_background, title;//背景图和标题图
IMAGE loadbar_dirt, loadbar_grass;//加载条的泥土和草皮图
IMAGE sodrollcap[2];//草卷的掩码和背景图
IMAGE zombie_head;//僵尸头部图
IMAGE loadbar_dirt_brighter;//加载条的泥土变亮图
//加载背景图,800和600为图像缩放后的宽和高,但不是必要的,因为原图像大小为800x600
loadimage(&title_background, "Assets/Images/interface/title_background.jpg", 800, 600);
//加载标题图,700和100为图像缩放后的宽和高
loadimage(&title, "Assets/Images/interface/title.png", 700, 100);
//加载加载条的泥土
loadimage(&loadbar_dirt, "Assets/Images/interface/LoadBar_dirt.png");
//加载加载条的泥土变亮图
loadimage(&loadbar_dirt_brighter, "Assets/Images/interface/LoadBar_dirt_brighter.png");
//加载加载条的草皮图
loadimage(&loadbar_grass, "Assets/Images/interface/LoadBar_grass.png");
//加载草卷的掩码和背景图
loadimage(&sodrollcap[0], "Assets/Images/interface/SodRollCap_mask.png");
loadimage(&sodrollcap[1], "Assets/Images/interface/SodRollCap_bk.png");
//加载僵尸头部图
loadimage(&zombie_head, "Assets/Images/interface/FlagMeterParts1.png");
int grass_w = 0;//草皮的水平方向显示长度,图像从左到右逐渐显示
int zombie_head_h = 0;//僵尸头部竖直方向显示高度,图像从上到下逐渐显示
long start_time = clock(), end_time;//开始时间和结束时间
ExMessage m;//定义消息结构体
settextstyle(16, 8, "Courier");// 设置文字样式,字体高为16,宽度为8,字体为Courier
settextcolor(YELLOW);// 设置文字颜色为黄色
setbkmode(TRANSPARENT);// 设置文字背景模式为透明
while (true)//大循环
{
end_time = clock();//获取当前程序运行时间
if (end_time - start_time > 10)//如果当前时间与上次时间的差值大于10毫秒,执行一次操作
{
putimage(0, 0, &title_background);//绘制背景图
transparentimage(NULL, 50, 25, &title);//绘制标题图
if (grass_w < 312)//草皮水平向显示长度小于312时
{
transparentimage(NULL, 240, 500, &loadbar_dirt);//绘制加载条的泥土图
outtextxy(360, 515, "LOADING...");//绘制文字
loadbar(loadbar_grass, sodrollcap, grass_w);//绘制加载条的草皮图,皮草显示的宽度为grass_w逐渐增加
//原游戏的加载条应该是随游戏加载进度变化而变化的,但我还不会实现,所以就模拟一个加载条的效果
grass_w += 2;//草皮的水平方向显示长度增加2
}
else
{
if (zombie_head_h < 23)//当僵尸头部竖直方向显示高度小于23
{
zombie_head_h++;// 僵尸头部竖直方向显示高度增加1
transparentimage(NULL, 240, 500, &loadbar_dirt);//绘制加载条的泥土图
outtextxy(360, 515, "LOADING...");//绘制文字
}
else
{
peekmessage(&m, EX_MOUSE | EX_KEY);//获取鼠标或按键消息
if (m.x > 240 && m.x < 561 && m.y>500 && m.y < 540)//如果鼠标在加载条的泥土范围
{
transparentimage(NULL, 240, 500, &loadbar_dirt_brighter);//绘制加载条的泥土变亮的图
settextcolor(RED);// 设置文字颜色为红色
if (m.message == WM_LBUTTONDOWN)//如果鼠标左键按下
{
return 0;
}
}
else
{
transparentimage(NULL, 240, 500, &loadbar_dirt);//绘制加载条的泥土图
settextcolor(YELLOW);// 设置文字颜色为黄色
}
outtextxy(330, 515, "CLICK TO START!");//绘制文字
}
transparentimage(NULL,//使用主窗口
480, 501 - zombie_head_h * 2,// 僵尸头部图的显示位置,竖直方向从y坐标501逐渐向上移动,
//乘2是因为图像放大了两倍,而zombie_head_h最大为原图像的高度
zombie_head.getwidth() * 2, zombie_head_h * 2,// 僵尸头部图的显示宽度和高度放大两倍
zombie_head.getwidth(), zombie_head_h,// 僵尸头部图的源图像宽度和高度,高度从0逐渐增加到23
&zombie_head);
// 绘制加载条的草皮图,草皮图的宽度为grass_w,高度为33
transparentimage(NULL, 240, 480, grass_w, 33, &loadbar_grass);
}
FlushBatchDraw();// 刷新未显示的绘图
start_time = end_time;
}
}
return 0;
}
//处理草卷缩小及滚动和从左到右逐渐显示草皮的函数,参数分别为草皮的引用、草卷的指针及草皮原图像水平方向显示的范围。
//引用是C++中的一种特殊类型,它允许函数直接修改变量的值,而不需要返回值或使用指针,就是变量的别名
void loadbar(IMAGE& loadbar_grass, IMAGE* sodrollcap, int w)
{
float s = (314.0 - w / 2.0) / 314.0;//草皮的缩放比例,314为草皮的原始宽度
double rad = w * 1 / (70 * s / 2);//草皮的旋转弧度,将草卷的初始直径约为70像素,半径35像素,
//草卷滚动的剩余长度,除以草卷半径得滚动的最大剩余弧度数,再除以剩余长度得草皮每向右显示一个像素草卷旋转的弧度
//最终化得草皮每向右显示一个像素草卷旋转的弧度为1 / (70 * s / 2),也就是草卷越小,旋转的弧度越大
static IMAGE img_bk, img_mask;//静态对象,自动初始化,函数结束后变量不会被销毁,用于存储旋转后的草卷图像
transparentimage(NULL, 240, 480, w, 33, &loadbar_grass);// 绘制加载条的草皮图,草皮图显示到原图宽w,高度为33
rotateimage(&img_mask, &sodrollcap[0], -rad, WHITE, false);// 旋转草卷的掩码图像,空白区域填充为白色
rotateimage(&img_bk, &sodrollcap[1], -rad, BLACK, false);// 旋转草卷的背景图像,空白区域填充为黑色
int src_w = img_bk.getwidth();//旋转后草卷图像的宽
int src_h = img_bk.getheight();//旋转后草卷图像的高
int x = 215 + w + (src_w - src_w * s) / 2;//计算缩小后草卷图像的显示的x坐标
int y = 440 + (src_h - src_h * s);//计算缩小后草卷图像的显示的y坐标,使草卷图像的底部与加载条的土块顶部对齐
// 显示旋转后再缩小的图像,x,y为显示坐标,src_w,src_h为草卷图像的原始宽和高,s为缩放比例
stretchimage(img_bk, img_mask, x, y, src_w * s, src_h * s, src_w, src_h);
//putimage(220 + w, 460, &img_mask, SRCAND);
//putimage(220 + w, 460, &img_bk, SRCPAINT);
}
//处理图像拉伸的函数,参数分别为背景图、掩码图的引用、目标位置的x坐标dst_x、y坐标dst_Y、目标位置的宽dst_w、高dst_h、
//源图像的宽和高src_w、src_h
void stretchimage(IMAGE& img_bk, IMAGE& img_mask, int dst_x, int dst_y, int dst_w, int dst_h, int src_w, int src_h)
{
HDC imgDC = GetImageHDC(&img_bk); // 获取源图像的 DC
HDC imgmaskDC = GetImageHDC(&img_mask);
// 主窗口作为目标位置
HDC mainDC = GetImageHDC(NULL); // 获取主窗口的 DC
SetStretchBltMode(mainDC, STRETCH_HALFTONE); // 设置拉伸贴图模式为抗锯齿
// 如果不设置,默认贴图模式为临近颜色,效果差,性能高
// 将源图像贴图到目标位置
StretchBlt(mainDC, // 目标 DC
dst_x, dst_y, // 目标位置的坐标
dst_w, dst_h, // 目标位置的长宽
imgmaskDC, // 源 DC
0, 0, // 源位置的坐标
src_w, src_h, // 源位置的长宽
SRCAND); // 贴图的光栅模式, 目标图像 AND 源图像
//光栅操作用目标图像 OR 源图像,其他同上面
StretchBlt(mainDC, dst_x, dst_y, dst_w, dst_h, imgDC, 0, 0, src_w, src_h, SRCPAINT);
}
//背景透明图像显示的函数,参数分别为目标图像的指针、目标位置的x坐标、y坐标、源图像的指针
void transparentimage(IMAGE* dstimg, int x, int y, IMAGE* srcimg) //新版png
{
HDC dstDC = GetImageHDC(dstimg);// 获取主窗口的 DC
HDC srcDC = GetImageHDC(srcimg);// 获取源图像的 DC
int w = srcimg->getwidth();// 获取源图像的宽度
int h = srcimg->getheight();// 获取源图像的高度
// 设置混合结构,AC_SRC_OVER表示源图像覆盖目标图像,0表示不使用混合标志,
//255表示源图像的透明度为不透明,AC_SRC_ALPHA要求的源图像不一定为32位图像
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
// 使用AlphaBlend函数进行背景透明图像显示,参数分别为目标图像的DC、目标位置的x坐标、y坐标、
// 目标位置的宽度和高度、源图像的DC、源图像的x坐标、y坐标、源图像的宽度和高度、混合结构bf
AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf);
}
//背景透明图像显示的重载函数,可输入目标位置及源图像要显示的宽高控制源图像显示范围,
// 参数分别为目标图像的指针、目标位置的x坐标、y坐标、目标位置及源图像的宽高、源图像的指针
void transparentimage(IMAGE* dstimg, int x, int y, int w, int h, IMAGE* srcimg) //
{
HDC dstDC = GetImageHDC(dstimg);// 获取主窗口的 DC
HDC srcDC = GetImageHDC(srcimg);// 获取源图像的 DC
//设置混合函数,AC_SRC_OVER表示源图像覆盖目标图像,0表示不使用混合标志,
//255表示源图像的透明度为不透明,AC_SRC_ALPHA要求的源图像不一定为32位图像
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
// 使用AlphaBlend函数进行背景透明图像显示,参数分别为目标图像的DC、目标位置的x坐标、y坐标、
// 目标位置的宽度和高度、源图像的DC、源图像的x坐标、y坐标、源图像的宽度和高度、混合结构bf
AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf);
}
//背景透明图像显示的重载函数,可分别输入目标位置显示的宽高实现图像缩放,及输入源图像要显示的宽高控制图像显示范围,
//参数分别为目标图像的指针、目标位置的x坐标、y坐标、目标位置的宽高dst_w,dsth及源图像的宽高src_w,src_h、源图像的指针
void transparentimage(IMAGE* dstimg, int x, int y, int dst_w, int dst_h, int src_w, int src_h, IMAGE* srcimg)
{
HDC dstDC = GetImageHDC(dstimg);// 获取主窗口的 DC
HDC srcDC = GetImageHDC(srcimg);// 获取源图像的 DC
//设置混合函数,AC_SRC_OVER表示源图像覆盖目标图像,0表示不使用混合标志,
//255表示源图像的透明度为不透明,AC_SRC_ALPHA要求的源图像不一定为32位图像
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
// 使用AlphaBlend函数进行背景透明图像显示, 参数分别为目标图像的DC、目标位置的x坐标、y坐标、
// 目标位置的宽高dst_w,dst_h、源图像的DC、源图像的x坐标、y坐标、源图像显示范围的宽高src_w,src_h、混合结构bf
AlphaBlend(dstDC, x, y, dst_w, dst_h, srcDC, 0, 0, src_w, src_h, bf);
}

被折叠的 条评论
为什么被折叠?



