实现效果

8b0876d59b8040208d517bea983adc00.gif

操作说明

注意需要先把自动排列图标和将图标与网格对齐关闭运行后可能会打乱图标排列
空格控制开始与游戏中的跳跃。
ESC键结束程序。

实现逻辑

首先需要了解一下桌面图标的一些api,例如获取屏幕长宽,设置图标坐标这些,代码里有注释

游戏逻辑:


给他一个速度量和重力加速度量,初始速度为0,始终受到重力加速度影响,每当按下跳跃,将速度重设为一个跳跃速度量。

在墙的图标个数中随机取一个数,多于这个数的图标的y轴值加上图标大小*3(3指空三格),即可做到随机生成裂口。然后每次刷新让墙的所有图标的x轴值减少,即可做到墙的移动。
碰撞检测
当鸟的坐标加一个身位会与墙发生重叠时,说明发生了碰撞,具体看代码。
因为比较懒,坠落检测之类的就没做了。需要完善的话可以自己加下。
如果需要隐藏控制台,把Init函数中的第一行注释掉的代码打开即可。

代码

BirdGame.h:

#pragma once
#include<vector>

struct Bird {
    int idx;                //对应图标下标
    int x, y;                //坐标
    int hSpeed = 0;        //纵向速度
    int lastRefreshTime;
    const double JUMP_ADD_SPEED;
    static const double G;            //重力加速度
    
    Bird(int _idx, int _x, int _y, const double _JUMP_ADD_SPEED);
    Bird() = default;

    void Jump();        //跳跃
    void Print();        //打印
    void Refresh();    //刷新信息
    void SetLocation(int x, int y);        //设置坐标
};

struct WallBlock {
    int idx;
    int x, y;
};

struct Wall {
    static const int SPACE_NUM;            //中间空几格
    std::vector<WallBlock> wallBlocks;
    int lastRefreshTime;
    int upRand;
    Wall(int beginIdx);
    bool Refresh();            //返回是否走到终点
    void Print();
    bool IsOverlap(int x, int y);            //返回传入坐标是否与墙发生overlap
};

void StartGame_Bird();
//暂停游戏,按空格继续
void StopGame();
//初始化游戏
void Init();

BirdGame.cpp:

#include "BirdGame.h"
#include <Windows.h>
#include <ShlObj.h>
#include <ctime>
#include<iostream>

using namespace std;


HWND desktop;     //桌面句柄
int iconCount;         //图标个数
int screenX;            //获取屏幕的分辨率(宽)
int screenY;            //获取屏幕的分辨率(高)
const int ICON_SIZE{ 80 };        //图标间隔
const double MAP_SPEED{700};

void Init() {
    //属性初始化
    //ShowWindow(GetConsoleWindow(), SW_HIDE);                //隐藏控制台
    srand(unsigned int(time(NULL)));
    HWND grandpa = FindWindowA("Progman", "Program Manager");
    HWND father = FindWindowExA(grandpa, NULL, "SHELLDLL_DefView", NULL);
    desktop = FindWindowExA(father, 0, "SysListView32", "FolderView");
    iconCount = SendMessage(desktop, LVM_GETITEMCOUNT, 0, 0);       //获取句柄中控件的个数
    screenX = GetSystemMetrics(SM_CXSCREEN);   //获取屏幕的分辨率(宽)
    screenY = GetSystemMetrics(SM_CYSCREEN);   //获取屏幕的分辨率(高)

    //隐藏图标
    for (int i = 0; i < iconCount; i++)
        SendMessageA(desktop, LVM_SETITEMPOSITION, i, (screenY << 16) + screenX);
}


Bird::Bird(int _idx, int _x, int _y, const double _JUMP_ADD_SPEED = 700)
    : idx{ _idx }, x{ _x }, y{ _y }, lastRefreshTime{ clock() }, JUMP_ADD_SPEED(_JUMP_ADD_SPEED) {}

const double Bird::G{ 2400 };
void Bird::Jump() {
    hSpeed = JUMP_ADD_SPEED;
}

void Bird::Print() {
    //cout << this->x << " " << this->y << endl;
    SendMessageA(desktop, LVM_SETITEMPOSITION, this->idx, (screenY - this->y << 16) + this->x);
}

void Bird::SetLocation(int x, int y) {
    this->x = x; this->y = y;
}

void Bird::Refresh() {
    double curTime = clock(); 
    double rateTime = (curTime - lastRefreshTime) / CLOCKS_PER_SEC;
    lastRefreshTime = curTime;
    y += hSpeed * rateTime - 0.5 * G * rateTime * rateTime;
    hSpeed -= G * rateTime;
    Print();
}

const int Wall::SPACE_NUM{ 5 };
Wall::Wall(int beginIdx) : lastRefreshTime{clock()} {
    int wallNum = (screenY + ICON_SIZE) / ICON_SIZE - SPACE_NUM;
    cout << "wallNum = " << wallNum << endl;
    upRand = rand() % (wallNum) + 1;        //1~wallNum-1
    for (int i = 0; i < wallNum; i++) {
        wallBlocks.push_back({ beginIdx + i, (screenX + ICON_SIZE) - (screenX + ICON_SIZE) % ICON_SIZE, i * ICON_SIZE + (i < upRand ? 0 : SPACE_NUM * ICON_SIZE) });
    }
}

//返回是否已经走到终点
bool Wall::Refresh() {
    double curTime = clock();
    double rateTime = (curTime - lastRefreshTime) / CLOCKS_PER_SEC;
    lastRefreshTime = curTime;
    if (wallBlocks[0].x - rateTime * MAP_SPEED < 0) {
        return true;
    }
    for (WallBlock &wallBlock : wallBlocks) {
        wallBlock.x -= rateTime * MAP_SPEED;
    }
    Print();

    return false;
}

void Wall::Print() {
    for (int i = 0; i < wallBlocks.size(); i++) {
        SendMessageA(desktop, LVM_SETITEMPOSITION, wallBlocks[i].idx, (wallBlocks[i].y << 16) + wallBlocks[i].x);
    }
}

bool Wall::IsOverlap(int x, int y) {
    if (y < -ICON_SIZE or y > screenY + ICON_SIZE)                //越界
        return false;
    if (x > wallBlocks[0].x + ICON_SIZE or x + ICON_SIZE < wallBlocks[0].x)            //x方向没碰到墙
        return false;
    if (y < upRand * ICON_SIZE or y + ICON_SIZE >(upRand + SPACE_NUM) * ICON_SIZE)
        return true;
    return false;
}


void StartGame_Bird() {
    int startTime = clock();
    Init();

    Bird bird(0, screenX / 3, screenY / 2);
    bird.Print();
    Wall *wall = new Wall(1);
    StopGame();
    bird.lastRefreshTime = clock();
    wall->lastRefreshTime = clock();
    while (true) {
        if (GetAsyncKeyState(VK_ESCAPE) or (clock() - startTime) / CLOCKS_PER_SEC > 2000)
            exit(0);
        bird.Refresh();
        if (wall->IsOverlap(bird.x, screenY - bird.y)) {
            MessageBox(desktop, TEXT("你死了,游戏结束!"), TEXT(""), MB_OK | MB_ICONEXCLAMATION);
            exit(0);
        }
        if (GetAsyncKeyState(VK_SPACE))
            bird.Jump();
        bool isOver = wall->Refresh();
        if (isOver) {
            delete wall;
            wall = new Wall(1);
        }
        Sleep(20);
    }
}

//暂停游戏,按空格继续
void StopGame() {
    while (true) {
        if (GetAsyncKeyState(VK_SPACE))break;
        Sleep(300);
    }
}

int main() {
    StartGame_Bird();

    return 0;
}
最后修改:2022 年 08 月 31 日
如果觉得我的文章对你有用,请随意赞赏