用C语言编写天天酷跑项目,在封装障碍物后,进行初始化和渲染时出现错误
0x00007FFDCB111989 (vcruntime140d.dll)处(位于 我的天天酷跑2022.exe 中)引发的异常: 0xC0000005: 写入位置 0x000000000 时发生访问冲突。
c
/*
* 天天酷跑开发日志
1.创建项目
2.导入素材
3.创建游戏界面( 对于初学者,最好的方式,建议从用户界面入手)
选择图形库或其他引擎(天天酷跑是基于easyX图形库的)
1)创建游戏窗口
2)构思游戏背景
a.3组背景不同速度同时移动
b.循环滚动背景实现
3)实现游戏背景
a.加载背景资源
b.渲染/打印图片(背景知识:坐标,以左上角坐标为准)
第一次运行bug:出现黑色背景
原因:easyX不支持透明背景的png图片
2022.12.27
4.实现玩家奔跑
5.实现玩家跳跃
6.创建障碍物小乌龟
7.封装障碍物
学习路径:C语言 C++ 算法(如自动寻路的追心算法)数据库 windows(win32+mfc) Qt linux服务器)
*/
#include
#include //基于easyX图形库,需安装easyX图形库
#include //判断是否有按键输入的头文件
#include
#include "tools.h"
using namespace std;//声明命名空间,与vector结合使用
#define WIN_WIDTH 1012
#define WIN_HEIGHT 396
#define OBSTACLE_COUNT 10//定义一个变量表示障碍物总数,进行统一规范管理(修改时只需在此处修改即可修改全局障碍物总数
//保存背景图片
//有3类背景图片,用一个数组来存放
//多个函数用到,定义为全局变量
IMAGE imgBgs[3];
int bgSpeed[3] = { 1,2,4 };//数组存放3种背景的移动速度
//背景变化过程中,3张图片X坐标在变化,定义一个数组来存放背景x坐标
int bgX[3];
IMAGE imgHeros[12];//存放玩家图片
int heroX;//玩家遇到障碍物将被弹飞,因此用一个变量来表示其x坐标
int heroY;//玩家会跳跃,用一个变量表示其y坐标
int heroIndex;//显示的玩家图片序号
bool heroJump;//跳跃启动开关
int jumpHeightMax;
//定义偏移量,跳跃改变的单位量
int heroJumpOff;
int update;//表示是否需要马上刷新
//IMAGE imgTortoise;//定义变量存放小乌龟图片
//int torToiseX;//小乌龟x坐标
//int torToiseY;
//bool torToiseExist;//判断当前窗口是否有小乌龟存在,使窗口只出现一个小乌龟
//为了避免障碍物越来越多,代码膨胀,设计结构体把特定类型封装成更加方便的类型
//相当于进行代码优化
//用枚举类型表示类型更加专业可读
typedef enum {
TORTOISE,//乌龟 0/枚举类型默认首位为0
LION,//狮子 1
OBSTACLE_TYPE_COUNT //2加计数变量作为边界,可用于查看枚举元素个数
}obstacle_type;
//obstacle_type type1;障碍物类型变量表示
//vector> data;//尖括号里面表示存放数据类型。需用2维数组,行表示类型,列表示帧数序号
vector> obstacleImgs;//可变数组,存放所有障碍物图片,类似于IMAGE obstacleImgs[3][12]
vector
imgTortArray;//定义存放图片的数组
//IMAGE obstacleImgs[3][12];//所有障碍物图片都放在这里,内存消耗的更少
//有的障碍物没有这么多图片,用12张有点浪费
//用c++的容器构造可变数组来实现,需加头文件
typedef struct obstacle {
obstacle_type type;//障碍物类型
int imgIndex;//当前显示图片序号
int x,y;//障碍物坐标
int speed;
int power;//杀伤力
bool exist;//障碍物是否存在
//IMAGE img[12];//有的障碍物没有12张图片,这种表示方法会浪费很多内存
}obstacle_t;//用于表示一种障碍物
obstacle_t obstacles[OBSTACLE_COUNT];//制作一个障碍物池,对障碍物进行统一管理
void init() {
//创建游戏窗口
initgraph(WIN_WIDTH, WIN_HEIGHT);
//加载背景资源,加载到内存里面
char name[64];
for (int i = 0; i < 3; i++) {
//"res/bg001.png"
sprintf(name, "res/bg%03d.png", i + 1);//宽度占3位,不够前面补0
loadimage(&imgBgs[i], name);//问题解决消除loadimage红色提示
bgX[i] = 0;//初始化背景x坐标
}
//加载玩家奔跑图片素材,加载到内存
for (int i = 0; i < 12; i++) {
//"res/hero1.png"...
sprintf(name, "res/hero%d.png", i + 1);
loadimage(&imgHeros[i], name);
}
//设置玩家初始位置:x坐标为窗口 宽度一半-图片宽度一半。y坐标为图片下边缘y值-图片长度
heroX = WIN_WIDTH * 0.5 - imgHeros[0].getwidth() * 0.5;
heroY = 345 - imgHeros[0].getheight();
heroIndex = 0;
heroJump = false;
jumpHeightMax= 345 - imgHeros[0].getheight()-120;
heroJumpOff = -4;
update = true;
////加载小乌龟
//loadimage(&imgTortoise, "res/t1.png");
//torToiseExist = false;//一开始没有小乌龟
//torToiseY = 350 - imgTortoise.getheight();
//封装障碍物后,小乌龟存储位置发生变化,应将小乌龟图片放到二维数组
IMAGE imgTort;//定义存放小乌龟图片的内存
loadimage(&imgTort, "res\t1.png");//将路径下的图片到内存
//vector
imgTortArray;//定义存放图片的数组
imgTortArray.push_back(imgTort);//数组中放入小乌龟图片
obstacleImgs.push_back(imgTortArray);//将数组添加到二维数组
IMAGE imgLion;//玩家与背景图片始终存在,定义为全局变量,狮子偶尔出现,定义为局部变量
vector
imgLionArray;
for (int i = 0; i < 6; i++) {
sprintf(name, "res/p%d.png", i + 1);//加载狮子
loadimage(&imgLion, name);
imgLionArray.push_back(imgLion);
}
obstacleImgs.push_back(imgLionArray);
//对池子进行初始化
for (int i = 0; i < OBSTACLE_COUNT; i++) {
obstacles[i].exist = false;
}
}
void createObstacle() {
int i;//小技巧,记录找到的不存在的障碍物类型
for (i = 0; i < OBSTACLE_COUNT; i++) {//从0(第一种类型)开始检查障碍物是否存在
if (obstacles[i].exist == false) {
break;//障碍物不存在再创建
}
}
if (i >= OBSTACLE_COUNT) {//i >= OBSTACLE_COUNT表示遍历玩障碍物类型,所有障碍物exist都为true,即障碍物都存在,无需创建
return;
}
//下面是找到的情况
obstacles[i].exist = true;
obstacles[i].imgIndex = 0;//最开始显示第一张
obstacles[i].type = (obstacle_type)(rand() * OBSTACLE_TYPE_COUNT);//随机产生一种类型的障碍物//报错,右边rand()*..为int类型,左边type是枚举类型,不能直接转
//明白层的意思了,代码中的元素分为各种类型,这些类型又以各种类型为属性,以此类推,直到不再细分的底层元素,这其中一种类型就称为一层
obstacles[i].x = WIN_WIDTH;
obstacles[i].y = 345 + 5 - obstacleImgs[obstacles[i].type][0].getheight();//创建障碍物首先创建第0张图片,并且位置在初始位置(即窗口最右侧)
if (obstacles[i].type == TORTOISE) {
obstacles[i].speed = 0;
obstacles[i].power = 5;
}
else if (obstacles[i].type == LION) {
obstacles[i].speed = 4;
obstacles[i].power = 20;
}
}
//使背景出现坐标移动的变化
void fly() {
for (int i = 0; i < 3; i++) {
bgX[i] -= bgSpeed[i];
//一轮跑完马上切换下一轮/素材背景长度为两个游戏窗口宽度
if (bgX[i] < -WIN_WIDTH) {
bgX[i] = 0;
}
}
//实现跳跃。在背景下实现跳跃
if (heroJump) {
if (heroY < jumpHeightMax) {//定义一个顶点限制跳跃高度
heroJumpOff = 4;//高度小于最大高度,偏移量变为正数
}
//跳跃过程中每次都加上这个偏移量
heroY += heroJumpOff;
if (heroY > 345 - imgHeros[0].getheight()) {
heroJump = false;//落回地面,跳跃结束,偏移量变为负以便下次跳跃
heroJumpOff = -4;
}
}
//不跳跃时才改变玩家画面
else {
heroIndex = (heroIndex + 1) % 12;//切换玩家图片
}
//小乌龟不是一直存在的,需创建小乌龟
static int frameCount = 0;//普通变量函数每执行一次内存就释放掉,再次调用回到初始值,加static变量永久有效
//定义一个频度,表示小乌龟出现的间隔帧数
static int enemyFre = 50;
frameCount++;//对fly()调用次数计数,每调用100帧(每3s)创建一次小乌龟
if (frameCount > enemyFre) {
frameCount = 0;//计数器清零
//3s创建一个可能导致画面出现多个小乌龟,先判断是否有小乌龟,没有再创建
//if (!torToiseExist) {
// torToiseExist = true;
// torToiseX = WIN_WIDTH;
// torToiseFre = 200 + rand() % 300;//随机数对300取余表示0-299
//}
//进行通用化的障碍物创建
enemyFre = 50 + rand() % 50;//50-99
createObstacle();//刷新帧数每大于enemyFre就创建一个障碍物
}
//if (torToiseExist) {//小乌龟存在时再对它进行移动
// torToiseX -=bgSpeed[2];//小乌龟速度与地面相同,静止出现在地面上
// //有始有终,小乌龟跑出画面进行判断
// if (torToiseX < -imgTortoise.getheight()) {
// torToiseExist = false;
// }
//}
}
//渲染/呈现游戏背景
void updateBg() {
putimagePNG2(bgX[0], 0, &imgBgs[0]);
putimagePNG2(bgX[1], 119, &imgBgs[1]);
putimagePNG2(bgX[2], 330, &imgBgs[2]);
}
void jump() {
heroJump = true;
update = true;//按下开关立即刷新,不等30ms
}
//处理按键输入
void keyEvent() {
char ch;
if (kbhit()) {//判断是否有按键输入
ch=getch();//不需要按回车直接读取输入字符
//scanf("%c", &c);// 会等待用户输入,阻塞程序运行
if (ch == ' ') {//如果输入空格就跳跃
jump();
}
}
}
void updateEnemy() {
////渲染小乌龟
//if (torToiseExist) {
// putimagePNG2(torToiseX, torToiseY, WIN_WIDTH, &imgTortoise);
//}
//通用化地渲染障碍物
for (int i = 0; i < OBSTACLE_COUNT; i++) {
if (obstacles[i].exist) {//该种障碍物存在时进行渲染
putimagePNG2(obstacles[i].x, obstacles[i].y, WIN_WIDTH, &obstacleImgs[obstacles[i].type][obstacles[i].imgIndex]);//渲染即将对应图片放到新位置(新x,y坐标),所取图片在数组中位置为:行为障碍物类型,列为障碍物序号
}
}
}
int main(void) {
init();
int timer = 0;//让画面不要太快刷新
//让图片动起来
while (1) {
keyEvent();//按键使玩家跳跃
timer+=getDelay();//getDelay()距离上次调用用了多少时间
if (timer > 30) {//大于30ms再刷新
timer = 0;
update = true;
}
if(update){
update = false;
//跑一点打印一点就会抖动,准备好再打印就不会抖动。用双缓冲机制防止抖动
BeginBatchDraw();
updateBg();
putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);//打印玩家图片
updateEnemy();
EndBatchDraw();
fly();//每次打印后改一下图片位置
}
//Sleep(30);//帧等待。帧打印过快看不出变化,每打印一次需进行帧等待
//程序一般不加sleep,sleep意味着程序休眠,如果30ms内用户按下按键,程序不会响应
}
updateBg();
system("pause");
return 0;
}
如图
尝试了安装vc2022库文件和添加vcruntime140d.dll到目录,无法解决
解决此bug
好家伙,你直接将一个vector加到另一个vector之中,内存不爆才怪呢