**
遇到问题:背景图片的png格式图片出现黑色
解决方案:导入tools.h和tools.cpp,包含到头文件并引用putimagePNG2
遇到问题:背景闪烁抖动(不断打印与渲染)
解决方案:双缓冲机制(一次性准备好后一起渲染)
BeginBatchDraw();EndBatchDraw();
heroIndex = (heroIndex + 1) % 12;
1> 角色Y坐标 > 最大高度,则向上偏移
2> 角色Y坐标 < 最大高度,将偏移量改为正,则向下偏移
1> 定义并取值小乌龟X坐标,Y坐标,图片帧序号
2> 图片帧序号数组+1取余实现小乌龟旋转效果
3>实现小乌龟水平移动,当小乌龟移出窗口后消失
4>定义小乌龟的出现频率,使小乌龟随2s~5s随机出现、
1>定义exist,imgIndex,type,x,y
2>乌龟的数值,狮子的数值
遇到问题:下蹲速度太快
解决方案:使用count增加帧数让图片变慢,增加delay延时
遇到问题:角色碰撞后持续掉血
解决方案:新定义hited,控制其开关来解决持续掉血
a)血量<= 0,加载结束图片,0表示加载到界面;关闭背景音乐;游戏暂停
b)暂停以后,直接开始下一局;血量回复;播放背景音乐
#include
#include
#include
#include
#include "tools.h"
using namespace std; //声明命名空间
#define WIN_SCORE 3
#define WIN_WIDTH 1012
#define WIN_HEIGHT 396 //宏定义,便于维护和更改
#define OBSTACLE_COUNT 10 //宏定义障碍物池数量
IMAGE imgBgs[3]; //(全局数组)存放背景图片
int bgX[3]; //背景图片的x坐标
int bgSpeed[3] = {2, 3, 5}; //三张图不同的速度
IMAGE imgHeros[12]; //存放角色奔跑图片
int heroX; //角色的x坐标
int heroY; //角色的y坐标
int heroIndex; //角色奔跑的图片帧序号
bool heroJump; //表示角色正在跳跃开关
int jumpHeighetMax; //跳跃的最大值
int heroJumpOff; //表示偏移量
int update; //表示是否马上刷新画面
// IMAGE imgTortoise[7]; //小乌龟
// int torToiseX; //小乌龟的水平坐标
// int torToiseY; //小乌龟的垂直坐标
// bool torToiseExist; //当前窗口是否存在小乌龟
// int tortoiseIndex;//小乌龟旋转的图片帧序号
int heroBlood; //玩家血量
int score; //得分
//枚举封装障碍物的类型
typedef enum {
TORTOISE, //乌龟 0
LION, //狮子 1
HOOK1, //四个柱子障碍物
HOOK2,
HOOK3,
HOOK4,
OBSTACLE_TYPE_CONST // 6 便于查看枚举数
}obstacle_type;
vector<vector<IMAGE>>obstacleImgs;//c++存放所用障碍物的各个图片
//相当于c中:IMAGE obstacleImgs[3][12]
//结构体封装障碍物
typedef struct obstacle {
int 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, EW_SHOWCONSOLE); //创建游戏窗口
//加载背景资源
char name[64];
for (int i = 0; i < 3; i++) {
//"res/bg001.png" "res/bg002.png" "res/bg003.png"
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" ... "res/hero12.png"
sprintf(name, "res/hero%d.png", i+1);
loadimage(&imgHeros[i], name);
}
//设置角色的初始位置
heroX = WIN_WIDTH * 0.5 - imgHeros[0].getwidth() * 0.5;
heroY = 355 - imgHeros[0].getheight();
heroIndex = 0;
heroJump = false;
jumpHeighetMax = 355 - imgHeros[0].getheight() - 120;
heroJumpOff = -5;
update = true;
// 6.1 加载小乌龟素材
// for (int i = 0; i < 7; i++) {
// // "res/t1.png" ... "res/t7.png"
// sprintf(name, "res/t%d.png", i + 1);
// loadimage(&imgTortoise[i], name);
// }
// torToiseExist = false;
// torToiseY = 355 - imgTortoise[0].getheight();
//用结构体加载小乌龟素材
IMAGE imgTort;
vector<IMAGE> imgTortArray;
for (int i = 0; i < 7; i++) {
// "res/t1.png" ... "res/t7.png"
sprintf(name, "res/t%d.png", i + 1);
loadimage(&imgTort, name);
imgTortArray.push_back(imgTort); //存放到一维
}
obstacleImgs.push_back(imgTortArray); //存放到二维
//用结构体加载狮子素材
IMAGE imgLion;
vector<IMAGE> imgLionArray;
for (int i = 0; i < 6; i++) {
// "res/p1.png" ... "res/p6.png"
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;
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;
//预加载音效
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 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() % OBSTACLE_TYPE_CONST); //使障碍物随机出现
obstacles[i].type = (obstacle_type)(rand() % 3); //对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 += rand() % 4; //0...3
}
obstacles[i].x = WIN_WIDTH;
obstacles[i].y = 355 - obstacleImgs[obstacles[i].type][0].getheight();
//乌龟的数值
if (obstacles[i].type == TORTOISE) {
obstacles[i].speed = 1; //乌龟速度值
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;
}
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; //a1x,a1y为左上角坐标:a2x,a2y为右下角坐标
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 = 355 - imgHeroDown[heroIndex].getheight();
a2x = heroX + imgHeroDown[heroIndex].getwidth() - off;
a2y = 355;
}
//障碍物碰撞检测
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() - 10;
//检测角色与障碍物是否相交
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 go()
{
for (int i = 0; i < 3; i++) {
bgX[i] -= bgSpeed[i];
if (bgX[i] < -WIN_WIDTH) {
bgX[i] = 0; //循环实现
}
}
//实现跳跃
if (heroJump) {
if (heroY < jumpHeighetMax) {
heroJumpOff = 6;
}
heroY += heroJumpOff;
if (heroY > 355 - imgHeros[0].getheight()) {
heroJump = false;
heroJumpOff = -6;
}
}
else if (heroDown) { //实现下蹲
static int count = 0;
int delays[2] = {6, 30};
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) { //25ms刷新一次,刷新一个障碍物
frameCount = 0;
enemyFre = 50 + rand() % 50; //障碍物在50 ~ 99随机出现
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;
}
}
// 6.1 出现小乌龟
//if (!torToiseExist) {
// torToiseExist = true;
// torToiseX = WIN_WIDTH; //小乌龟从窗口宽度边缘出现
// enemyFre = 200 + rand() % 300; //小乌龟在200 ~ 500(2s~5s)随机出现
//}
// 6.1 实现小乌龟水平移动
// if (torToiseExist) {
// tortoiseIndex = (tortoiseIndex + 1) % 7; //实现乌龟旋转效果
// torToiseX -= bgSpeed[2];
// if (torToiseX < -imgTortoise[0].getwidth()) { //当小乌龟移出窗口后消失
// torToiseExist = false;
// }
//}
//玩家和障碍物的“碰撞检测”处理
checkHit();
}
//渲染游戏背景
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;
}
void down() {
heroDown = true; //打开下蹲开关
update = true;
heroIndex = 0;
}
//处理用户输入的按键
void keyEvent()
{
char ch;
if (_kbhit()) { //如果有按键按下,kbhit()返回 ture
ch = _getch(); //_getch不需要按回车即可直接读取
if (ch == ' ') {
jump();
}
else if (ch == 'z') { //按z下蹲
down();
}
}
}
void updateEnemy() {
//渲染所有障碍物
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]);
}
}
}
// 6.1 渲染小乌龟
// if (torToiseExist) {
// putimagePNG2(torToiseX, torToiseY, WIN_WIDTH, &imgTortoise[tortoiseIndex]);
// }
//更新跳跃和下蹲
void updateHero() {
if (!heroDown) {
putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
}
else {
int y = 355 - imgHeroDown[heroIndex].getheight();
putimagePNG2(heroX, y, &imgHeroDown[heroIndex]);
}
}
//添加血条
void updateBloodBar() {
drawBloodBar(10, 10, 200, 10, 2, BLUE, DARKGRAY, RED, heroBlood / 100.0);
}
//判断游戏结束
void checkOver() {
if (heroBlood <= 0) {
loadimage(0,"res/over.png"); //加载结束图片,0表示加载到界面
FlushBatchDraw();
mciSendString("stop res/bg.mp3", 0, 0, 0); // 关闭背景音乐
system("pause"); //游戏暂停
//暂停以后,直接开始下一局
heroBlood = 100;
score = 0;
mciSendString("play res/bg.mp3 repeat", 0, 0, 0);
}
}
//添加初始界面
void checkStart() {
loadimage(0, "res/over.png");
system("pause");
}
//越过障碍物自动得分机制
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("score: %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", 0, 0, 0);
}
}
int main(void)
{
init();
checkStart();
int timer = 0;
while (1) {
keyEvent();
timer += getDelay(); //返回距离上一次调用间隔的时间(单位:ms),第一次调用时返回0
if (timer > 25) { //实现sleep25ms循环效果
timer = 0;
update = true;
}
if (update) { //如果没到30ms触发按键,直接刷新
update = false;
BeginBatchDraw(); //双缓冲机制(防背景闪屏)
updateBg();
//putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
updateHero();
updateEnemy();
updateBloodBar();
updateScore();
checkWin();
EndBatchDraw();
checkOver();
checkScore();
go();
}
//Sleep(25); //导致休眠
}
system("pause");
return 0;
}