easyX像素鸟PixelBird

https://www.bilibili.com/video/BV1or4y1W74v/

运行效果

菜单

请添加图片描述

运行画面

请添加图片描述

死亡画面

请添加图片描述

视频演示

B站视频

说明

有空的时候会录视频教各位怎么实现这个小游戏。
视频长度为半小时左右。

源码

main.cpp

#include <graphics.h>
#include <cstdio>
#include "Button.h"
#include "Bean.h"

#define WIDTH 450 //画面高度
#define HEIGHT 600 //画面宽度

#define NODE_Height 130 //洞口高度
#define PILLAR_Width 50 //柱子宽度
#define BIRD_HW 20 //鸟的宽高
#define GRAVITY 4 //世界的重力
#define PILLAR_NUM 20 //柱子数量,只要大于3就可以

#define INTERVAL 1 //两个柱子的间隔,1的话,就是一个画面的宽度,2的话是半个画面的宽度

#define DEBUG true

const static char *startText = "开始游戏";
const static char *dieText = "你挂了";
static Pillar pillars[PILLAR_NUM];//柱子数组

IMAGE img_bird,img_background;

static enum GameState {
    CHOOSE, PLAY, DIE
} state;//游戏状态

class Bird {
public:
    int Y;//鸟的y坐标
    int v;//鸟的速度
    int frame;//跳跃状态剩余时长
} bird;
void drawAlpha(IMAGE *picture, int picture_x, int picture_y); //绘制透明图片
int randPath(int a, int b) {//生成a到b的随机数
    return rand() % (b - a + 1) + a;
}

void initPillar() {//初始化开局的柱子
    for (int i = 0; i < PILLAR_NUM; ++i) {
        pillars[i].x = WIDTH / INTERVAL * i;//每一个画面宽度一个柱子
        pillars[i].nodeY = randPath(0, HEIGHT - NODE_Height);//洞口位置随机
    }
}

void initBird() {//初始化鸟的参数
    bird.v = 1;
    bird.Y = HEIGHT / 2 - BIRD_HW;
}

void startButton_clickAction() {//开始菜单的点击事件
    printf("startGame\n");
    state = PLAY;
}

void observeMouse(Button btn);

void drawPillar() {//绘制柱子
    setfillcolor(GREEN);
    for (int i = 0; i < PILLAR_NUM; ++i) {
        solidrectangle(pillars[i].x, 0, pillars[i].x + PILLAR_Width, pillars[i].nodeY);
        solidrectangle(pillars[i].x, pillars[i].nodeY + NODE_Height, pillars[i].x + PILLAR_Width, HEIGHT);
        pillars[i].x--;
    }
    if (pillars[0].x + PILLAR_Width < 0) {//如果柱子已经在屏幕外面,则把他放到数组最后一个,并重新设置位置
        Pillar temp = pillars[0];
        temp.x = pillars[PILLAR_NUM - 1].x + WIDTH / INTERVAL;
        temp.nodeY = randPath(0, HEIGHT - NODE_Height);
        pillars[0] = pillars[PILLAR_NUM - 1];
        pillars[PILLAR_NUM - 1] = temp;
    }
}

void drawBird() {//绘制鸟
   // setfillcolor(WHITE);
    //鸟在x坐标是死的,(WIDTH + BIRD_HW) / 2
    //solidrectangle((WIDTH + BIRD_HW) / 2, bird.Y, (WIDTH + BIRD_HW) / 2 + BIRD_HW, bird.Y + BIRD_HW);
    drawAlpha(&img_bird,(WIDTH + BIRD_HW) / 2, bird.Y);
    if (bird.frame != 0) {
        bird.frame--;//如果鸟在跳跃状态,则状态持续时长减一
    } else
        bird.v = 1;
    bird.Y += bird.v + GRAVITY / 2;//x=v*t+(a*t)2物理公式
}

bool isCrash(int l1, int t1, int r1, int b1,
             int l2, int t2, int r2, int b2) {
    if (l1 < r2 && l2 < r1 && t1 < b2 && t2 < b1) {
        return true;
    }
    return false;
}

static int score;//游戏得分

void check() {
    for (int i = 0; i < PILLAR_NUM; ++i) {//鸟和柱子碰撞检测
        if (pillars[i].x == ((WIDTH + BIRD_HW) / 2)) {//经过,则分数加一
            score++;
            printf("score:%d\n", score);
        }
        bool topCrash = isCrash(//上柱碰撞检测
                (WIDTH + BIRD_HW) / 2, bird.Y, (WIDTH + BIRD_HW) / 2 + BIRD_HW, bird.Y + BIRD_HW,
                pillars[i].x, 0, pillars[i].x + PILLAR_Width, pillars[i].nodeY
        );
        bool bottomCrash = isCrash(//下柱碰撞检测
                (WIDTH + BIRD_HW) / 2, bird.Y, (WIDTH + BIRD_HW) / 2 + BIRD_HW, bird.Y + BIRD_HW,
                pillars[i].x, pillars[i].nodeY + NODE_Height, pillars[i].x + PILLAR_Width, HEIGHT
        );
        if (topCrash || bottomCrash) {
            state = DIE;
            break;
        }
    }
    //鸟出界检测
    if (bird.Y<0||bird.Y+BIRD_HW>HEIGHT)
        state=DIE;
}
void drawScore(){
    char textValue[20];
    sprintf(textValue,"分数:%d",score);
    outtextxy(0,0,textValue);
}
void loadImg(){
    loadimage(&img_bird, "./image/bird.png",BIRD_HW,BIRD_HW);
    loadimage(&img_background, "./image/background.png",WIDTH,HEIGHT);
}
int main() {
    initgraph(WIDTH, HEIGHT, DEBUG ? SHOWCONSOLE : 0);
    loadImg();
    setbkmode(TRANSPARENT);
choose:
    TextButton startButton = TextButton();
    startButton.setText(WIDTH / 2, HEIGHT / 2, startText);
    startButton.setTextSize(25);
    startButton.clickAction = startButton_clickAction;
    while (state == CHOOSE) {//菜单选择
        BeginBatchDraw();
        putimage(0,0,&img_background);
        startButton.draw();
        observeMouse(startButton);
        EndBatchDraw();
        Sleep(13);
    }

    initPillar();
    initBird();
    score=0;
    while (state == PLAY) {
        if (kbhit())
            if (getch() == 32) {//如果有输入,且输入的是空格,则设置鸟跳跃13帧
                bird.frame = 13;
                bird.v = -GRAVITY;
            }
        BeginBatchDraw();
        putimage(0,0,&img_background);
        drawPillar();
        drawBird();
        check();
        drawScore();
        EndBatchDraw();
        //32是空格
        Sleep(13);//一帧耗时
    }
    cleardevice();
    TextButton dieButton=TextButton();
    dieButton.setText(WIDTH / 2, HEIGHT / 2, dieText);
    dieButton.setTextSize(45);
    dieButton.clickAction = startButton_clickAction;
    while (state==DIE){
        if (kbhit())
            if (getch() == 'x') {//如果有输入,且输入的是x,则重新开始
                state=CHOOSE;
            }
        BeginBatchDraw();
        putimage(0,0,&img_background);
        outtextxy(0,HEIGHT / 2-80,"按x重新开始");
        drawScore();
        dieButton.draw();
        EndBatchDraw();
        Sleep(13);
    }
    if (state==CHOOSE)
        goto choose;
    return 0;
}

void observeMouse(Button btn) {
    int mouseX, mouseY;
    if (MouseHit()) {//如果鼠标没有点击,则不交互
        MOUSEMSG msg = GetMouseMsg();
        mouseX = msg.x;
        mouseY = msg.y;
        if (msg.mkLButton && msg.uMsg != WM_MOUSEMOVE) {
            btn.receiveEvent(mouseX, mouseY);
        }
        FlushMouseMsgBuffer();
    }
}
void drawAlpha(IMAGE *picture, int picture_x, int picture_y) {
    DWORD *dst = GetImageBuffer();
    DWORD *draw = GetImageBuffer();
    DWORD *src = GetImageBuffer(picture);
    int picture_width = picture->getwidth();
    int picture_height = picture->getheight();
    int graphWidth = getwidth();
    int graphHeight = getheight();
    int dstX = 0;
    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);
            int sr = ((src[srcX] & 0xff0000) >> 16);
            int sg = ((src[srcX] & 0xff00) >> 8);
            int sb = src[srcX] & 0xff;
            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)
                             | ((sg * sa / 255 + dg * (255 - sa) / 255) << 8)
                             | (sb * sa / 255 + db * (255 - sa) / 255);
            }
        }
    }
}

Button.h

#ifndef BIRDDOWN_BUTTON_H
#define BIRDDOWN_BUTTON_H
#include <graphics.h>        // 引用图形库头文件
#include <conio.h>
class Button {
public:
    Button(int x, int y, int width, int height) {
        this->x = x;
        this->y = y;
        this->height = height;
        this->width = width;
    }

    Button() {};
    int x;
    int y;
    int height;
    int width;

    void (*clickAction)();

    void (*drawButton)(int x, int y, int width, int height);

    void receiveEvent(int cx, int cy) {
        if (isClickMe(cx, cy)) {
            clickAction();
        }
    }

    virtual void draw() {
        drawButton(x, y, width, height);
    }

private:
    bool isClickMe(int cx, int cy) const {
        return cx >= x && cy >= y && x + width >= cx && y + height >= cy;
    }
};

class TextButton : public Button {
private:
    char *showText;
    int textSize;
    int textX,textY;
public:
    TextButton(){};
    void setText(int x,int y,const char *text) {
        int len = strlen(text);
        showText =(char*) malloc(sizeof(char) * len);
        strcpy(showText, text);
        this->textX=x;
        this->textY=y;
    }
    void setTextSize(int size){
        this->textSize=size;
    }
    void draw(){
        width= textwidth(showText);
        height= textheight(showText);
        int oldMode=getbkmode();
        COLORREF oldColor=gettextcolor();
        settextcolor(BLUE);
        settextstyle(textSize, 0, nullptr, 0, 0, 1, false, false, false);
        x=textX-width/2;
        y=textY-height/2;
        outtextxy(x,y,showText);
        setbkmode(oldMode);
        settextcolor(oldColor);
    }
};

#endif //BIRDDOWN_BUTTON_H

Bean.h

#ifndef BIRDDOWN_BEAN_H
#define BIRDDOWN_BEAN_H
class Pillar{
public:
    int x;
    int nodeY;
};
#endif //BIRDDOWN_BEAN_H

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有头发的琦玉

打点钱,我会再努力的

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值