https://www.bilibili.com/video/BV1or4y1W74v/
B站视频
有空的时候会录视频教各位怎么实现这个小游戏。
视频长度为半小时左右。
main.cpp
#include
#include
#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 // 引用图形库头文件
#include
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