弁言
本文章适用于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 time
- for (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(); // 静态变量,记录上一次 tick
- oldclock += ms * CLOCKS_PER_SEC / 1000; // 更新 tick
- if (clock() > oldclock) // 如果已经超时,无需延时
- oldclock = clock();
- else
- while (clock() < oldclock) // 延时
- Sleep(1); // 释放 CPU 控制权,降低 CPU 占用率,精度 10~16ms
- // Sleep(0); // 更高精度、更高 CPU 占用率,精度 1ms
- }
复制代码 复制好后,点击上方绿色空三角运行。(运行结果如下)

以上便完成了全部的情况设置,开启开发游戏之旅
基本游戏逻辑
起首需要包罗头文件 #include<easyx.h>来调用图形函数
想要将代码中的结果显现出来,需要一个图形化窗口,并非是黑框框。
所以,第一步初始化一个图形化窗口。
该函数运行后,除了命令提示符的黑窗口之外,还会产生一个新的窗口,此时窗口内是空的。
如果我们想把外部图片贴上去,需要一个容器储存外部图片。
- IMAGE img;//声明一个可以存储外部图片的容器
复制代码 然后储存外部图片进入容器操作,&img是获取容器地址,“photo.png”是需要引入图片的路径
(路径可分为相对路径和绝对路径,我保举将图片和源程序放到同一个根目次中,既方便引用,又方便后续对于游戏的封装)
- loadimage(&img, "photo.png");加载图片进容器
复制代码 那储存好的图片怎样表如今屏幕上,我们需要函数将图片贴到屏幕上。
图中,x,y,前两个函数是指贴入图片的坐标(图片左上角顶点的坐标),
&img参数指贴入的图片容器,确定具体贴入哪个图片。
如今基本的图片表现便有了。
如果我们想让这个图片动起来,很好明白,我们只需要逐渐改变putimage函数的坐标参数就可以。
需要一个循环来刷新新的图像(改变坐标之后的贴图),(还需要刷新屏幕,或者利用背景覆盖法)
1,刷新屏幕: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 }; // 标记数组,初始化为 0
- int 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类型指针变为false
- void 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;
- else
- light_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);
- else
- transparentimage3(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;
- else
- dragon[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;
- else
- dragon[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]);
- else
- transparentimage3(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=2s
- continue;
- //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 }; // 标记数组,初始化为 0 int 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; else light_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; else light_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); else transparentimage3(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 {//没有按下D if (GetAsyncKeyState('A'))//是否按下A { transparentimage3(NULL, hero.x, hero.y, &run_imgL[run_num]); } else {//没有按下A if (GetAsyncKeyState('W') || GetAsyncKeyState('S')) {//是否按下W或S if (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;
- else
- dragon[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]); else transparentimage3(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=2s continue; //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游戏框架,未添加太多功能,感兴趣的小伙伴可以按照喜好实验添加)
本文结束....感谢观看。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |