当前位置: 首页 > news >正文

从零到有的游戏开发(visual studio 2022 + easyx.h)

引言

本文章适用于C语言初学者掌握基本的游戏开发,

我将用详细的步骤引领大家如何开发属于自己的游戏。

作者温馨提示:不要认为开发游戏很难,一些基本的游戏逻辑其实很简单,

关于游戏的开发环境也不用担心,我会详细说明如何配置开发环境,下载链接我也会列出。

文章前半部分教你掌握开发游戏的基本逻辑(各种游戏逻辑)。

文章后半部分我会提供一个基本的2D角色扮演的游戏框架,(开发功能取决于玩家)。

游戏开发环境的配置

首先我们需要一个能安装easyx.h图形界面库的C语言编译器,这里我推荐vsual studio 2022

该编译器功能也是很强大,可以兼容各种编程语言的项目开发,这里我们只使用C语言即可。

visual studio 2022 的 安装

下载链接:Visual Studio 2022 IDE - 适用于软件开发人员的编程工具

选择图中的 community 2022 社区版本(社区版免费),

然后等待安装资源包的下载。

下载好后,弹出来的窗口,点击继续→

稍稍等待一小会儿.....

从左上角可以看到(工作负荷,单个组件,语言包,安装位置)四个头目录。

首先是(工作负荷):我们只需要勾选 “使用C++的桌面开发”。

然后(单个组件):只需要检查一下图中是否勾选了上述选项,一般不用更改(确定好win系统)

(语言包)默认勾选“简体中文”即可

最后(安装位置)要注意的是分成三个不同的子文件夹,你可以在同一个文件夹中新建三个子文件夹,然后将上述三个位置分别选中子文件夹即可,(如果第三个路径不可选,说明你之前下载过该编译器。)关于安装路径在哪个盘都随意。

第三个路径不可选的解决办法也很简单

第一步 :win + R 打开运行

第二步:输入 regedit 打开注册表

第三步:找到该位置

第四步:删除图中除(默认)以外的配置即可

然后点击安装,等待下载完成即可(需要一段时间,内存不小)

下载好后,运行打开,点击图中创建新项目。

选择空项目点击下一步

输入项目名称和路径

右键点击

新建项

定义名

然后就可以写代码了(你可以用helloworld试试)

现在编译器便安装好了,然后还需要安装图形界面库(很快)

easyx库的配置

下载链接:EasyX Graphics Library for C++

点击右侧红色 “下载EasyX”

下载好后,弹出窗口点击下一步。

然后会自动检测你的编译器版本,找到刚下载的Visual C++2022点击安装,,显示安装成功就可以了,重启visual studio 2022,即可。

(最上面的EasyX文档也可以安装,里面包含easyx图形界面库的全部函数用法)

测试easyx库的配置(将下述代码复制进去)


#include<graphics.h> //需安装easyx图形库插件
#include<conio.h>
#include<time.h>
#include<math.h>
#include<sys/timeb.h>struct MyLove
{int NUMS;  //  编号double m;double n;double size;bool Is_show;int x;int y;
};MyLove mylove[400];
int CenterX = 320;
int CenterY = 180;
double Size = 60;
void initdata();  // 初始化数据
void updata();    // 更新
void movedata();  // 平移
void showdata();  // 显示
int* GetRand(int* buf, int count, int range);  // 随机数的生成
void heart(int x0, int y0, int size, COLORREF C);
void HpSleep(int ms);int main()
{initgraph(640, 480);initdata();BeginBatchDraw();while (true){updata();showdata();HpSleep(30);    // 改为精确延时FlushBatchDraw();cleardevice();}EndBatchDraw();_getch();return 0;
}void updata()
{int* buf = (int*)malloc(sizeof(int) * 20);buf = GetRand(buf, 20, (int)(2 * Size / 0.01));movedata();for (int i = 0; i < 20; i++){mylove[i].m = buf[i] * 0.01;mylove[i].n = (((sin(buf[(int)i] * 0.01) * sqrt(fabs(cos(buf[(int)i] * 0.01)))) / (sin(buf[(int)i] * 0.01) + 1.4142)) - 2 * sin(buf[(int)i] * 0.01) + 2);mylove[i].size = Size;mylove[i].NUMS = i / 20;mylove[i].Is_show = true;mylove[i].x = (int)(-Size * mylove[i].n * cos(mylove[i].m) + CenterX);mylove[i].y = (int)(-Size * mylove[i].n * sin(mylove[i].m) + CenterY - mylove[i].size);}for (int i = 20; i < 400; i++){mylove[i].size = mylove[i].size + 1;if (mylove[i].size > 80){mylove[i].size = 80;}mylove[i].NUMS = i / 20;mylove[i].x = (int)(-mylove[i].size * mylove[i].n * cos(mylove[i].m) + CenterX);mylove[i].y = (int)(-mylove[i].size * mylove[i].n * sin(mylove[i].m) + CenterY - mylove[i].size);}
}void movedata()
{for (int i = 399; i > 19; i--){mylove[i] = mylove[i - 20];}
}void showdata()
{settextcolor(RED);wchar_t c = 0x59;    // 0x28 是电话机在 Wingdings 字体中的对应编码for (int i = 0; i < 400; i++){settextstyle(mylove[i].NUMS + 10, 0, _T("Webdings"));setbkmode(TRANSPARENT);outtextxy(mylove[i].x + 20, mylove[i].y + 20, c);}
}int* GetRand(int* buf, int count, int range)
{struct timeb timeSeed;ftime(&timeSeed);srand(timeSeed.time * 1000 + timeSeed.millitm);  // milli timefor (int i = 0; i < count; i++){int randTmp = rand() % range;for (int j = 0; j < i; j++){if (buf[j] == randTmp){break;//检查重复。}}buf[i] = randTmp;}return buf;
}void initdata()
{for (int i = 0; i < 400; i++){mylove[i].NUMS = 0;mylove[i].m = 0;mylove[i].n = 0;mylove[i].size = 0;mylove[i].Is_show = false;mylove[i].x = 0;mylove[i].y = 0;}
}// 精确延时函数(可以精确到 1ms,精度 ±1ms)
// by yangw80<yw80@qq.com>, 2011-5-4
void HpSleep(int ms)
{static clock_t oldclock = clock();    // 静态变量,记录上一次 tickoldclock += ms * CLOCKS_PER_SEC / 1000;  // 更新 tickif (clock() > oldclock)          // 如果已经超时,无需延时oldclock = clock();elsewhile (clock() < oldclock)      // 延时Sleep(1);            // 释放 CPU 控制权,降低 CPU 占用率,精度 10~16ms//      Sleep(0);            // 更高精度、更高 CPU 占用率,精度 1ms
}

复制好后,点击上方绿色空三角运行。(运行效果如下)

以上便完成了全部的环境配置,开启开发游戏之旅

基本游戏逻辑

首先需要包含头文件 #include<easyx.h>来调用图形函数

想要将代码中的效果展现出来,需要一个图形化窗口,并非是黑框框。

所以,第一步初始化一个图形化窗口。

initgraph(800,800);

该函数运行后,除了命令提示符的黑窗口之外,还会产生一个新的窗口,此时窗口内是空的。

如果我们想把外部图片贴上去,需要一个容器储存外部图片

IMAGE img;//声明一个可以存储外部图片的容器

然后储存外部图片进入容器操作,&img是获取容器地址,“photo.png”是需要引入图片的路径

(路径可分为相对路径和绝对路径,我推荐将图片和源程序放到同一个根目录中,既方便引用,又方便后续对于游戏的封装)

loadimage(&img, "photo.png");加载图片进容器

那储存好的图片如何显示在屏幕上,我们需要函数将图片贴到屏幕上。

图中,x,y,前两个函数是指贴入图片的坐标(图片左上角顶点的坐标),

&img参数指贴入的图片容器,确定具体贴入哪个图片。

putimage(x,y, &img);

现在基本的图片显示便有了。

如果我们想让这个图片动起来,很好理解,我们只需要逐渐改变putimage函数的坐标参数就可以。

需要一个循环来刷新新的图像(改变坐标之后的贴图),(还需要刷新屏幕,或者使用背景覆盖法)

1,刷新屏幕:FlushBatchDraw ();(不需要参数) 清除掉上一个贴图,执行目前的贴图。

FlushBatchDraw ();

2,背景覆盖法:可以每次循环(先贴背景(覆盖掉上个位置的贴图)再贴改变坐标后的贴图)

关于图像的移动

#include<stdio.h>
#include<easyx.h>
#include<windows.h>IMAGE back;
IMAGE img;
int main()
{loadimg (&back,"选中背景图片的路径");
loadimg (&img,"选中目标图片的路径");
for(int i=1;i<=500;i++)
{
putimage(0,0,&back);
putimage(i,i,&img);
Sleep(100);
}return 0;
}

其中,&back 是获取背景(IMAGE back 容器存储着与窗口大小一致的背景图片),

所以每次贴图的坐标是0,0,

&img存取的则是需要移动的目标贴图,

每次循环,会在不同坐标贴上目标图片,

由于每次循环都会贴一次背景图,所以会覆盖掉上次的目标贴图,再贴下次的目标贴图,

这样,窗口中就始终只能看到一个目标贴图,且位置在不停发生改变,产生目标图片移动的效果。

(Sleep(100)是没隔100ms也就是每0.1秒刷新一次位置,不然上述循环会在一瞬间结束,无法观察,该函数在Windows.h库内)

上述代码就会产生一个从(1,1)移动到(500,500)的图像。

自主控制实时移动

既然贴图函数的坐标参数决定了目标图像的位置,那么我们如果按下相应的按键改变坐标参数,便可实现用按键控制移动,

我们可以调用一个Windows.h函数 GetAsyncKeyState('D') ,括号内参数是被检测的按键,

如果D( 不分大小写)按键被按下,则返回非零值,否则返回零,

所以,该代码便可检测按键的实时状态,如果按下D则x++(向右移动)

if(GetAsyncKeyState('D'))
x++;

所以整体移动函数模块就是(其中设置了范围,防止目标移动出边界),每次增加或减少的值不是1,而是一个预先定义好的值,可以自由控制移动速度(#define SPEED 10)

void control_move()//控制人物移动
{if (GetAsyncKeyState('D') && hero.x < width)//角色右移{hero.x += SPEED;}if (GetAsyncKeyState('A') && hero.x > 0)//角色左移{hero.x -= SPEED;}if (GetAsyncKeyState('W') && hero.y > 0)//角色上移hero.y -= SPEED;if (GetAsyncKeyState('S') && hero.y < high)//角色下移hero.y += SPEED;
}

然后把这个函数放入主循环内,因为游戏是一致运行的,所以全部需要改变的行为都要放到一个主循环内,由于GetAsyncKeyState是非阻塞性函数,也就是说,即使没有按键按下,主循环依然循环着,游戏持续运行着,只是目标贴图未移动。

int main()
{
....省略
while(1)
{
control_move();
putimage(0,0,&back);
putimage(i,i,&img);
}
return 0;}

关于目标发射物(开发目标远程攻击)

struct bang{
int x;//坐标
int y;
bool live = false;//是否存活
}fire;if(GetAsyncKeyState('j'))
fire.live = true;if(fire.live)
{
fire.x+=SPEED;
putimage(x,y,&img);
}

需要设定发射物的结构体,如果检测到J按键,则让发射物存活,并且自定义逻辑发射出去。

图中假设只有一个发射物,并且横向发射移动,如果需要发射多个,则只需要将结构体变量改成结构体变量数组,然后每次判断存活和移动的操作加一个外层数组遍历,同时同步所有状态。

现在基本的移动和发射逻辑都已说明

我们还需要一些辅助函数代码块,比如时间戳,每间隔多少ms运行一次函数体,且不阻塞主循环

bool timer(int ms, int id)//时间戳
{static DWORD t[500];// 将 clock() 的返回值转换为 DWORD 类型if (static_cast<DWORD>(clock()) - t[id] > static_cast<DWORD>(ms)){t[id] = static_cast<DWORD>(clock());return true;}return false;
}
/*时间戳*/

飞机大战测试

1,头文件

#include<stdio.h>
#include<easyx.h>
#include<conio.h>
#include<time.h>
#include<windows.h>
#include<stdlib.h>

2,设定图形变量存储图片

IMAGE BACK_DROP;
IMAGE PLANE_1;//飞机1
IMAGE PLANE_2;//飞机2
IMAGE DG_1;//敌机1
IMAGE DG_2;//敌机2
IMAGE BULLET_1;//子弹1
IMAGE BULLET_2;//子弹2

3,预定义需要使用参数值   设定 结构体(飞机和敌机)

enum My {WIDTH = 600,HEIGHT = 864,BULLET_NUM = 300,SHIP_SPEED = 2,BULLET_SPEED = 30,ENEMY_NUM = 5,ENEMY_SPEED = 1,
};struct ZT//状态结构体
{int x;int y;  //坐标int hp = 100;//血量bool live = false;//是否存活int width;int height;
};ZT myplane;//飞机ZT BULLET[BULLET_NUM];//子弹ZT ENEMY[ENEMY_NUM];//敌机

基本互动(如果子弹和敌机图像有交叉,则判定击中,减血,血量<=0则判定死亡 )

int play()
{for (int i = 0;i <= ENEMY_NUM;i++){if (!ENEMY[i].live){continue;}for (int j = 0;j < BULLET_NUM;j++){if (!BULLET[i].live){continue;}//检测击中if (BULLET[j].x > ENEMY[i].x && BULLET[j].x<ENEMY[i].x + ENEMY[i].width&& BULLET[j].y>ENEMY[i].y && BULLET[j].y < ENEMY[i].y + ENEMY[i].height){BULLET[i].live = false;ENEMY[i].hp--;}//掉血就去死,ok?if (ENEMY[i].hp == 0){ENEMY[i].live = false;}}}return 0;
}

子弹和敌机的创建

int PLANE_MY()//构建飞机和子弹和敌机
{//绘制飞机putimage(myplane.x, myplane.y, &PLANE_1,NOTSRCERASE);putimage(myplane.x, myplane.y, &PLANE_2, SRCINVERT);//绘制子弹for (int i = 0;i <= BULLET_NUM;i++){if (BULLET[i].live){putimage(BULLET[i].x, BULLET[i].y, &BULLET_2, NOTSRCERASE);putimage(BULLET[i].x, BULLET[i].y, &BULLET_1, SRCINVERT);}}//绘制敌机for (int i = 0;i <= ENEMY_NUM;i++){if (ENEMY[i].live){putimage(ENEMY[i].x, ENEMY[i].y, &DG_2, NOTSRCERASE);putimage(ENEMY[i].x, ENEMY[i].y, &DG_1, SRCINVERT);}}return 0;
}
int createbullet()//子弹创建
{for (int i = 0;i <= BULLET_NUM;i++){if (!BULLET[i].live){BULLET[i].x = myplane.x + 49;BULLET[i].y = myplane.y;BULLET[i].live = true;break;}}return 0;
} 

详细解释一下该部分(使用两张互补的色差图像可以实现透明贴图,后续有优化版本)

putimage(BULLET[i].x, BULLET[i].y, &BULLET_2, NOTSRCERASE);putimage(BULLET[i].x, BULLET[i].y, &BULLET_1, SRCINVERT);

子弹和敌机的移动,以及碰撞检测(检测可以放到里面,也可以独立出一个函数)非

int bulletmove()//子弹移动
{for (int i = 0;i <= BULLET_NUM;i++){if (BULLET[i].live){BULLET[i].y -= BULLET_SPEED;}if (BULLET[i].y < 0){BULLET[i].live = false;}}return 0;
}
int createenemy()
{for (int i = 0;i <= ENEMY_NUM;i++){if (!ENEMY[i].live){ENEMY[i].x = rand() % (WIDTH - 60);ENEMY[i].y = 0;ENEMY[i].live = true;break;}enemyhp(i);}return 0;
}int enemymove()//敌机的移动
{for (int i = 0;i <= ENEMY_NUM;i++){if (ENEMY[i].live){ENEMY[i].y += ENEMY_SPEED;}if (ENEMY[i].y > HEIGHT){ENEMY[i].live = false;}
}return 0;
}
int penzhuang()//碰撞检测
{for (int i = 0;i <= ENEMY_NUM;i++){if (myplane.y <= ENEMY[i].y && myplane.y >= ENEMY[i].y + ENEMY[i].height&& myplane.x >= ENEMY[i].x && myplane.x <= ENEMY[i].x + ENEMY[i].width){myplane.live = false;exit(0);}}
}

需要采用双缓冲绘图法,可以去除游戏循环的卡顿,

BeginBatchDraw();	开始批量绘图。写在循环外
EndBatchDraw();	结束批量绘制,并执行未完成的绘制任务。循坏外,程序结束前
FlushBatchDraw();	执行未完成的绘制任务。写在循环内,构图后,延迟前

飞机大战代码汇总

#include<stdio.h>
#include<easyx.h>
#include<conio.h>
#include<time.h>
#include<windows.h>
#include<stdlib.h>
//牢笼
IMAGE BACK_DROP;
IMAGE PLANE_1;
IMAGE PLANE_2;
IMAGE DG_1;
IMAGE DG_2;
IMAGE BULLET_1;
IMAGE BULLET_2;enum My {WIDTH = 600,HEIGHT = 864,BULLET_NUM = 300,SHIP_SPEED = 2,BULLET_SPEED = 30,ENEMY_NUM = 5,ENEMY_SPEED = 1,
};
const int MAX = 10;struct ZT//状态结构体
{int x;int y;  //坐标int hp = 100;//血量bool live = false;//是否存活int width;int height;
};ZT myplane;//飞机ZT BULLET[BULLET_NUM];//子弹ZT ENEMY[ENEMY_NUM];//敌机int DRAW_BACKDROP()//构造背景图
{putimage(0, 0, &BACK_DROP);return 0;
}
int enemyhp(int i)
{ENEMY[i].hp = 1;ENEMY[i].width = 90;	ENEMY[i].height = 100;return 0;
}int play()
{for (int i = 0;i <= ENEMY_NUM;i++){if (!ENEMY[i].live){continue;}for (int j = 0;j < BULLET_NUM;j++){if (!BULLET[i].live){continue;}//检测击中if (BULLET[j].x > ENEMY[i].x && BULLET[j].x<ENEMY[i].x + ENEMY[i].width&& BULLET[j].y>ENEMY[i].y && BULLET[j].y < ENEMY[i].y + ENEMY[i].height){BULLET[i].live = false;ENEMY[i].hp--;}//掉血就去死,ok?if (ENEMY[i].hp == 0){ENEMY[i].live = false;}}}return 0;
}int PLANE_MY()//构建飞机和子弹和敌机
{//绘制飞机putimage(myplane.x, myplane.y, &PLANE_1,NOTSRCERASE);putimage(myplane.x, myplane.y, &PLANE_2, SRCINVERT);//绘制子弹for (int i = 0;i <= BULLET_NUM;i++){if (BULLET[i].live){putimage(BULLET[i].x, BULLET[i].y, &BULLET_2, NOTSRCERASE);putimage(BULLET[i].x, BULLET[i].y, &BULLET_1, SRCINVERT);}}//绘制敌机for (int i = 0;i <= ENEMY_NUM;i++){if (ENEMY[i].live){putimage(ENEMY[i].x, ENEMY[i].y, &DG_2, NOTSRCERASE);putimage(ENEMY[i].x, ENEMY[i].y, &DG_1, SRCINVERT);}}return 0;
}
int createbullet()//子弹创建
{for (int i = 0;i <= BULLET_NUM;i++){if (!BULLET[i].live){BULLET[i].x = myplane.x + 49;BULLET[i].y = myplane.y;BULLET[i].live = true;break;}}return 0;
} 
bool timer(int ms, int id)//制造随机性
{static DWORD t[MAX];if (clock() - t[id] > ms){t[id] = clock();return true;}return false;}int bulletmove()//子弹移动
{for (int i = 0;i <= BULLET_NUM;i++){if (BULLET[i].live){BULLET[i].y -= BULLET_SPEED;}if (BULLET[i].y < 0){BULLET[i].live = false;}}return 0;
}
int createenemy()
{for (int i = 0;i <= ENEMY_NUM;i++){if (!ENEMY[i].live){ENEMY[i].x = rand() % (WIDTH - 60);ENEMY[i].y = 0;ENEMY[i].live = true;break;}enemyhp(i);}return 0;
}int enemymove()//敌机的移动
{for (int i = 0;i <= ENEMY_NUM;i++){if (ENEMY[i].live){ENEMY[i].y += ENEMY_SPEED;}if (ENEMY[i].y > HEIGHT){ENEMY[i].live = false;}
}return 0;
}
int penzhuang()//碰撞检测
{for (int i = 0;i <= ENEMY_NUM;i++){if (myplane.y <= ENEMY[i].y && myplane.y >= ENEMY[i].y + ENEMY[i].height&& myplane.x >= ENEMY[i].x && myplane.x <= ENEMY[i].x + ENEMY[i].width){myplane.live = false;exit(0);}}
}int main()
{initgraph(600, 1000);loadimage(&BACK_DROP, "back.jpg");loadimage(&PLANE_1,"plane1.png");loadimage(&PLANE_2, "plane2.png");loadimage(&DG_1, "D1.png");loadimage(&DG_2, "D2.png");loadimage(&BULLET_1, "zd1.png");loadimage(&BULLET_2, "zd2.png");myplane.x = 200;myplane.y = 500;myplane.live = true;for (int i = 0;i <= BULLET_NUM;i++){BULLET[i].x = 0;BULLET[i].y = 0;BULLET[i].live = false;}while (1){if (_kbhit())//检测案件发生{char c = _getch();//获取键盘信息switch (c)//控制移动{case 'w'://上if (myplane.y >= 10)myplane.y -= 20;break;case 's'://下if (myplane.y <= 885)myplane.y += 20;break;case 'a'://左if (myplane.x >= 20)myplane.x -= 20;break;case 'd'://右if (myplane.x <= 465)myplane.x += 20;break;case 'j':createbullet();break;}}else {Sleep(100);//基本刷新频率}DRAW_BACKDROP();//构建背景图//FlushBatchDraw();PLANE_MY();//基本原件生成bulletmove();//子弹移动if (timer(500, 0))//控制敌机的出现频率{createenemy();}if (timer(30, 2))	{	enemymove();}play();//打penzhuang();//碰撞检测}//主循环return 0;
}//八个小时,老弟。

 需要链接图片才可以运行哦,(上述说过,需要将目标图片放入指定容器)

上述可能会不太好理解,纯干货,可以参照b站课程

原创优化游戏逻辑的2D角色扮演游戏框架

先展示优化的游戏函数

设定好的全局变量和常量宏

#include <graphics.h>//图形算法库
#include <conio.h>//控制台交流库
#include<windows.h>//系统函数库
#include<stdio.h>//标准输入输出库
#include<time.h>//时间定义库
#include<easyx.h>//图形界面库
#include<math.h>//数学函数库#pragma comment( lib, "MSIMG32.LIB")//图形链接库
//============================================================================预处理
#define M_PI 3.1415926  //圆周率#define HERO_SPEED  1     //hero.移动速度#define HERO_JUMP_SPEED 10 //hero.跳跃帧高度#define HERO_JUMP_NUM  5   //hero.跳跃帧数#define LIGHT_SWORD_SPEED 3 //light_sword.光刃飞行速度#define DRAGON_NUM_MAX 2 //龙同时存在最大数量#define DRAGON_SPEED 2 //龙的移动速度
//============================================================================常量宏
int HEIGHT = 1000;//当前屏幕设备的高度(单位毫米)int WIDTH = 1700;//当前屏幕设备的宽度(单位毫米)IMAGE back;//背景IMAGE stop_imgR[13];//静止 右 待机动作IMAGE stop_imgL[13];//静止 左 待机动作IMAGE run_imgR[5];//奔跑 右 动作IMAGE run_imgL[5];//奔跑 左 动作IMAGE raise_sword;//举剑的动作IMAGE light_sword_imgR;//右光刃
IMAGE light_sword_imgL;//左光刃IMAGE HP_img;//血量显示IMAGE MP_img;//蓝量显示IMAGE TX_ADD_HP[16]; //加血特效图IMAGE dragon_imgR[7]; //右 龙图片
IMAGE dragon_imgL[7]; //左 龙图片IMAGE light_effect[31]; //受击光效图片int run_num = 1;//移动动作循环底码int stop_num = 1;//待机动作循环底码int TX_ADD_HP_num = 1;//特效图像循环底码int dragon_img_num = 1;//龙图运动循环底码int Affected_img_num = 1;//基础光刃受击特效图循环底码bool Previous_direction = true;//前一时刻方向判定量int dragon_rand_move_num[DRAGON_NUM_MAX + 1];//龙的随机运动底码
int dragon_rand_pursuit_num[DRAGON_NUM_MAX + 1];//龙的随机追击底码
//=============================================================================全局变量

设定好的结构体

struct role {int x = 200;			//hero.x坐标int y = 100;			//hero.y坐标int blood = 100;    //hero.血量int blue = 100;     //hero.蓝量bool live = true;   //hero.存活bool ground = true; //hero.触地
}hero;
/*人物状态结构体*/struct sword {int x = 0;//光刃x坐标int y = 0;//光刃y坐标bool live = false;//光刃存活bool direction = true;//光刃方向
};
/*基本远程攻击结构体*/struct sword light_sword[11];//光刃struct Special_effects {int x = 1; //特效.x坐标int y = 1; //特效.y坐标bool live = false; //是否激活
};/*基本特效结构体*/
struct Special_effects add_blood; //加血特效
struct Special_effects Affected_effect[11];//基础光刃受击效果struct move {//基本移动体坐标int x = 800;int y = 500;//坐标int HP = 100;//血量int speed_x = 10;int speed_y = 10;//速度bool live = false;//是否存活bool if_move = true; //是否能移动bool direction = true;//向左向右bool pursuit = true;//是否追击int die_num_zhen = 0;//死亡后的帧数
};
//基本敌对目标结构体
struct move dragon[DRAGON_NUM_MAX + 1]; //敌龙  同时最多存在五只//==============================================================================结构体

加载图片

void load()//加载图片素材
{loadimage(&back, "back.png", 1700, 1000);//背景图的加载loadimage(&HP_img, "HP.png", 100, 50);//血条HP图片加载loadimage(&MP_img, "MP.png", 100, 50);//蓝条MP图片加载//loadimage(&raise_sword, "attack.png", 400, 400);//攻击举剑动作图片加载loadimage(&light_sword_imgR, "光刃.png", 400, 400);//右光刃攻击特效图片加载loadimage(&light_sword_imgL, "光刃f.png", 400, 400);//左光刃攻击特效图片加载for (int i = 1;i <= 9;i++)//01.png  02.png  03.png  04........{char str[50];sprintf_s(str, "0%d.png", i);loadimage(&stop_imgR[i], str, 200, 200);//加载待机动作}for (int x = 10;x <= 12;x++){char str2[50];sprintf_s(str2, "%d.png", x);loadimage(&stop_imgR[x], str2, 200, 200);//加载 右 待机动作}for (int y = 1;y <= 4;y++){char str3[50];char str4[50];sprintf_s(str3, "run%d.png", y);loadimage(&run_imgR[y], str3, 180, 180);//加载 右 奔跑动作sprintf_s(str4, "frun%d.png", y);loadimage(&run_imgL[y], str4, 180, 180);//加载 左 奔跑动作}for (int a = 1; a <= 12; a++){char str5[50];sprintf_s(str5, "fs%d.png", a);loadimage(&stop_imgL[a], str5, 200, 200);//加载 左 待机动作}for (int i = 1;i <= 15;i++)//加载加血特效{char str6[50];sprintf_s(str6, "tx%d.png", i);loadimage(&TX_ADD_HP[i], str6, 400, 400);}for (int i = 1;i <= 6;i++)//加载龙的素材图{char str7[50];sprintf_s(str7, "dg%d.png", i);loadimage(&dragon_imgR[i], str7, 200, 200);char str8[50];sprintf_s(str8, "dgf%d.png", i);loadimage(&dragon_imgL[i], str8, 200, 200);}for (int i = 1;i <= 30;i++)//加载受击光效{char str9[50];sprintf_s(str9, "gx%d.png", i);loadimage(&light_effect[i], str9, 200, 200);}}
//加载图片素材

时间戳

bool timer(int ms, int id)//时间戳
{static DWORD t[500];// 将 clock() 的返回值转换为 DWORD 类型if (static_cast<DWORD>(clock()) - t[id] > static_cast<DWORD>(ms)){t[id] = static_cast<DWORD>(clock());return true;}return false;
}
/*时间戳*/

获取屏幕参数(全屏的关键)

/*获取当前屏幕的参数*/void transparentimage3(IMAGE* dstimg, int x, int y, IMAGE* srcimg) //png_windows透明贴图
{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);
}

前面我们每个目标都要采用两张叠加的图片才能实现透明贴图,而该函数只需要使用wps工具将单个图片背景设置成win的透明背景,然后插入该函数可自动剔除掉背景


void transparentimage3(IMAGE* dstimg, int x, int y, IMAGE* srcimg) //png_windows透明贴图
{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);
}
/*windows.h 的png透明贴图工具*/

原创函数,用于可控范围的切换图片目标,实现特定范围的人物移动行走效果,和特效

void random_nums()//一帧内生成十个的随机数,前五个赋值给龙的判断移动变量,后五个给龙的追击判断变量
{int num = 10;int used[100] = { 0 };  // 标记数组,初始化为 0int numbers[10];srand((unsigned int)time(NULL));  // 初始化随机数种子for (int i = 0; i < num; i++) {int num;do {num = rand() % 100;  // 生成 0 到 RANGE - 1 之间的随机数} while (used[num]);  // 如果该数字已被使用,则重新生成numbers[i] = num;used[num] = 1;  // 标记该数字已被使用}// 输出生成的随机数for (int i = 1; i <= num / 2; i++) {dragon_rand_move_num[i] = numbers[i];}for (int i = num / 2 + 1;i <= num;i++){dragon_rand_pursuit_num[i - num / 2] = numbers[i];}
}
//一帧内生成特定数量的随机数int cycle_count(int min, int max, int type)//调用返回值从min~max之间的单向循环
{static int count[10];while (count[type] < min - 1)count[type]++;count[type]++;if (count[type] > max)count[type] = min;return count[type];
}//不同type参数分配不同的静态变量count
/*可控范围的底码循环,用于运动图片的切换*/
控制特效的单次便利图像运行,单次便利结束后,将传入的bool类型指针变为falsevoid draw_effect_ADD_blood()
{if (add_blood.live)transparentimage3(NULL, hero.x - 100, hero.y - 150, &TX_ADD_HP[TX_ADD_HP_num]);
}

控制移动(通过检测上次的移动方向,可以知道某时刻角色的面朝向,从而决定贴图朝向)

void control_hero()//控制人物移动
{if (GetAsyncKeyState('D') && hero.x < 1550)//角色右移{hero.x += HERO_SPEED;Previous_direction = true;}if (GetAsyncKeyState('A') && hero.x > -5)//角色左移{hero.x -= HERO_SPEED;Previous_direction = false;}if (GetAsyncKeyState('W') && hero.y > -5)//角色上移hero.y -= HERO_SPEED;if (GetAsyncKeyState('S') && hero.y < 850)//角色下移hero.y += HERO_SPEED;
}
/*控制角色移动*/

发射物光刃一体化程序

//创造光刃void move_sword()
{for (int i = 1;i <= 10;i++){if (light_sword[i].live){if (light_sword[i].direction)//是否朝右light_sword[i].x += LIGHT_SWORD_SPEED;elselight_sword[i].x -= LIGHT_SWORD_SPEED;}}
}
//移动光刃void draw_sword()
{for (int i = 1;i <= 10;i++)if (light_sword[i].live){if (light_sword[i].direction)transparentimage3(NULL, light_sword[i].x, light_sword[i].y, &light_sword_imgR);elsetransparentimage3(NULL, light_sword[i].x, light_sword[i].y, &light_sword_imgL);}
}
//绘画光刃void draw_HPMP()
{transparentimage3(NULL, 10, 10, &HP_img);transparentimage3(NULL, 10, 70, &MP_img);
}//对基本光刃受击特效的绘画void Attack_detection()
{for (int i = 1;i <= 10;i++){int ctr = 1;for (int a = 1;a <= DRAGON_NUM_MAX;a++){if (light_sword[i].x - dragon[a].x<200 && light_sword[i].x - dragon[a].x>-200 && light_sword[i].live)if (dragon[a].live)if (light_sword[i].y - dragon[a].y<0 && light_sword[i].y - dragon[a].y>-200){dragon[a].HP -= 20;Affected_effect[i].x = dragon[a].x + 50;Affected_effect[i].y = dragon[a].y + 30;Affected_effect[i].live = true;light_sword[i].live = false;ctr = 0;break;}}if (ctr == 0)break;}
}
//基本光刃命中判定以及反馈

游戏特效

//创造加血特效 (内含按键 U )int control_effect_count(int min, int max, bool* live, int type)//控制特效的单次循环运行
{static int count[10] = { min - 1 };count[type]++;if (count[type] >= max + 1){*live = false;count[type] = min - 1;return count[type] + 1;}return count[type];
}//加血特效的绘画void select_dragon_speed() //根据距离分配速度
{for (int i = 1;i <= DRAGON_NUM_MAX;i++)if (dragon[i].pursuit && dragon[i].live){//同时满足追击和移动和存活条件后,赋值追击速度double cx = (double)(dragon[i].x - hero.x);  //敌我x坐标差double cy = (double)(dragon[i].y - hero.y);  //敌我y坐标差double cz = sqrt(cx * cx + cy * cy);     //绝对距离if (cx == 0 && cy == 0)//防止敌我目标重合带来的除0bug{cz = 1;}double cxz = cx / cz;double cyz = cy / cz;//移动方向参数dragon[i].speed_x = (int)(-DRAGON_SPEED * cxz);dragon[i].speed_y = (int)(-DRAGON_SPEED * cyz);//分配速度}
}

用算法赋予目标自动寻敌并且追击的效果

//根据敌我位移分配速度和状态void dragon_move()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].live && dragon[i].pursuit){//基本移动dragon[i].x += dragon[i].speed_x;dragon[i].y += dragon[i].speed_y;}if (dragon[i].speed_x > 0)dragon[i].direction = false;elsedragon[i].direction = true;}
}

敌对目标的创建

void dragon_move()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].live && dragon[i].pursuit){//基本移动dragon[i].x += dragon[i].speed_x;dragon[i].y += dragon[i].speed_y;}if (dragon[i].speed_x > 0)dragon[i].direction = false;elsedragon[i].direction = true;}
}
//龙的移动void draw_dragon()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++)if (dragon[i].live){if (dragon[i].direction)transparentimage3(NULL, dragon[i].x, dragon[i].y, &dragon_imgR[dragon_img_num]);elsetransparentimage3(NULL, dragon[i].x, dragon[i].y, &dragon_imgL[dragon_img_num]);}
}
//龙的绘画void Stop_the_Dragon_Crossing_Realm()//阻止龙的越界
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].x <= 20)// 注意30-20要 > speed_x,防止瞬间越界{dragon[i].x = 30;dragon[i].speed_x = -dragon[i].speed_x;}if (dragon[i].x >= 1680)// 注意980-970要 > speed_x,防止瞬间越界{dragon[i].x = 1670;dragon[i].speed_x = -dragon[i].speed_x;}if (dragon[i].y <= 20)// 注意30-20要 > speed_y,防止瞬间越界{dragon[i].y = 30;dragon[i].speed_y = -dragon[i].speed_y;}if (dragon[i].y >= 980)// 注意1680-1670要 > speed_y,防止瞬间越界{dragon[i].y = 970;dragon[i].speed_y = -dragon[i].speed_y;}}
}
//阻止龙越界void creat_dragon()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].HP <= 0 && dragon[i].live){dragon[i].die_num_zhen = 0;dragon[i].live = false;//dragon[i].deathTime = clock(); // 更新死亡时间}if (!dragon[i].live){if (dragon[i].die_num_zhen <= 4)//4*0.5=2scontinue;//if (clock() - dragon[i].deathTime < 2000) continue; // 5 秒内不重新生成dragon[i].x = 800;dragon[i].y = 500;dragon[i].live = true;dragon[i].HP = 100; // 重新生成时恢复血量break;}}
}
//创造龙,附带空地才创造void dragon_x_dragon()//两条龙之间保持距离,避免重叠
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){for (int a = 1;a <= i;a++){if (dragon[i].x - dragon[a].x <= 200 && dragon[i].x - dragon[a].x > 0){//  dragon[i]在左  <- -> dragon[i+1]在右 if (dragon[a].speed_x > 0)dragon[a].speed_x = 0;//如果左边的在右移则水平停止if (dragon[i].speed_x < 0)dragon[i].speed_x = 0;}if (dragon[a].x - dragon[i].x <= 200 && dragon[a].x - dragon[i].x > 0){//   dragon[i+1]在左 <- ->  dragon[i]在右if (dragon[i].speed_x > 0)dragon[i].speed_x = 0;if (dragon[a].speed_x < 0)dragon[a].speed_x = 0;}}}
}
//两条龙之间保持距离,避免重叠,该函数需要放到获取所有速度之后void draw_light_effect()
{for (int i = 1;i <= 10;i++)if (Affected_effect[i].live)transparentimage3(NULL, Affected_effect[i].x, Affected_effect[i].y, &light_effect[Affected_img_num]);
}

组合的mian函数主运行块

int main()
{Get_Height_And_Width(&HEIGHT, &WIDTH);//获取屏幕参数,构建全屏窗口initgraph(WIDTH, HEIGHT);//初始化图形界面窗口load();//加载图片putback();//张贴背景BeginBatchDraw();//开启双缓冲绘图srand(time(0));//设定随机种子while (true){putback();//背景绘画control_hero();//控制角色移动   (控制按键:W,A,S,D )Select_texture();//控制选择人物状态并绘图出人物timer_thing();//需要时间延迟的事件集合(内含控制按键J)select_dragon_speed();//赋予龙追击的能力Attack_detection();//受击检测dragon_x_dragon();//防止龙的重叠Stop_the_Dragon_Crossing_Realm();//绘画{draw_sword();//光刃的绘画draw_HPMP();//状态条的绘画draw_effect_ADD_blood();//加血特效的绘画draw_dragon();//绘画龙draw_light_effect();}//移动{move_sword();//光刃的移动}{creat_add_HP();//创造加血特效 (内含按键 U )}beyond_sword_boundary();//超出边界的光刃判断消失FlushBatchDraw();//刷新缓冲绘图//cleardevice();}EndBatchDraw();//结束缓冲绘图exit(0);//退出程序return 0;
}

该游戏总代码

#include <graphics.h>//图形算法库
#include <conio.h>//控制台交流库
#include<windows.h>//系统函数库
#include<stdio.h>//标准输入输出库
#include<time.h>//时间定义库
#include<easyx.h>//图形界面库
#include<math.h>//数学函数库#pragma comment( lib, "MSIMG32.LIB")//图形链接库
//============================================================================预处理
#define M_PI 3.1415926  //圆周率#define HERO_SPEED  1     //hero.移动速度#define HERO_JUMP_SPEED 10 //hero.跳跃帧高度#define HERO_JUMP_NUM  5   //hero.跳跃帧数#define LIGHT_SWORD_SPEED 3 //light_sword.光刃飞行速度#define DRAGON_NUM_MAX 2 //龙同时存在最大数量#define DRAGON_SPEED 2 //龙的移动速度
//============================================================================常量宏
int HEIGHT = 1000;//当前屏幕设备的高度(单位毫米)int WIDTH = 1700;//当前屏幕设备的宽度(单位毫米)IMAGE back;//背景IMAGE stop_imgR[13];//静止 右 待机动作IMAGE stop_imgL[13];//静止 左 待机动作IMAGE run_imgR[5];//奔跑 右 动作IMAGE run_imgL[5];//奔跑 左 动作IMAGE raise_sword;//举剑的动作IMAGE light_sword_imgR;//右光刃
IMAGE light_sword_imgL;//左光刃IMAGE HP_img;//血量显示IMAGE MP_img;//蓝量显示IMAGE TX_ADD_HP[16]; //加血特效图IMAGE dragon_imgR[7]; //右 龙图片
IMAGE dragon_imgL[7]; //左 龙图片IMAGE light_effect[31]; //受击光效图片int run_num = 1;//移动动作循环底码int stop_num = 1;//待机动作循环底码int TX_ADD_HP_num = 1;//特效图像循环底码int dragon_img_num = 1;//龙图运动循环底码int Affected_img_num = 1;//基础光刃受击特效图循环底码bool Previous_direction = true;//前一时刻方向判定量int dragon_rand_move_num[DRAGON_NUM_MAX + 1];//龙的随机运动底码
int dragon_rand_pursuit_num[DRAGON_NUM_MAX + 1];//龙的随机追击底码
//=============================================================================全局变量struct role {int x = 200;			//hero.x坐标int y = 100;			//hero.y坐标int blood = 100;    //hero.血量int blue = 100;     //hero.蓝量bool live = true;   //hero.存活bool ground = true; //hero.触地
}hero;
/*人物状态结构体*/struct sword {int x = 0;//光刃x坐标int y = 0;//光刃y坐标bool live = false;//光刃存活bool direction = true;//光刃方向
};
/*基本远程攻击结构体*/struct sword light_sword[11];//光刃struct Special_effects {int x = 1; //特效.x坐标int y = 1; //特效.y坐标bool live = false; //是否激活
};/*基本特效结构体*/
struct Special_effects add_blood; //加血特效
struct Special_effects Affected_effect[11];//基础光刃受击效果struct move {//基本移动体坐标int x = 800;int y = 500;//坐标int HP = 100;//血量int speed_x = 10;int speed_y = 10;//速度bool live = false;//是否存活bool if_move = true; //是否能移动bool direction = true;//向左向右bool pursuit = true;//是否追击int die_num_zhen = 0;//死亡后的帧数
};
//基本敌对目标结构体
struct move dragon[DRAGON_NUM_MAX + 1]; //敌龙  同时最多存在五只//==============================================================================结构体
void load()//加载图片素材
{loadimage(&back, "back.png", 1700, 1000);//背景图的加载loadimage(&HP_img, "HP.png", 100, 50);//血条HP图片加载loadimage(&MP_img, "MP.png", 100, 50);//蓝条MP图片加载//loadimage(&raise_sword, "attack.png", 400, 400);//攻击举剑动作图片加载loadimage(&light_sword_imgR, "光刃.png", 400, 400);//右光刃攻击特效图片加载loadimage(&light_sword_imgL, "光刃f.png", 400, 400);//左光刃攻击特效图片加载for (int i = 1;i <= 9;i++)//01.png  02.png  03.png  04........{char str[50];sprintf_s(str, "0%d.png", i);loadimage(&stop_imgR[i], str, 200, 200);//加载待机动作}for (int x = 10;x <= 12;x++){char str2[50];sprintf_s(str2, "%d.png", x);loadimage(&stop_imgR[x], str2, 200, 200);//加载 右 待机动作}for (int y = 1;y <= 4;y++){char str3[50];char str4[50];sprintf_s(str3, "run%d.png", y);loadimage(&run_imgR[y], str3, 180, 180);//加载 右 奔跑动作sprintf_s(str4, "frun%d.png", y);loadimage(&run_imgL[y], str4, 180, 180);//加载 左 奔跑动作}for (int a = 1; a <= 12; a++){char str5[50];sprintf_s(str5, "fs%d.png", a);loadimage(&stop_imgL[a], str5, 200, 200);//加载 左 待机动作}for (int i = 1;i <= 15;i++)//加载加血特效{char str6[50];sprintf_s(str6, "tx%d.png", i);loadimage(&TX_ADD_HP[i], str6, 400, 400);}for (int i = 1;i <= 6;i++)//加载龙的素材图{char str7[50];sprintf_s(str7, "dg%d.png", i);loadimage(&dragon_imgR[i], str7, 200, 200);char str8[50];sprintf_s(str8, "dgf%d.png", i);loadimage(&dragon_imgL[i], str8, 200, 200);}for (int i = 1;i <= 30;i++)//加载受击光效{char str9[50];sprintf_s(str9, "gx%d.png", i);loadimage(&light_effect[i], str9, 200, 200);}}
//加载图片素材bool timer(int ms, int id)//时间戳
{static DWORD t[500];// 将 clock() 的返回值转换为 DWORD 类型if (static_cast<DWORD>(clock()) - t[id] > static_cast<DWORD>(ms)){t[id] = static_cast<DWORD>(clock());return true;}return false;
}
/*时间戳*/void Get_Height_And_Width(int* H, int* W)//获取当前屏幕的参数
{int screenWidth = *W = GetSystemMetrics(SM_CXSCREEN);int screenHeight = *H = GetSystemMetrics(SM_CYSCREEN);
}
/*获取当前屏幕的参数*/void transparentimage3(IMAGE* dstimg, int x, int y, IMAGE* srcimg) //png_windows透明贴图
{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);
}
/*windows.h 的png透明贴图工具*/void random_nums()//一帧内生成十个的随机数,前五个赋值给龙的判断移动变量,后五个给龙的追击判断变量
{int num = 10;int used[100] = { 0 };  // 标记数组,初始化为 0int numbers[10];srand((unsigned int)time(NULL));  // 初始化随机数种子for (int i = 0; i < num; i++) {int num;do {num = rand() % 100;  // 生成 0 到 RANGE - 1 之间的随机数} while (used[num]);  // 如果该数字已被使用,则重新生成numbers[i] = num;used[num] = 1;  // 标记该数字已被使用}// 输出生成的随机数for (int i = 1; i <= num / 2; i++) {dragon_rand_move_num[i] = numbers[i];}for (int i = num / 2 + 1;i <= num;i++){dragon_rand_pursuit_num[i - num / 2] = numbers[i];}
}
//一帧内生成特定数量的随机数int cycle_count(int min, int max, int type)//调用返回值从min~max之间的单向循环
{static int count[10];while (count[type] < min - 1)count[type]++;count[type]++;if (count[type] > max)count[type] = min;return count[type];
}//不同type参数分配不同的静态变量count
/*可控范围的底码循环,用于运动图片的切换*/void control_hero()//控制人物移动
{if (GetAsyncKeyState('D') && hero.x < 1550)//角色右移{hero.x += HERO_SPEED;Previous_direction = true;}if (GetAsyncKeyState('A') && hero.x > -5)//角色左移{hero.x -= HERO_SPEED;Previous_direction = false;}if (GetAsyncKeyState('W') && hero.y > -5)//角色上移hero.y -= HERO_SPEED;if (GetAsyncKeyState('S') && hero.y < 850)//角色下移hero.y += HERO_SPEED;
}
/*控制角色移动*/void creat_sword()
{if (GetAsyncKeyState('J')){for (int i = 1;i <= 10;i++){if (!light_sword[i].live){light_sword[i].live = true;light_sword[i].x = hero.x - 100;//光刃继承人物前坐标释放light_sword[i].y = hero.y - 100;if (Previous_direction)//是否朝右light_sword[i].direction = true;elselight_sword[i].direction = false;break;}}}
}
//创造光刃void move_sword()
{for (int i = 1;i <= 10;i++){if (light_sword[i].live){if (light_sword[i].direction)//是否朝右light_sword[i].x += LIGHT_SWORD_SPEED;elselight_sword[i].x -= LIGHT_SWORD_SPEED;}}
}
//移动光刃void draw_sword()
{for (int i = 1;i <= 10;i++)if (light_sword[i].live){if (light_sword[i].direction)transparentimage3(NULL, light_sword[i].x, light_sword[i].y, &light_sword_imgR);elsetransparentimage3(NULL, light_sword[i].x, light_sword[i].y, &light_sword_imgL);}
}
//绘画光刃void draw_HPMP()
{transparentimage3(NULL, 10, 10, &HP_img);transparentimage3(NULL, 10, 70, &MP_img);
}
//状态栏的构建void Select_texture()//选择任务状态并且绘图
{if (GetAsyncKeyState('D'))//是否按下D{transparentimage3(NULL, hero.x, hero.y, &run_imgR[run_num]);}else {//没有按下Dif (GetAsyncKeyState('A'))//是否按下A{transparentimage3(NULL, hero.x, hero.y, &run_imgL[run_num]);}else {//没有按下Aif (GetAsyncKeyState('W') || GetAsyncKeyState('S')){//是否按下W或Sif (Previous_direction)//是否右朝向transparentimage3(NULL, hero.x, hero.y, &run_imgR[run_num]);//右朝向上下移动else//左朝向transparentimage3(NULL, hero.x, hero.y, &run_imgL[run_num]);//左朝向上下移动}else {//待机动作if (Previous_direction)//是否右朝向transparentimage3(NULL, hero.x, hero.y, &stop_imgR[stop_num]);//待机右朝向else//左朝向transparentimage3(NULL, hero.x, hero.y, &stop_imgL[stop_num]);//待机左朝向}}}
}
//人物动作状态的选择判断绘图void putback()
{putimage(0, 0, &back);
}
//背景图的绘画void beyond_sword_boundary()
{for (int i = 1;i <= 10;i++)if (light_sword[i].x<0 || light_sword[i].x>WIDTH)light_sword[i].live = false;}
//超出边界的光刃判定消失void creat_add_HP()//创造加血特效 (内含按键 U )
{//触发条件,检验按键“U" 并且 特效不存活 并且 特效已完成if (GetAsyncKeyState('U') && !add_blood.live)add_blood.live = true;
}
//创造加血特效 (内含按键 U )int control_effect_count(int min, int max, bool* live, int type)//控制特效的单次循环运行
{static int count[10] = { min - 1 };count[type]++;if (count[type] >= max + 1){*live = false;count[type] = min - 1;return count[type] + 1;}return count[type];
}
控制特效的单次便利图像运行,单次便利结束后,将传入的bool类型指针变为falsevoid draw_effect_ADD_blood()
{if (add_blood.live)transparentimage3(NULL, hero.x - 100, hero.y - 150, &TX_ADD_HP[TX_ADD_HP_num]);
}
//加血特效的绘画void select_dragon_speed() //根据距离分配速度
{for (int i = 1;i <= DRAGON_NUM_MAX;i++)if (dragon[i].pursuit && dragon[i].live){//同时满足追击和移动和存活条件后,赋值追击速度double cx = (double)(dragon[i].x - hero.x);  //敌我x坐标差double cy = (double)(dragon[i].y - hero.y);  //敌我y坐标差double cz = sqrt(cx * cx + cy * cy);     //绝对距离if (cx == 0 && cy == 0)//防止敌我目标重合带来的除0bug{cz = 1;}double cxz = cx / cz;double cyz = cy / cz;//移动方向参数dragon[i].speed_x = (int)(-DRAGON_SPEED * cxz);dragon[i].speed_y = (int)(-DRAGON_SPEED * cyz);//分配速度}
}
//根据敌我位移分配速度和状态void dragon_move()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].live && dragon[i].pursuit){//基本移动dragon[i].x += dragon[i].speed_x;dragon[i].y += dragon[i].speed_y;}if (dragon[i].speed_x > 0)dragon[i].direction = false;elsedragon[i].direction = true;}
}
//龙的移动void draw_dragon()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++)if (dragon[i].live){if (dragon[i].direction)transparentimage3(NULL, dragon[i].x, dragon[i].y, &dragon_imgR[dragon_img_num]);elsetransparentimage3(NULL, dragon[i].x, dragon[i].y, &dragon_imgL[dragon_img_num]);}
}
//龙的绘画void Stop_the_Dragon_Crossing_Realm()//阻止龙的越界
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].x <= 20)// 注意30-20要 > speed_x,防止瞬间越界{dragon[i].x = 30;dragon[i].speed_x = -dragon[i].speed_x;}if (dragon[i].x >= 1680)// 注意980-970要 > speed_x,防止瞬间越界{dragon[i].x = 1670;dragon[i].speed_x = -dragon[i].speed_x;}if (dragon[i].y <= 20)// 注意30-20要 > speed_y,防止瞬间越界{dragon[i].y = 30;dragon[i].speed_y = -dragon[i].speed_y;}if (dragon[i].y >= 980)// 注意1680-1670要 > speed_y,防止瞬间越界{dragon[i].y = 970;dragon[i].speed_y = -dragon[i].speed_y;}}
}
//阻止龙越界void creat_dragon()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].HP <= 0 && dragon[i].live){dragon[i].die_num_zhen = 0;dragon[i].live = false;//dragon[i].deathTime = clock(); // 更新死亡时间}if (!dragon[i].live){if (dragon[i].die_num_zhen <= 4)//4*0.5=2scontinue;//if (clock() - dragon[i].deathTime < 2000) continue; // 5 秒内不重新生成dragon[i].x = 800;dragon[i].y = 500;dragon[i].live = true;dragon[i].HP = 100; // 重新生成时恢复血量break;}}
}
//创造龙,附带空地才创造void dragon_x_dragon()//两条龙之间保持距离,避免重叠
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){for (int a = 1;a <= i;a++){if (dragon[i].x - dragon[a].x <= 200 && dragon[i].x - dragon[a].x > 0){//  dragon[i]在左  <- -> dragon[i+1]在右 if (dragon[a].speed_x > 0)dragon[a].speed_x = 0;//如果左边的在右移则水平停止if (dragon[i].speed_x < 0)dragon[i].speed_x = 0;}if (dragon[a].x - dragon[i].x <= 200 && dragon[a].x - dragon[i].x > 0){//   dragon[i+1]在左 <- ->  dragon[i]在右if (dragon[i].speed_x > 0)dragon[i].speed_x = 0;if (dragon[a].speed_x < 0)dragon[a].speed_x = 0;}}}
}
//两条龙之间保持距离,避免重叠,该函数需要放到获取所有速度之后void draw_light_effect()
{for (int i = 1;i <= 10;i++)if (Affected_effect[i].live)transparentimage3(NULL, Affected_effect[i].x, Affected_effect[i].y, &light_effect[Affected_img_num]);
}
//对基本光刃受击特效的绘画void Attack_detection()
{for (int i = 1;i <= 10;i++){int ctr = 1;for (int a = 1;a <= DRAGON_NUM_MAX;a++){if (light_sword[i].x - dragon[a].x<200 && light_sword[i].x - dragon[a].x>-200 && light_sword[i].live)if (dragon[a].live)if (light_sword[i].y - dragon[a].y<0 && light_sword[i].y - dragon[a].y>-200){dragon[a].HP -= 20;Affected_effect[i].x = dragon[a].x + 50;Affected_effect[i].y = dragon[a].y + 30;Affected_effect[i].live = true;light_sword[i].live = false;ctr = 0;break;}}if (ctr == 0)break;}
}
//基本光刃命中判定以及反馈//=========================================================================功能函数的构建void timer_thing()//需要时间延迟的事件集合
{if (timer(100, 1)){//角色待机动作速率stop_num = cycle_count(1, 12, 1);}if (timer(60, 2)){//角色奔跑动作速率run_num = cycle_count(1, 4, 2);}if (timer(50, 3))  //防止一瞬间释放过多的光刃{creat_sword();//控制光刃释放(控制按键:J )}if (timer(50, 4) && add_blood.live)//控制加血特效图片运行的延迟{TX_ADD_HP_num = control_effect_count(1, 15, &add_blood.live, 1);}if (timer(100, 5)) //控制龙的动作图片{dragon_img_num = cycle_count(1, 6, 3);}if (timer(2000, 7)){creat_dragon();//创造龙}if (timer(10, 8)){dragon_move();//龙的移动}if (timer(10, 9)){//基础光刃攻击受击特效速度控制for (int i = 1;i <= 10;i++)Affected_img_num = control_effect_count(1, 30, &Affected_effect[i].live, 2);}if (timer(500, 10)){for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (!dragon[i].live)dragon[i].die_num_zhen++;}}}
//需要时间延迟的事件集合,内含J按键int main()
{Get_Height_And_Width(&HEIGHT, &WIDTH);//获取屏幕参数,构建全屏窗口initgraph(WIDTH, HEIGHT);//初始化图形界面窗口load();//加载图片putback();//张贴背景BeginBatchDraw();//开启双缓冲绘图srand(time(0));//设定随机种子while (true){putback();//背景绘画control_hero();//控制角色移动   (控制按键:W,A,S,D )Select_texture();//控制选择人物状态并绘图出人物timer_thing();//需要时间延迟的事件集合(内含控制按键J)select_dragon_speed();//赋予龙追击的能力Attack_detection();//受击检测dragon_x_dragon();//防止龙的重叠Stop_the_Dragon_Crossing_Realm();//绘画{draw_sword();//光刃的绘画draw_HPMP();//状态条的绘画draw_effect_ADD_blood();//加血特效的绘画draw_dragon();//绘画龙draw_light_effect();}//移动{move_sword();//光刃的移动}{creat_add_HP();//创造加血特效 (内含按键 U )}beyond_sword_boundary();//超出边界的光刃判断消失FlushBatchDraw();//刷新缓冲绘图//cleardevice();}EndBatchDraw();//结束缓冲绘图exit(0);//退出程序return 0;
}

游戏效果

 

该游戏资源

 游戏已经被我封装好,分享到了网盘上,感情兴趣的可以尝试一下。

通过网盘分享的文件:封装游戏测试.zip链接:

https://pan.baidu.com/s/1indM1boxj6QvrpsaIH_85Q?pwd=LONG

提取码: LONG

提取后使用方法:

使用文件资源管理器打开,点击Debug

点击该运行文件,

 点击全部解压缩

同样再找到该运行文件并运行

一步步操作

上述您选择安装的指定位置(一般和解压后的文件一个位置)

就会出现一个软件,点击运行,就可以玩了

(注意:同时打开的有一个黑框框,最小化即可,不要关掉,他会获取用户的按键操作)

(W A S D 移动 U 特效 J 攻击)(只是一个基础2D游戏框架,未添加太多功能,感兴趣的小伙伴可以按照喜好尝试添加)

本文结束....感谢观看。

相关文章:

从零到有的游戏开发(visual studio 2022 + easyx.h)

引言 本文章适用于C语言初学者掌握基本的游戏开发&#xff0c; 我将用详细的步骤引领大家如何开发属于自己的游戏。 作者温馨提示&#xff1a;不要认为开发游戏很难&#xff0c;一些基本的游戏逻辑其实很简单&#xff0c; 关于游戏的开发环境也不用担心&#xff0c;我会详细…...

海外高防服务器延迟优化——跨国业务安全加速的底层逻辑

本文深度解析海外高防服务器延迟优化的技术实现路径&#xff0c;揭示跨国业务场景下DDoS防护与网络性能的平衡法则。从物理线路选择到协议栈调优&#xff0c;从流量调度算法到安全检测机制重构&#xff0c;系统阐述降低20ms-50ms延迟的工程实践方案&#xff0c;并附2023年东南亚…...

常用环境部署(二十六)——Centos搭建MQTT服务端EMQX

1、安装docker https://blog.csdn.net/wd520521/article/details/112609796?spm1011.2415.3001.5331 2、安装EMQX4.4.4 &#xff08;1&#xff09;使用docker pull指令安装emqx镜像 docker pull emqx/emqx:4.4.4 &#xff08;2&#xff09;查看镜像 docker images 3、启…...

ecovadis认证基本概述,ecovadis认证审核有效期

EcoVadis认证基本概述 1. 什么是EcoVadis认证&#xff1f; EcoVadis是全球领先的企业可持续发展&#xff08;ESG&#xff09;评级平台&#xff0c;专注于评估企业在**环境&#xff08;E&#xff09;、劳工与人权&#xff08;S&#xff09;、商业道德&#xff08;L&#xff09…...

2.8.4 iOS覆盖率SDK开发

iOS系统的覆盖率SDK,通过搭建本地的pod仓库,直接在podfile中添加指定的下载地址,就可以实现对被测试的app注入覆盖率SDK。 2.8.4.1 开发iOS覆盖率获取Pod私有库 在网上查找了相应的开发方法后,决定开发自己的依赖库,开发方法及步骤如下: 1,开发新的包 (1)通过Xcode创…...

Redhat(6)-ansible-变量

变量 1.作用域 Global scope:命令行中设置。 Play scope:play中设置。 Host scope &#xff1a;inventory中定义、facts收集或任务中注册&#xff0c;在主机组和主机上设置。 1.全局变量 1.1cmd命令 #1.全局变量 #显示变量 echo %PATH1.2.ansible变量 全局变量 var变量不加…...

麦科信光隔离探头在碳化硅(SiC)MOSFET动态测试中的应用

碳化硅&#xff08;SiC&#xff09;MOSFET 是基于宽禁带半导体材料碳化硅&#xff08;SiC&#xff09;制造的金属氧化物半导体场效应晶体管&#xff0c;相较于传统硅&#xff08;Si&#xff09;MOSFET&#xff0c;具有更高的击穿电压、更低的导通电阻、更快的开关速度以及更优异…...

android audiorecord

这里写目录标题 初始化失败记录 AudioRecord 初始化及参数介绍基本初始化参数详解1. audioSource (音频源)2. sampleRateInHz (采样率)3. channelConfig (声道配置)4. audioFormat (音频格式)5. bufferSizeInBytes (缓冲区大小) 完整初始化示例使用注意事项 参考地址 初始化失败…...

有一个变量 在有些线程没有加锁 有些线程加锁了,那我在这些加锁的线程中能起到对应的作用吗

这是一个非常经典、但也很危险的问题。 &#x1f9e8; 简单结论&#xff1a; 如果一个变量在某些线程访问时没有加锁&#xff0c;即使其他线程对它加了锁&#xff0c;也&#xff1a; ❌ 不能保证线程安全&#xff01; ❌ 加锁的部分不会“保护”未加锁的部分&#xff01; &am…...

【人工智能】AI大模型开发数学基础指南

目录 学习内容**1. 线性代数****2. 概率与统计****3. 微积分****4. 优化理论****5. 信息论****6. 数值计算****7. 离散数学****8. 统计学进阶****如何学习&#xff1f;****总结** 如何学习**1. 明确学习目标****2. 分阶段学习计划****阶段 1&#xff1a;夯实基础****阶段 2&…...

直流减速电机控制实验:Simulink应用层开发(3)

文章目录 1 阶段目标2 单元测试方法3 单元测试过程3.1 按键指令识别测试3.2 电机状态转换测试4 代码生成5 总结1 阶段目标 本文是《直流减速电机控制实验》的第四部分,会通过图文结合的方式,手把手带读者操作Simulink工具进行直流减速电机的应用层开发。 本章主要将《直流减…...

隔行换色总结

功能效果展示&#xff1a; 第一种思路&#xff1a; 使用数组&#xff0c;将数组的内容渲染到页面上&#xff0c;序号也就是将数组的下标输出到第一个td上&#xff0c;将数组的内容输出到第二个td上&#xff0c;&#xff08;使用拼接字符串&#xff09; 具体操作&#xff1a; …...

【kind管理脚本-2】脚本使用说明文档 —— 便捷使用 kind 创建、删除、管理集群脚本

当然可以&#xff0c;以下是为你这份 Kind 管理脚本写的一份使用说明文档&#xff0c;可作为 README.md 或内部文档使用&#xff1a; &#x1f680; Kind 管理脚本说明文档 本脚本是一个便捷的工具&#xff0c;帮助你快速创建、管理和诊断基于 Kind (Kubernetes IN Docker) 的…...

Python星球日记 - 第13天:封装、继承与多态

🌟引言: 上一篇:Python星球日记 - 第12天:面向对象编程(OOP)基础 名人说:不要人夸颜色好,只留清气满乾坤(王冕《墨梅》) 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、引言二、封装的概念与实现1. 公有属性与私有属性2. 使用getter和sett…...

基于AT89C52单片机的GSM上报智能家居报警温度烟雾防盗系统

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/90579530?spm1001.2014.3001.5503 功能介绍&#xff1a; 1、功能&#xff1a;具有温度、烟雾、入侵报警功能&#xff0c;采用LCD1602液晶显示屏实时显示温度值与…...

北重数控滑台厂家:机器人地轨究竟是如何运作的,又在哪些领域发光发热呢?

机器人地轨是指利用机器人技术在地面上移动或执行任务的轨道系统。这种系统通常包括导轨、传动装置、传感器和控制系统等组成部分。机器人地轨的运作原理是通过控制传动装置沿着导轨移动&#xff0c;同时利用传感器获取周围环境信息并通过控制系统进行实时调节。 机器人地轨在…...

2025最新系统 Git 教程(三)

2.3 Git 基础 - 查看提交历史 查看提交历史 在提交了若干更新&#xff0c;又或者克隆了某个项目之后&#xff0c;你也许想回顾下提交历史。 完成这个任务最简单而又有效的工具是 git log 命令。 我们使用一个非常简单的 simplegit 项目作为示例。 运行下面的命令获取该项目&…...

显示器各类异常处理方法

显示器各类异常处理方法 导航 文章目录 显示器各类异常处理方法导航画面无显示/黑屏/无HDMI信号输入显示器闪烁显示器花屏显示画面模糊或扭曲显示器颜色异常显示器出现死点或亮点 画面无显示/黑屏/无HDMI信号输入 ​ 首先应该检查的是显示器电源&#xff08;真的有人弄掉电源…...

Error 1062 (23000): Duplicate entry ‘‘ for key ‘id‘`

目录 Error 1062 (23000): Duplicate entry for key id1. **问题分析**2. **解决方法****步骤 1&#xff1a;检查 id 字段的值****步骤 2&#xff1a;检查表结构****步骤 3&#xff1a;检查现有数据****步骤 4&#xff1a;检查插入逻辑****步骤 5&#xff1a;修改表结构&#…...

谈谈模板方法模式,模板方法模式的应用场景是什么?

一、模式核心理解 模板方法模式是一种​​行为设计模式​​&#xff0c;通过定义算法骨架并允许子类重写特定步骤来实现代码复用。 如同建筑图纸规定房屋结构&#xff0c;具体装修由业主决定&#xff0c;该模式适用于​​固定流程中需要灵活扩展​​的场景。 // 基础请求处理…...

未来蓉城:科技与生态共舞的诗意栖居-成都

故事背景 故事发生在中国四川成都的2075年&#xff0c;展现科技与自然深度交融的未来城市图景。通过六个充满想象力的生态装置场景&#xff0c;描绘市民在智慧城市中诗意栖居的生活状态&#xff0c;展现环境保护与人文传承的和谐共生。 故事内容 在电子竹林轻轨站&#xff0c;通…...

模仿axios的封装效果来封装fetch,实现baseurl超时等

因为要在cocos游戏项目里面发送网络请求获取数据&#xff0c;并且还有可能用到websocket发送请求&#xff0c;所以这里封装一个fetch放便使用&#xff1a; // fetch封装// 基础配置 const BASE_URL 你的url const TIMEOUT 5000// 请求封装 const http async (url: string, …...

Linux(CentOS10) gcc编译

本例子摘自《鸟哥的linux私房菜-基础学习第四版》 21.3 用make进行宏编译 书中的代码在本机器(版本见下&#xff09;编译出错&#xff0c;改正代码后发布此文章&#xff1a; #kernel version: rootlocalhost:~/testmake# uname -a Linux localhost 6.12.0-65.el10.x86_64 #1…...

Design Compiler:语法检查工具dcprocheck

相关阅读 Design Compilerhttps://blog.csdn.net/weixin_45791458/category_12738116.html?spm1001.2014.3001.5482 dcprocheck是一个在Design Compiler存在于安装目录下的程序&#xff08;其实它是一个指向snps_shell的符号链接&#xff0c;但snps_shell可以根据启动命令名判…...

根据日期格式化的常见规则和标准

根据日期格式化的常见规则和标准&#xff0c;2025年1月9日17:00可以正常格式化。具体分析如下&#xff1a; 1. 日期合法性验证 日期2025年1月9日不存在逻辑错误&#xff08;如2月30日等非法日期&#xff09;&#xff0c;且时间17:00&#xff08;24小时制&#xff09;符合规范…...

macOS Chrome - 打开开发者工具,设置 Local storage

文章目录 macOS Chrome - 打开开发者工具设置 Local storage macOS Chrome - 打开开发者工具 方式2&#xff1a;右键点击网页&#xff0c;选择 检查 设置 Local storage 选择要设置的 url&#xff0c;显示右侧面板 双击面板&#xff0c;输入要添加的内容 2025-04-08&#xff…...

idea 的 WEB-INF 下没有 classes 编译文件,如何添加?

idea 打开项目却没有在 WEB-INF 下找到 classes 编译文件 添加流程如下&#xff1a; 1、选中 File ->Project Structure 后右击 2、选中 Modules ->选中项目 ->点击 Paths ->修改 output path为&#xff1a;项目绝对路径\WebRoot\WEB-INF\classes 3、修改完成后&…...

EasyExcel结合多线程+控制sheet页全量导出

业务分析 内部平台需要一个导出mysql数据到excel的方法&#xff0c;所以使用了EasyExcel 因为EasyExcel的sheet页是放到一个List里面的&#xff0c;如果把百万量级的数据放到sheet页中全量写入会有OOM风险&#xff0c;所以最终选择的方案是分sheet页写入 同时因为该平台是多…...

【学习笔记】RL4LLM

字数溢出&#xff0c;分了一半出来 上半段&#xff1a;LLMRL 文章目录 8 [RL4LLM] 理解 reasoning model Tokenizer 的 chat template&#xff0c;vllm inferencetokenizerchat templatedistill tokenizerqwen tokenizer apply chat templatevllm inference 9 [RL4LLM] PPO wo…...

在Windows搭建gRPC C++开发环境

一、环境构建 1. CMake Download CMake 2. Git Git for Windows 3. gRPC源码 git clone --recurse-submodules -b v1.67.1 --depth 1 --shallow-submodules https://github.com/grpc/grpc grpc-1.67.1二、使用CMake生成工程文件 mkdir cmake_build cd cmake_build cmake…...

NO.76十六届蓝桥杯备战|数据结构-单调栈|发射站|Largest Rectangle in a Histogram(C++)

什么是单调栈&#xff1f; 单调栈&#xff0c;顾名思义&#xff0c;就是具有单调性的栈。它依旧是⼀个栈结构&#xff0c;只不过⾥⾯存储的数据是递增或者递减的。这种结构是很容易实现的&#xff08;如下⾯的代码&#xff09;&#xff0c;但重点是维护⼀个单调栈的意义是什么 …...

消息队列(Message Queue)简介

消息队列是一种进程间通信&#xff08;IPC&#xff09;机制&#xff0c;允许不同进程通过发送和接收消息进行 异步通信。它的核心特点包括&#xff1a; 解耦&#xff1a;消息队列解耦了生产者和消费者&#xff0c;简化了系统设计。 持久化存储&#xff1a;支持将消息存储在队列…...

动/静态库

1.先了解一下动静态库 上图可以看出来静态库就是由一堆进过链接阶段的.o文件组成的.a文件。在这里必须要强调的是库文件格式一定是libxxx.a/so在你进行路径查找使用的时候要去掉lib和后缀使用&#xff01; 静态库 概念&#xff1a;在程序编译链接阶段&#xff0c;其代码被完整…...

KWDB创作者计划—边缘计算:从概念到落地的技术解读

引言 随着物联网&#xff08;IoT&#xff09;和人工智能&#xff08;AI&#xff09;的快速发展&#xff0c;数据量呈爆炸式增长&#xff0c;传统的云计算架构逐渐暴露出延迟高、带宽占用大等问题。边缘计算作为一种新兴的分布式计算范式&#xff0c;正在改变数据处理的方式。本…...

ubuntu根文件系统通过uMTP-Responder实现usb的MTP功能

实现mtp设备 添加服务 /home/flynn/firfly_rootfs/lib/systemd/system/adbd.service #start [Unit] Description Adbd for linux Beforerockchip.service[Service] Typeforking ExecStart/etc/init.d/adbd.sh start ExecStop/etc/init.d/adbd.sh stop ExecReload/etc/init.d…...

8、nRF52xx蓝牙学习(boards.h文件学习)

boards.h文件的代码如下&#xff1a; #ifndef BOARDS_H #define BOARDS_H#include "nrf_gpio.h" #include "nordic_common.h"#if defined(BOARD_NRF6310)#include "nrf6310.h" #elif defined(BOARD_PCA10000)#include "pca10000.h" #…...

辛格迪客户案例 | 河南宏途食品实施电子合约系统(eSign)

01 河南宏途食品有限公司&#xff1a;食品行业的数字化践行者 河南宏途食品有限公司&#xff08;以下简称“宏途食品”&#xff09;作为国内食品行业的创新企业&#xff0c;专注于各类食品的研发、生产和销售。公司秉承“质量为先、创新驱动、服务至上”的核心价值观&#xff…...

webrtc-stats

1. RTP 相关统计 1.1 inbound-rtp (接收端统计) 接收到的 RTP 流统计信息&#xff0c;包含以下关键指标&#xff1a; bytesReceived: 接收到的字节总数packetsReceived: 接收到的数据包总数packetsLost: 丢失的数据包数量jitter: 数据包到达时间的抖动&#xff08;毫秒&…...

【LangChain框架组成】 LangChain 技术栈的模块化架构解析

目录 整体架构概述 整体架构层级划分 模块详细解析 1. 部署与服务层&#xff08;LangServe & Deployments&#xff09; 2. 应用模板层&#xff08;Templates & Committee Architectures&#xff09; 3. 核心功能层&#xff08;LangChain&#xff09; 4. 社区扩展…...

RNN、LSTM、GRU汇总

RNN、LSTM、GRU汇总 0、论文汇总1.RNN论文2、LSTM论文3、GRU4、其他汇总 1、发展史2、配置和架构1.配置2.架构 3、基本结构1.神经元2.RNN1. **RNN和前馈网络区别&#xff1a;**2. 计算公式&#xff1a;3. **梯度消失:**4. **RNN类型**:&#xff08;查看发展史&#xff09;5. **…...

用TypeScript和got库编写爬虫程序指南

用TypeScript和got库写一个爬虫程序。首先&#xff0c;我得确认他们对TypeScript和Node.js的基础了解&#xff0c;可能他们已经有了一些JS的经验&#xff0c;但不确定。接下来&#xff0c;需要明确爬虫的目标&#xff0c;比如要爬取的网站、需要的数据类型以及处理方式。 首先…...

使用 Spring Boot 快速构建企业微信 JS-SDK 权限签名后端服务

使用 Spring Boot 快速构建企业微信 JS-SDK 权限签名后端服务 本篇文章将介绍如何使用 Spring Boot 快速构建一个用于支持企业微信 JS-SDK 权限校验的后端接口&#xff0c;并提供一个简单的 HTML 页面进行功能测试。适用于需要在企业微信网页端使用扫一扫、定位、录音等接口的…...

【软考-架构】13.2、软件层次风格

✨资料&文章更新✨ GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目录 2、层次架构风格两层C/S架构三层C/S架构三层B/S架构富互联网应用RIAMVC架构MVP架构MVVM架构 ✨3、面向服务的架构风格SOASOA中应用的关键技术WEB Service企业服务总线ESB …...

Java 进阶-全面解析

目录 异常处理​ 集合框架​ List 集合​ Set 集合​ Map 集合​ 文件与字符集​ IO 流​ 多线程​ 通过继承Thread类创建线程 通过实现Runnable接口创建线程 线程同步示例​ 线程通信示例 网络编程 Java 高级技术 反射机制 动态代理 注解 异常处理​ 在 Java …...

mongodb 创建keyfile

在 MongoDB 中&#xff0c;keyFile 是用于副本集成员间内部认证的密钥文件。它是一个包含随机字符串的文件&#xff0c;所有副本集成员必须使用相同的 keyFile 进行通信。以下是创建和配置 keyFile 的详细步骤。 创建 KeyFile 的步骤 1. 生成随机字符串 使用以下命令生成一个…...

工业4.0时代,RK3562工控机为何成为智慧工位首选?

在制造业数字化转型的浪潮中&#xff0c;智慧车间已成为提升生产效率、降低运营成本的关键战场。作为智慧车间的"神经末梢"&#xff0c;工位机的智能化程度直接影响着整个生产线的运行效率。RK3562工控机凭借其强大的计算性能、稳定的运行表现和丰富的接口配置&#…...

WPF 资源加载问题:真是 XAML 的锅吗?

你的观察很敏锐&#xff01;确实&#xff0c;在 WPF 项目中&#xff0c;.cs 文件主要负责逻辑实现&#xff0c;而资源加载的问题通常跟 XAML&#xff08;以及它背后的 .csproj 配置&#xff09;关系更大。我会围绕这个观点&#xff0c;用 CSDN 博客风格详细解释一下 .cs、XAML …...

5. 深度剖析:Spring AI项目架构与分层体系全解读

1、前言 前面我们已经可以通过简单的方式集成Spring AI进行快速开发了。授人以鱼不如授人以渔&#xff0c;我们还是需要了解Spring AI的项目结构&#xff0c;以及他的一些核心概念。 2、项目结构 我们将Spring AI代码直接fork到我们自己的仓库中。fork的目的是方便我们为了学…...

2025最新数字化转型国家标准《数字化转型管理参考架构》 正式发布

当前&#xff0c;数字化转型是数字时代企业生存和发展的必答题&#xff0c;其根本任务是价值体系优化、创新和重构。数字生产力的飞速发展不仅引发了生产方式的转变&#xff0c;也深刻改变了企业的业务体系和价值模式。 为进一步引导企业明确数字化转型的主要任务和关键着力点…...

蓝桥杯备赛 Day 20 树基础

![[树的基础概念.png]] 树的遍历 二叉树遍历分类 DFS前序遍历 根节点-左儿子-右儿子 DFS中序遍历 左儿子-根节点-右儿子 DFS后序遍历 左儿子-右儿子-根节点 BFS层序遍历![[树的遍历.png]] 代码: #include <bits/stdc.h>using namespace std; const int N20; i…...