«

C++怎么利用easyx图形库实现天天酷跑小游戏

时间:2024-8-4 06:40     作者:韩俊     分类: Java


这篇文章主要介绍“C++怎么利用easyx图形库实现天天酷跑小游戏”,在日常操作中,相信很多人在C++怎么利用easyx图形库实现天天酷跑小游戏问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++怎么利用easyx图形库实现天天酷跑小游戏”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

开发日志

1.创建项目

2.导入素材

3.创建游戏界面

从用户界面入手

选择图形库或者其他引擎,酷跑是基于“easyx”图形库的

1)创建游戏窗口

2)设计游戏背景

a.三重背景不同的速度移动

b.循环滚动背景的实现

3)实现游戏背景

a.加载背景资源

b.渲染(实现打印图片的效果)背景知识:坐标

遇到的问题:背景图片的png格式图片出现黑色

4.实现玩家奔跑

5.实现玩家跳跃

6.实现随机小乌龟

7.创建结构体结构类型

8.使用障碍物结构体后重新初始化

9.封装后多个障碍物的显示

10.实现玩家的下蹲技能

11.添加柱子障碍物

代码实现

//#undef UNICODE
//#undef _UNICODE

#include <stdio.h>
#include <graphics.h>
#include <conio.h>
#include <vector> //c++ 长度可变的数组
#include "tools.h"

using namespace std; //声明命名空间
#define WIN_SCORE 10

#define WIN_WIDTH 1012 //定义宏 便于后期维护与处理
#define WIN_HEIGHT 396
#define OBSTACLE_COUNT 10

IMAGE imgBgs[3];//背景图片——创建图片数组
int bgX[3];//背景图片的X坐标(不断变化)
int bgSpeed[3] = { 1,2,4 };//控制3个背景不同速度

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; //小乌龟的竖直坐标
//bool torToiseExist; //当前窗口是否有小乌龟

int heroBlood; //定义玩家血量
int score;

typedef enum {
    TORTOISE, //乌龟 0
    LION, //狮子 1
    HOOK1,
    HOOK2,
    HOOK3,
    HOOK4,
    OBSTACLE_TYPE_COUNT //边界 6
}obstacle_type;

// 相当于 IMAGE obstacleImgs[3][5]
vector<vector<IMAGE>>obstacleImgs; //二维数组 存放所有障碍物的各个图片

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;//解决障碍物bug(柱子与狮子在一起)

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);//%03d占3位,不足3位自动补0
        //#undef _UNICODE  
        loadimage(&imgBgs[i], name);//把3个图片加载到了图片数组的位置

        bgX[i] = 0;
    }

    //加载Hero奔跑的图片帧素材
    for (int i = 0; i < 12; i++) {
        sprintf(name, "res/hero%d.png", i + 1);
        loadimage(&imgHeros[i], name);
    }
     
    //设置玩家的初始位置
    heroX = WIN_WIDTH * 0.5 - imgHeros[0].getwidth() * 0.5;//X坐标:屏幕宽度的一半减去人物宽度的一半
    heroY = 345 - imgHeros[0].getheight();//Y坐标:脚底坐标减去人物高度
    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() + 5;
    IMAGE imgTort;
    loadimage(&imgTort, "res/t1.png");
    vector<IMAGE>imgTortArray;
    imgTortArray.push_back(imgTort);//添加图片
    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/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, 250, 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 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 += rand() % 4; //0-3
    }

    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 = 1;
        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;
            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;
            }

            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
", heroBlood);
                playSound("res/hit.mp3");
                obstacles[i].hited = true;
            }
        }
    }
}

//让背景动起来
void run() {
    for (int i = 0; i < 3; i++) {
        bgX[i] -= bgSpeed[i];//3个背景移动的速度不同
        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 if (heroDown) { //人物下蹲
        static int count = 0;
        int delays[2] = { 8,30 }; //设置下蹲的时间不一样
        count++;
        if (count >= delays[heroIndex]) {
            count = 0;
            heroIndex++;
            if (heroIndex >= 2) {
                heroIndex = 0;
                heroDown = false;
            }
        }
    }
    else{ //不跳跃
        heroIndex = (heroIndex + 1) % 12; //12张图片循环播放完成一系列动作
    }

    //创建障碍物
    static int frameCount = 0;
    static int enemyFre = 50;
    frameCount++;
    if (frameCount > enemyFre){
        frameCount = 0;
        //if (!torToiseExist) { //避免屏幕同时出现多个小乌龟
        //  torToiseExist = true;
        //  torToiseX = WIN_WIDTH;    
        //  enemyFre=rand()%301+200; //每200-500帧随机出现一只龟
        //}
        enemyFre = rand() % 50 + 50;
        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() * 2) {
                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]);
    putimagePNG2(bgX[1], 119, &imgBgs[1]);
    putimagePNG2(bgX[2], 330, &imgBgs[2]);

}

//实现跳跃
void jump() {
    heroJump = true;
    update = true; //未处于刷新时也能跳跃
}

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

//处理用户按键的输入
void keyEvent() {

    //char c;
    //scanf("%c", &c);   会直接阻塞程序的执行

    if (GetAsyncKeyState(VK_UP)){ //虚拟键
        jump();
        /*
            if(kbhit())   //kbhit()判断有无键盘输入。若有按键按下,则kbhit()返回 TRUE
            {
                char ch = _getch();//不需要按下回车即可直接读取
                if (ch == ' ') {//按下空格跳跃
                    jump();
            }
        */
    }

    else if (GetAsyncKeyState(VK_DOWN)) {
        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]);
    }
}

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

void checkOver() {
    if (heroBlood <= 0) {
        loadimage(0, "res/over.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);
    }
}

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

void updateScore() {
    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();//此函数返回距离上一次调用间隔的时间,第一次返回0
        if (timer > 30) { //30毫秒刷新时间
            timer = 0;
            update = true;
        }
        if (update) {
            update = false;
            BeginBatchDraw();//这个函数用于开始批量绘图。执行后,任何绘图操作都将暂时不输出到绘图窗口上,直到执行 FlushBatchDraw 或 EndBatchDraw 才将之前的绘图输出。
            updateBg();
            //putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
            updateHero();
            updateEnemy();
            updateBloodBar();
            updateScore();
            checkWin();
            EndBatchDraw();//这个函数用于结束批量绘制,并执行未完成的绘制任务。   这两个函数主要为了消除闪烁。

            checkOver();
            checkScore();

            run();
        }

        //Sleep(30); //休眠
    }

    system("pause");
    return 0;
}

##  tool头文件
```javascript
#include <stdio.h>
#include <Windows.h>
#include "tools.h"

#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")

int getDelay() {
    static unsigned long long lastTime = 0;
    unsigned long long currentTime = GetTickCount();
    if (lastTime == 0) {
        lastTime = currentTime;
        return 0;
    }
    else {
        int ret = currentTime - lastTime;
        lastTime = currentTime;
        return ret;
    }
}

// 载入PNG图并去透明部分
void putimagePNG(int  picture_x, int picture_y, IMAGE* picture) //x为载入图片的X坐标,y为Y坐标
{
    DWORD* dst = GetImageBuffer();    // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
    DWORD* draw = GetImageBuffer();
    DWORD* src = GetImageBuffer(picture); //获取picture的显存指针
    int picture_width = picture->getwidth(); //获取picture的宽度,EASYX自带
    int picture_height = picture->getheight(); //获取picture的高度,EASYX自带
    int graphWidth = getwidth();       //获取绘图区的宽度,EASYX自带
    int graphHeight = getheight();     //获取绘图区的高度,EASYX自带
    int dstX = 0;    //在显存里像素的角标

    // 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
    for (int iy = 0; iy < picture_height; iy++)
    {
        for (int ix = 0; ix < picture_width; ix++)
        {
            int srcX = ix + iy * picture_width; //在显存里像素的角标
            int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
            int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的R
            int sg = ((src[srcX] & 0xff00) >> 8);   //G
            int sb = src[srcX] & 0xff;              //B
            if (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight)
            {
                dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //在显存里像素的角标
                int dr = ((dst[dstX] & 0xff0000) >> 16);
                int dg = ((dst[dstX] & 0xff00) >> 8);
                int db = dst[dstX] & 0xff;
                draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16)  //公式: Cp=αp*FP+(1-αp)*BP  ; αp=sa/255 , FP=sr , BP=dr
                    | ((sg * sa / 255 + dg * (255 - sa) / 255) << 8)         //αp=sa/255 , FP=sg , BP=dg
                    | (sb * sa / 255 + db * (255 - sa) / 255);              //αp=sa/255 , FP=sb , BP=db
            }
        }
    }
}

// 适用于 y <0 以及x<0的任何情况
void putimagePNG2(int x, int y, IMAGE* picture) {
    IMAGE imgTmp;
    if (y < 0) {
        SetWorkingImage(picture);
        getimage(&imgTmp, 0, -y,
            picture->getwidth(), picture->getheight() + y);
        SetWorkingImage();
        y = 0;
        picture = &imgTmp;
    }

    if (x < 0) {
        SetWorkingImage(picture);
        getimage(&imgTmp, -x, 0, picture->getwidth() + x, picture->getheight());
        SetWorkingImage();
        x = 0;
        picture = &imgTmp;
    } 

    putimagePNG(x, y, picture);
}

// 适用于 y <0 以及y>0的任何情况
void putimagePNG2(int x, int y, int winWidth, IMAGE* picture) {
    IMAGE imgTmp;
    if (y < 0) {
        SetWorkingImage(picture);
        getimage(&imgTmp, 0, -y,
            picture->getwidth(), picture->getheight() + y);
        SetWorkingImage();
        y = 0;
        picture = &imgTmp;
    }

    if (x < 0) {
        SetWorkingImage(picture);
        getimage(&imgTmp, -x, 0, picture->getwidth() + x, picture->getheight());
        SetWorkingImage();
        x = 0;
        picture = &imgTmp;
    }
    else if (x >= winWidth) {
        return;
    }
    else if (x > winWidth-picture->getwidth()) {
        SetWorkingImage(picture);
        getimage(&imgTmp, 0, 0, winWidth - x, picture->getheight());
        SetWorkingImage();
        picture = &imgTmp;
    }

    putimagePNG(x, y, picture);
}

//设A[x01,y01,x02,y02]  B[x11,y11,x12,y12].
bool rectIntersect(int x01, int y01, int x02, int y02,
    int x11, int y11, int x12, int y12)
{
    int zx = abs(x01 + x02 - x11 - x12);
    int x = abs(x01 - x02) + abs(x11 - x12);
    int zy = abs(y01 + y02 - y11 - y12);
    int y = abs(y01 - y02) + abs(y11 - y12);
    return  (zx <= x && zy <= y);
}

void  preLoadSound(const char* name) {
    char cmd[512];
    sprintf_s(cmd, sizeof(cmd), "open %s alias %s-1", name, name);
    mciSendString(cmd, 0, 0, 0);
    sprintf_s(cmd, sizeof(cmd), "open %s alias %s-2", name, name);
    mciSendString(cmd, 0, 0, 0);
}

void  playSound(const char* name) {
    static int index = 1;
    char cmd[512];

    if (index == 1) {
        sprintf_s(cmd, sizeof(cmd), "play %s-1", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "close %s-2", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "open %s alias %s-2", name, name);
        mciSendString(cmd, 0, 0, 0);
        index++;
    }
    else if (index == 2) {
        sprintf_s(cmd, sizeof(cmd), "play %s-2", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "close %s-1", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "open %s alias %s-1", name, name);
        mciSendString(cmd, 0, 0, 0);
        index = 1;
    }
}

void drawBloodBar(int x, int y, int width, int height, int lineWidth, int boardColor, int emptyColor, int fillColor, float percent) {
    LINESTYLE lineStyle;
    getlinestyle(&lineStyle);
    int lineColor = getlinecolor();
    int fileColor = getfillcolor();

    if (percent < 0) {
        percent = 0;
    }

    setlinecolor(BLUE);
    setlinestyle(PS_SOLID | PS_ENDCAP_ROUND, lineWidth);
    setfillcolor(emptyColor);
    fillrectangle(x, y, x + width, y + height);
    setlinestyle(PS_SOLID | PS_ENDCAP_FLAT, 0);
    setfillcolor(fillColor);
    setlinecolor(fillColor);
    if (percent > 0) {
        fillrectangle(x + 0.5 * lineWidth, y + lineWidth * 0.5, x + width * percent, y + height - 0.5 * lineWidth);
    }
    
    setlinecolor(lineColor);
    setfillcolor(fillColor);
    setlinestyle(&lineStyle);
}

标签: java

热门推荐