【C语言项目】——天天酷跑

C语言项目——天天酷跑

前言

自学编程最有效的就是通过一些自己感兴趣的项目去学习,如果只是纯粹的听取知识点很难坚持,在项目中看到不懂的再去查询相关知识点,印象会更加深刻!
今天我开始了第一个项目——天天酷跑小游戏!

注意事项

注意下载easyx包哦!
相关png,mp3文件素材或tools文件工具包,可以自己百度寻找也可以在三连后评论区告诉我!推荐使用自己找的素材可以变得更加有趣!
在这里插入图片描述

源代码分享

#define _CRT_SECURE_NO_WARNINGS 1 
/*
* 基于easyx图形库
* 背景三重移动速度不同
*/
#include <stdio.h>
#include <graphics.h>
#include "tools.h"//消除png背景透明,其他功能函数
#include <conio.h>
#include <vector>//暂时借用C++里的可变数组头文件

using namespace std;//声明命名空间


#define WIN_WIDTH 1012
#define WIN_HEIGHT 396
#define OBSTACLE_COUNT 10
#define WIN_SCORE 20

IMAGE imgBgs[3];//小游戏,全局变量,类型为图片的数组IMAGE,素材在文件夹res里
int bgX[3];//实际上背景的x坐标为动态变化
int bgSpeed[3] = {
    
     1,2,4 };
IMAGE imgHeros[12];
int heroX;
int heroY;
int heroIndex;//英雄图片帧序号

bool heroJump;//true和false

int jumpHeightMax;
int heroJumpOff;//跳跃偏移量
int update;//表示是否需要马上刷新画面

//IMAGE imgTortoise;//小乌龟随机出现
//int tortoiseX;
//int tortoiseY;
//bool tortoiseExist;//当前窗口是否有小乌龟

int heroBlood;
int score;



//enum用于定义枚举变量,typedef和obstacle_type配合产生一种新的类型
typedef enum {
    
    //枚举类型
	TORTOISE,//乌龟 0
	LION,//狮子 1
	HOOK1,
    HOOK2,
	HOOK3,
	HOOK4,
	OBSTACLE_TYPE_COUNT //6,表示类型个数
}obstacle_type;
obstacle_type type1;//obstacle_type为一种新定义的类型

vector<vector<IMAGE>> obstacleImgs;
//C++特殊二维数组:存放三种障碍物各自图片;C语言IMAGE obstacleImags[3][12]但不可变
//vector<int> data;//特殊一维数组:可变数组,类型为int
//IMAGE obstacleImgs[3][12];

typedef struct obstacle {
    
    
	obstacle_type type;//障碍物类型
	int imgIndex;//当前显示图片的序号
	int x, y;//障碍物坐标
	int speed;//障碍物速度
	int power;//杀伤力
	bool exist;//是否存在
	bool hited;//是否碰撞,图片加载单独放外面少内存消耗
	bool passed;//是否通过
}obstacle_t;
obstacle_t obstacles[OBSTACLE_COUNT];

int lastObsIndex;

IMAGE imgHeroDown[2];
bool heroDown;//表示玩家是否下蹲

IMAGE imgSZ[10];


//游戏初始化
void init(){
    
    
	initgraph(WIN_WIDTH, WIN_HEIGHT);//面板
	char name[64];//图片文件名
	for (int i = 0; i < 3; i++){
    
    
		sprintf(name, "res/bg%03d.png", i + 1);//用于生成文件名,%03d三位整数,不够三位补零
		loadimage(&imgBgs[i],name);//在面板中加载图片
		bgX[i] = 0;
	}
	for (int i = 0; i < 12; i++){
    
    //加载Hero奔跑的图片帧素材
		sprintf(name, "res/hero%d.png", i + 1);
		loadimage(&imgHeros[i], name);
	}
	//设置hero初始位置
	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 = 345 - imgTortoise.getheight()+4;
	IMAGE imgTort;
	loadimage(&imgTort, "res/t1.png");//加载乌龟图片,这里只用了一张
	vector<IMAGE>imgTortArray;//定义乌龟障碍图片空数组
	imgTortArray.push_back(imgTort);//将imgTort添加到空数组,若多个图片可利用循环添加见LION
	obstacleImgs.push_back(imgTortArray);//将乌龟图片数组添加到上面的二维数组中

	IMAGE imgLion;
	vector<IMAGE> 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;
	}
	//加载下蹲素材
	loadimage(&imgHeroDown[0], "res/d2.png");
	loadimage(&imgHeroDown[1], "res/d2.png");
	heroDown = false;
	//柱子障碍
	IMAGE imgH;
	for (int i = 0; i < 4; i++) {
    
    
		vector<IMAGE> imgHookArray;
		sprintf(name, "res/h%d.png", i + 1);
		loadimage(&imgH, name, 63, 260, true);
		imgHookArray.push_back(imgH);
		obstacleImgs.push_back(imgHookArray);
	}
	heroBlood = 100;
	//预加载音效,防止hit音效延迟
	preLoadSound("res/hit.mp3");
	mciSendString("play res/bg.mp3 repeat", 0, 0, 0);
	lastObsIndex = -1;
	score = 0;

	//加载数字图片
	for (int i = 0; i < 10; i++) {
    
    
		sprintf(name, "res/sz/%d.png", i);
		loadimage(&imgSZ[i], name);
	}
}

void creatObstacle() {
    
    
	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 ()% OBSTACLE_TYPE_COUNT);
	obstacles[i].type = (obstacle_type)(rand() % 3);

	//限制障碍物出现无死角的情况
	if (lastObsIndex >= 0 &&
				obstacles[lastObsIndex].type >= HOOK1 &&
				obstacles[lastObsIndex].type <= HOOK4 &&
				obstacles[i].type == LION &&
				obstacles[lastObsIndex].x > (WIN_WIDTH - 500)) {
    
    
		obstacles[i].type = TORTOISE;
	}
	lastObsIndex = i;

	if (obstacles[i].type == HOOK1) {
    
    
		obstacles[i].type = (obstacle_type)((int)(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 = 10;
	}
	else if (obstacles[i].type >= HOOK1&& obstacles[i].type <= HOOK4) {
    
    
		obstacles[i].speed = 0;
		obstacles[i].power = 10;
		obstacles[i].y = 0;
	}
	obstacles[i].passed = false;
}

void checkHit() {
    
    
	for (int i = 0; i <OBSTACLE_COUNT; i++) {
    
    
		if (obstacles[i].exist && obstacles[i].hited==false) {
    
    
			int a1x, a1y, a2x, a2y;//hero图片斜对角两点坐标
			int off = 30;//偏移量,不那么严格
			if (!heroDown) {
    
    //非下蹲
				a1x = heroX + off;
				a1y = heroY + off;
				a2x = heroX + imgHeros[heroIndex].getwidth() - off;
				a2y = heroY + imgHeros[heroIndex].getheight();
			 
			}
			else {
    
    //下蹲时
				a1x = heroX + off;
				a1y = 345-imgHeroDown[heroIndex].getheight();
				a2x = heroX + imgHeroDown[heroIndex].getwidth() - off;
				a2y = 345;
			}
			int b1x = obstacles[i].x + off;//障碍物对角坐标
			int b1y = obstacles[i].y + off;
			int b2x = obstacles[i].x + obstacleImgs[obstacles[i].type][obstacles[i].imgIndex].getwidth() - off;
			int b2y = obstacles[i].y+obstacleImgs[obstacles[i].type][obstacles[i].imgIndex].getheight()- 10;
			if (rectIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) {
    
    //tools里的矩形碰撞检测函数
				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;
		}
	}
	//heroIndex = (heroIndex + 1) % 12;//数组序号不想要1-12;而要0-11
	//实现跳跃
	if (heroJump) {
    
     //按下空格后,跳跃启动函数jump()为true
		if (heroY < jumpHeightMax) {
    
    //超过高度上限;注:Y坐标正方向朝下;X朝右
			heroJumpOff = 4;
		}
		heroY += heroJumpOff;//上升或下落
		if (heroY > 345 - imgHeros[0].getheight()) {
    
    //地面坐标
			heroJump = false;
			heroJumpOff = -4;
		}
	}
	else if (heroDown) {
    
    
		static int count = 0;
		int delays[2] = {
    
     4,12 };//前一帧时间短点,后一帧下蹲时间长点儿 
		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 = 60;
	frameCount++;
	if (frameCount > enemyFre) {
    
    //帧数计够数,该产生乌龟了;计数是随机的
		frameCount = 0;
		//if (!tortoiseExist)//如果不存在
		//{
    
    
		//	tortoiseExist = true;//使存在
		//	tortoiseX = WIN_WIDTH;
		//enemyFre = 200 + rand() % 300;//计数随机
		//}
		enemyFre = 50 + rand() % 50;//50+0~49即50-99
		creatObstacle();
	}

	//if (tortoiseExist) {//更新小乌龟位置如果存在
	//	tortoiseX -= bgSpeed[2];
	//	if (tortoiseX < -imgTortoise.getwidth()) {
    
    
	//		tortoiseExist = false;
	//	}
	//}
	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()) {
    
    
				obstacles[i].exist = false;
			}
			int len = obstacleImgs[obstacles[i].type].size();
			obstacles[i].imgIndex = (obstacles[i].imgIndex + 1) % len;
		}
	}

	//"碰撞检测“
	checkHit();
}

	void updateBg()//渲染游戏背景,背景是移动的
	{
    
    
		putimagePNG2(bgX[0], 0, &imgBgs[0]);//指定图片加载位置,左上角坐标,x不固定变量
		putimagePNG2(bgX[1], 119, &imgBgs[1]);
		putimagePNG2(bgX[2], 330, &imgBgs[2]);
	}

	void jump() 
	{
    
    
		//启动跳跃开关,真正实现此功能在fly()函数
		heroJump = true;
		update = true;//如果没计时到30ms按下按键可能跳走停止画面刷新,这里强制使其继续刷新
	}

	void down() {
    
    
		heroDown = true;
		update = true;
		heroIndex = 0;
	}

	void keyEvent()//用户按键输入,跳跃蹲下等
	{
    
    
		char ch;
		//scanf函数需要回车键会卡住程序,不用
		if (_kbhit())//有按键按下,kbhit()返回 true
		{
    
    
			ch=_getch();//不需要回车直接读取
			if (ch == 'w') {
    
    
				jump();
			}
			else if (ch == 's') {
    
    
				down();
			}
		}
	}


	void updateEnemy() {
    
    
		//if (tortoiseExist) {//初始值为false
		//	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]);
		}
	}

	void updateBloodBar() {
    
    
		drawBloodBar(10, 10, 200, 10, 2, BLUE, DARKGRAY, RED, heroBlood / 100.0);//tools工具
	}

	void checkOver() {
    
    
		if (heroBlood <= 0) {
    
    
			loadimage(0, "res/over.png");
			FlushBatchDraw();//显示缓存
			mciSendString("stop res/bg.mp3 repeat", 0, 0, 0);//关闭背景音乐
			system("pause");
			//暂停之后,充币复活,或者开始下一局
			heroBlood = 100;
			mciSendString("play res/bg.mp3", 0, 0, 0);
		}
	}

	void checkScore() {
    
    
		for (int i = 0; i < OBSTACLE_COUNT; i++) {
    
    
			if (obstacles[i].exist &&
					obstacles[i].passed == false &&
					obstacles[i].hited ==false && 
					obstacles[i].x + obstacleImgs[obstacles[i].type][0].getwidth() < heroX) {
    
    
				score++;
				obstacles[i].passed = true;
				printf("分数:%d\n", score);

			}
		}
	}

	void updateScore() {
    
    
		//50=>"50"  '5'  '5'-'0'==5
		char str[8];
		sprintf(str, "%d", score);
		int x = 20;
		int y = 25;
		for (int i = 0; str[i]; i++) {
    
    
			int sz = str[i] - '0';
			putimagePNG(x, y, &imgSZ[sz]);
			x += imgSZ[sz].getwidth() + 5;
		}
	}

	void checkWin() {
    
    
		if (score >= WIN_SCORE) {
    
    
			FlushBatchDraw();
			mciSendString("play res/win.mp3", 0, 0, 0);
			Sleep(2000);
			loadimage(0, "res/win.png");
			FlushBatchDraw();
			mciSendString("stop res/bg.mp3", 0, 0, 0);
			system("pause");
			heroBlood = 100;
			score = 0;
			mciSendString("play res/bg.mp3 repeat", 0, 0, 0);
		}
	}

int main(void){
    
    
	init();
	//显示初始界面
	loadimage(0, "res/over.png");
	system("pause");

	int timer = 0;
	while (1)
	{
    
    

		keyEvent();
		timer += getDelay();//tools工具,帧休眠,代替sleep提高实时性
		if (timer > 30)
		{
    
    
			timer = 0;
			update = true;
			//Sleep(30);//帧休眠,不然速度太快
		}
		if (update)
		{
    
    
			update = false;
			BeginBatchDraw();//tools工具,为了改善闪烁
			updateBg();
			//putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);//确定英雄位置,利用图片帧实现奔跑
			updateHero();
			updateEnemy();
			updateBloodBar();
			updateScore();
			checkWin();
			EndBatchDraw();
			checkOver();//游戏是否结束
			checkScore();
			fly();
		}
	}
	return 0;
}

效果展示

在这里插入图片描述
在这里插入图片描述

总结

遇到挫折,要有勇往直前的信念,马上行动,坚持到底,决不放弃,成功者决不放弃,放弃者绝不会成功。成功的道路上,肯定会有失败;对于失败,我们要正确地看待和对待,不怕失败者,则必成功;怕失败者,则一无是处,会更失败。
本次项目练习学习亲测有效可以运行,参考了互联网上诸多材料,仅供互相交流学习使用!
欢迎自学的小伙伴们发言指正!

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_51658186/article/details/129408759