学习了1个多星期的easyx图形库教程后,结合csdn上面飞机大战的相关思路,做了一些坐标题目。本人c语言学了大概有130多天,尝试了一下天天酷跑的小项目,素材图片太过简陋.并且程序终止于添加血条,音效以及按键长按问题(期中考试,应付其他科目)。
目前程序无bug,可以实现人物正常跑路,下蹲,以及与障碍物的碰撞检测。
有注释,可大致解释清楚部分代码用途
没有运用到指针,运用到了部分c++的数组vector代码
程序适合于小白临摹;需要使用c++以及easyx图形库实现。
#define _CRT_SECURE_NO_WARNINGS 1
//天天酷跑开发日志:4.11日程序猿:南林叶良辰
//1:创建项目
//2:导入素材
//3:开发游戏界面
//4:实际的开发流程
//b站老师说,对于初学者,最好的方式是从用户界面入手,也就是菜单什么的
//1:创建游戏窗口
//2:实现游戏背景,因为背景图片首先储存在硬盘,得需要进度条进行准备工作
//2(1):3种背景图,以不同的速度同时移动
//2(2):循环滚动背景的实现
//3:实现游戏背景
//a.加载背景资源
//b.渲染
//背景知识:游戏窗口坐标
//定位图片素材坐标需要知道左上角坐标
//对于图片素材和窗口都是,所以窗口左上角为原点,向下为y
//4:实现玩家奔跑
//5:实现玩家的跳跃,按空格跳一下
//6:随机小乌龟
//7:障碍物封装用结构体
//8:初始化障碍物结构体
//9:封装多障碍物显示
//10:玩家下蹲
//11:实现柱子障碍物
#include
#include
#include
#include"tools.h"
#include
#include
using namespace std;//使用vector更加方便,是为了声明一个命名空间
#define WIN_WIDTH 1012
#define WIN_HEIGHT 396
#define OBSTACLE_COUNT 10
//背景图片,因为3个用全局变量的数组
IMAGE imgBgs[3];//背景图片
int bgX[3];//背景图片的x坐标
int bgSpeed[3] = { 1,2,4 };
IMAGE imgHeros[12];
int heroX;//玩家的x坐标
int heroY;//玩家的y坐标
int heroIndex;//玩家奔跑的图片的序号
bool heroJump;//表示玩家正在跳跃
int jumpHeightMax;
int heroJumpOff;//跳跃偏移量
int update;//表示马上要刷新画面
IMAGE imgTortoise;//小乌龟
int torToiseX;//乌龟的水平坐标
int torToiseY;//乌龟y坐标
bool torToiseExist;//当前窗口是否有小乌龟
int heroBlood;
typedef enum {
TORTOISE,//乌龟 默认是0 特殊的整数
LION,//狮子 1
HOOK1,
HOOK2,
HOOK3,
HOOK4,
OBSTACLE_TYPE_COUNT //6 学到的开发小技巧,用来计数枚举常量的个数
} obstacle_type;
//IMAGE obstacleImgs[3][12];//内存消耗
//vector> data;//相当于c语言的二维数组,超纲了可以去cplusplus.com了解用法
vector > obstacleImgs;//存放障碍物图片
//相当于int n;int data[n]变长数组
typedef struct obstacle {
int type;//障碍物的类型
int imgIndex;//当前显示图片的序号
int x, y;//障碍物坐标
int speed;//速度
int power;//伤害
bool exist;//是否存在
bool hited;//表示是否已经发生碰撞
}obstacle_t;
obstacle_t obstacles[OBSTACLE_COUNT];
IMAGE imgHeroDown[2];
bool heroDown;//表示玩家是否处于下蹲状态
void init()
{
//创建一个游戏窗口
initgraph(WIN_WIDTH, WIN_HEIGHT,EW_SHOWCONSOLE); //第三个是打印控制台的
//窗口有了,下一步是背景导入
//加载游戏背景资源
char name[64];
for (int i = 0; i < 3; i++) {
//"res/bg001.jpg res/bg002.jpg res/bg003.jpg
sprintf(name, "res/bg%03d.png", i + 1);
loadimage(&imgBgs[i], name);
bgX[i] = 0;
}
//加载Hero奔跑的图片帧素材
for (int i = 0; i < 12; i++)
{
//"res/hero1.png----12png
sprintf(name, "res/hero%d.png", i + 1);
loadimage(&imgHeros[i], name);
}
//设置玩家的初始位置
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 = -5;
update = true;
//加载小乌龟
//loadimage(&imgTortoise, "res/t1.png");
//torToiseExist = false;
//torToiseY = 350 - imgTortoise.getheight();
IMAGE imgTort;
//loadimage(&imgTortoise, "res/t1.png");
loadimage(&imgTort, "res/t1.png");
vectorimgTortArray;
imgTortArray.push_back(imgTort);
obstacleImgs.push_back(imgTortArray);
IMAGE imgLion;
vectorimgLionArray;
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;
}
//加载下蹲素材
loadimage(&imgHeroDown[0], "res/d1.png");
loadimage(&imgHeroDown[1], "res/d2.png");
heroDown = false;
IMAGE imgH;
vectorimgHookArray;
for (int i = 0; i < 4; i++)
{
vectorimgHookArray;//表示一种柱子的数组,存放柱子的形态,但是只有一个
sprintf(name,"res/h%d.png", i + 1);
loadimage(&imgH, name,63,260,true);//可以自己美工
imgHookArray.push_back(imgH);
obstacleImgs.push_back(imgHookArray);
}
//加载4个柱子的图片
heroBlood = 100;
}
void createObstacle (){
int i;
for (i = 0; i < OBSTACLE_COUNT; i++)
{
if (obstacles[i].exist == false)
{
break;
}
}
if (i > OBSTACLE_COUNT)
{
return;
}
obstacles[i].exist = true;
obstacles[i].hited = false;
obstacles[i].imgIndex = 0;
obstacles[i].type = (obstacle_type)(rand() % 3);
if (obstacles[i].type == HOOK1)
{
obstacles[i].type += rand() % 4;
}
obstacles[i].x = WIN_WIDTH;
obstacles[i].y = 345 + 5 - obstacleImgs[obstacles[i].type][0].getheight();
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;
}
else if (obstacles[i].type >= HOOK1 && obstacles[i].type<= HOOK4) {
obstacles[i].speed = 0;
obstacles[i].power = 20;
obstacles[i].y = 0;
}
}
void checkHit(){
for(int i = 0; i < OBSTACLE_COUNT; i++) {
if (obstacles[i].exist&&obstacles[i].hited == false)
{
int off = 75;
int a1x, a1y, a2x, a2y;
if (!heroDown) {
a1x = heroX + off;
a1y = heroX + off;
a2x = heroX + imgHeros[heroIndex].getwidth() - off;
a2y = heroY + imgHeros[heroIndex].getheight();
}
else {
a1x = heroX - 40;
a1y = 345 - imgHeroDown[heroIndex].getheight();
a2x = heroX + imgHeroDown[heroIndex].getwidth() - off;
a2y = 345;
}
IMAGE img = obstacleImgs[obstacles[i].type][obstacles[i].imgIndex];
int b1x = obstacles[i].x + off;
int b1y = obstacles[i].y + off;
int b2x = obstacles[i].x + img.getwidth() - off;
int b2y = obstacles[i].y + img.getheight() - 5;
if (rectIntersect(a1x,a1y,a2x,a2y,b1x,b1y,b2x,b2y))
{
heroBlood -= obstacles[i].power;
printf("血量剩余 %d\n", heroBlood);
playSound("res/hit.mp3");
obstacles[i].hited = true;
}
}
}
}
//让图片动起来
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 = 5;
}
heroY += heroJumpOff;
if (heroY > 345 - imgHeros[0].getheight())
{
heroJump = false;
heroJumpOff = -5;
}
}
else if (heroDown) {
static int count = 0;
int delays[2] = { 4,10 };
count++;
if (count >= delays[heroIndex]) {
count = 0;
heroIndex++;
if (heroIndex >= 2) {
heroIndex = 0;
heroDown = false;
}
}
}
else{
heroIndex = (heroIndex + 1) % 12;
//不跳跃
}
//生成小乌龟
static int frameCount = 0;//保持生命周期
static int enemyFre = 50;
frameCount++;
if (frameCount > enemyFre) {
frameCount = 0;
enemyFre = 50 + rand() % 50;//50-99fps;
createObstacle();
}
//更新障碍物的坐标
for (int i = 0; i < OBSTACLE_COUNT; i++)
{
if (obstacles[i].exist) {
obstacles[i].x -= obstacles[i].speed + bgSpeed[2];
if (obstacles[i].x < -obstacleImgs[obstacles[i].type][0].getwidth() * 2)
{
obstacles[i].exist = false;
}
int len = obstacleImgs[obstacles[i].type].size();
obstacles[i].imgIndex = (obstacles[i].imgIndex + 1) % len;
}
}
//碰撞检测(最难部分)
checkHit();
}
//渲染游戏背景
void updateBg()
{//防止屏幕闪烁,因此一次性打印比较好,用了b站大佬的工具库函数,可以防止y坐标<0;
putimagePNG2(bgX[0], 0,&imgBgs[0]);
putimagePNG2(bgX[1], 119, &imgBgs[1]);
putimagePNG2(bgX[2],330, &imgBgs[2]);
}
//这是easyx图形库
//天天酷跑本程序是基于"easyx图形库的"
//实现跳跃
void jump()
{
heroJump = true;
update = true;
}
void down() {
update = true;
heroDown = true;
heroIndex = 0;
}
//处理用户的键盘输入情况
void keyEvent()
{
if (_kbhit())
{
//kbhit引用conio库,如果有按键输入返回真
//scanf("%c,&c);如果不用kbhit,这个是会阻塞程序
//而且按一次需要按一次回车不方便
char ch;
ch = _getch(); //getch()不需要敲回车就可以直接读取,读取的是字符
if (ch == ' ')
{
jump();
}
else if (ch == 's') {
down();
}
}
//应该是先判断是否有按键按下去
}
//渲染小乌龟
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]);//第几个障碍物显示第几章图片,太乱了写的
}
}
}
void updateHero() {
if (!heroDown) {
putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
}
else {
int y = 345 - imgHeroDown[heroIndex].getheight();
putimagePNG2(heroX, y, &imgHeroDown[heroIndex]);
}
}
int main(void)
{
init();//初始化
int timer = 0;
//距离上一次调用相隔多久
while (1)
{
keyEvent();
timer += getDelay();
if (timer > 30) {
timer = 0;//计数器清零
update = true;
}
if (update)
{
update = false;
BeginBatchDraw();
updateBg();
//putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
updateHero();
updateEnemy();
EndBatchDraw();
fly();//让他x坐标更改
//Sleep(30);//帧等待 默认是毫秒,休眠作用但是对空格键有影响,延迟30ms
}
}
system("pause");
return 0;
}
这是试运行的程序图。
以上是部分代码,具体的代码分析我会在期中考试结束后带来一篇详细的博客。
小白代码,大佬勿喷,谢谢。爆肝五天,头发狂掉。