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