C语言植物大战僵尸EasyX训练实例(一)——开始界面制作(包含素材)

本文使用了EasyX和GDI部分函数,主要使用C语言面向过程编程方式,使用了部分C++语法,如引用,easyx里的类和对象也是C++的,但我没有使用自定义类。
本文涉及到普通贴图、透明背景贴图、图像旋转、缩放、及旋转缩放后使用掩码图实现透明贴图、图像移动、图像逐步显示、鼠标光标位置、按键信息处理等。本篇只制作了植物大战僵尸开始的加载界面,由于部分素材找不到,或者暂时偷懒,所以有些显示效果就暂时没有做,后续还会逐渐进行优化和添加效果。

  1. vs2022项目文件,包括素材
    百度云:
    通过网盘分享的文件:vs_pvz
    链接: https://pan.baidu.com/s/1jiyavbXLgLFBCGbl2njxyw?pwd=g8n6 提取码: g8n6
    csdn:
    正在审核
  2. 主要参考了一篇文章和一个问答,其他内容主要参考easyx的官网和微软learn网站
    https://qa.codebus.cn/question/1318
    https://blog.csdn.net/Solahalo/article/details/127598433
  3. 自己创建项目需要将项目属性的字符集改成使用多字节字符集,并配置好easyx环境。在这里插入图片描述
  4. 部分效果图:
    在这里插入图片描述
    在这里插入图片描述
  5. 直接贴代码,有较完整的注释,但本人语文不好,有的地方可能讲得不清楚,后面会慢慢改。
#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); 
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值