重整控制台RPG——双缓冲无屏闪以及第一个无中生有的地图类,玩家类_哔哩哔哩_bilibili
是鄙人解说鄙人的代码
#include #include #include #define KEY_DOWN(vKey) ((GetAsyncKeyState(vKey) & 0x8000) ? 1:0) // 判断是否按下按键,按下之后,最高位变成 1,所以需要位运算去除其他位,只检测最高位 #define KEY_DOWN_FOREGROUND(hWnd, vk) (KEY_DOWN(vk) && GetForegroundWindow() == hWnd) // 前景窗口判断 #pragma warning(disable : 4996) // 便于移植到非微软的编译器里 using namespace std; // 加入的游戏背景数据,通常不修改,放到 Bkmap 进行修改 class Gamemap { public: Gamemap(int height,int wide) { this->wide=wide; this->height=height; this->gamemap=new int*[this->height]; for(int i=0; igamemap[i]=new int[this->wide]; for(int j=0; jwide; j++) { this->gamemap[i][j]=0; } } } ~Gamemap(); public: int** gamemap; // 游戏地图数组 int wide; // 游戏长宽 int height; }; // 游戏背景操作网格数组,可以通过坐标直接改 class Bkmap { public: Bkmap(int height,int wide) { this->wide=wide; this->height=height; this->bkmap=new char*[this->height]; for(int i=0; ibkmap[i]=new char[this->wide]; for(int j=0; jbkmap[i][j]='\0'; } } } public: char** bkmap; // 游戏地图操作网格数组 int wide; // 操作网格长宽 int height; public: // 测试用,程序闪退,检测当时哪里数组溢出,哪里数据缺失 void adddata() { for(int i=0; iheight; i++) // 地图数据写入 { for(int j=0; jwide-1; j++) { this->bkmap[i][j]='*'; } this->bkmap[i][this->wide-1]='\0'; // 截至符号,防止后续粘贴多粘贴其他奇奇怪怪的旧数据 } } // 刷新缓冲区,清除之前的数据,防止混合新数据形成奇奇怪怪的bug void fresh(Gamemap* gamemap) { for(int i=0; iheight; i++) // 地图复印到操作网格区 { this->bkmap[i][0]='#'; // 最左侧是边界 for(int j=1; jwide; j++) { if(gamemap->gamemap[i][j]==0) this->bkmap[i][j]=' '; // 这里决定地图打印在屏幕的样子 } this->bkmap[i][gamemap->wide]='#'; // 设置截断点,防止残留数据 this->bkmap[i][gamemap->wide+1]='\0'; } for(int j=0; jwide; j++) this->bkmap[gamemap->height][j]='#'; // 底部也有边界线 this->bkmap[gamemap->height][gamemap->wide+1]='\0'; } }; // 字符串缓冲类 class Showmap { public: Showmap(int height,int wide) { this->showmap=new char[wide*height+1000]; strcpy(showmap,""); // showmap={}; // 不能这样写,否则报错,指针重新变空 } public: char* showmap; // 显示区缓冲 public: // 加入缓冲数据,用于一键打出,多个 cout printf 反而会打印慢,有一行行残影 void adddata(Bkmap* bkmap) { if(showmap==NULL) { cout 1) // 速度限制 flag_x = 1; else if (flag_x 1) flag_y = 1; else if (flag_y = limitx) // 角色位置限制 playerx = limitx - 1; else if (playerx = limity) playery = limity - 1; else if (playery bkmap[playery][playerx]=player; } // 检测攻击 void checkatk(HWND hwnd) { if (is_atk == 0 ) { if( KEY_DOWN_FOREGROUND(hwnd, 0x4A)) // j 键攻击 is_atk = 1; else if(KEY_DOWN_FOREGROUND(hwnd,0x31)) // 1 键攻击 is_atk=2; } } // 检测buff void checkbuff(HWND hwnd) { if (is_buff == 0 && KEY_DOWN_FOREGROUND(hwnd, 0x4B)) // k 键增加范围 buff { is_buff = 1; } } // 绘制地图 void drawmap(HWND hwnd,Gamemap* gamemap) { if (KEY_DOWN_FOREGROUND(hwnd, 0x30)) // 0 键绘制地图 gamemap->gamemap[playery][playerx] = 0; if (KEY_DOWN_FOREGROUND(hwnd, 0x36)) // 6 键绘制地图 gamemap->gamemap[playery][playerx] = 6; if (KEY_DOWN_FOREGROUND(hwnd, 0x37)) // 7 键绘制地图 gamemap->gamemap[playery][playerx] = 7; if (KEY_DOWN_FOREGROUND(hwnd, 0x38)) // 8 键绘制地图 gamemap->gamemap[playery][playerx] = 8; if (KEY_DOWN_FOREGROUND(hwnd, 0x39)) // 9 键绘制地图 gamemap->gamemap[playery][playerx] = 9; } }; // 攻击类,这里直线攻击做测试 class Aking { public: Aking(int height,int wide,int atk_time,int number) { this->height=height; this->wide=wide; this->number=number; this->atk_time=atk_time; atk_count=0; } public: int height; // 攻击范围长宽 int wide; int number; // 攻击序号,因为有多个攻击技能,所以编号,可以查找攻击 int atk_time; // 攻击时长 int atk_count; // 当前攻击持续时间 public: // 攻击中 void atking(Bkmap* bkmap,Player* player) { if (atk_count != atk_time) // 攻击 { atk_count++; for(int j=0; jbkmap[player->playery][player->playerx+1+j]='P'; } } else { atk_count=0; player->is_atk=0; // 玩家状态复位 } } }; // 攻击类v2继承之前的攻击类,这里做范围攻击做测试 class Akingv2:public Aking { public: Akingv2(int height,int wide,int atk_time,int number):Aking( height,wide,atk_time,number) { this->height=height; this->wide=wide; this->number=number; this->atk_time=atk_time; atk_count=0; } public: void atkingv2(Bkmap* bkmap,Player* player) { if (atk_count != atk_time) // 攻击 { atk_count++; for(int i=0; iplayery-5+i][player->playerx+1+j]='P'; } } else { atk_count=0; player->is_atk=0; // 玩家状态复位 } } }; int main() { Gamemap* gamemap = new Gamemap(20,50); Bkmap* bkmap=new Bkmap(20+3,50); Showmap* showmap= new Showmap(20,80); Aking* ak = new Aking(10,1,12,1); // 直线攻击,长 10 宽 1 持续时间 12 次循环,序号是1 Akingv2* akv2 = new Akingv2(10,1,12,2); Player* player = new Player(0,0,20,50); bkmap->fresh(gamemap); // bkmap->adddata(); // 测试数据 改bug 测试用 showmap->adddata(bkmap); // showmap->show(); HWND hwnd = GetForegroundWindow(); // 获取前端窗口句柄,由于程序刚运行时是在前端,所以这就是本程序的窗口句柄 //获取默认标准显示缓冲区句柄 HANDLE hOutput; COORD coord= {0,0}; hOutput=GetStdHandle(STD_OUTPUT_HANDLE); //创建新的缓冲区 HANDLE hOutBuf = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL ); //设置新的缓冲区为活动显示缓冲 SetConsoleActiveScreenBuffer(hOutBuf); //隐藏两个缓冲区的光标 CONSOLE_CURSOR_INFO cci; cci.bVisible=0; cci.dwSize=1; SetConsoleCursorInfo(hOutput, &cci); SetConsoleCursorInfo(hOutBuf, &cci); //双缓冲处理显示 DWORD bytes=0; chAR Data[4900]; while(1) { if (KEY_DOWN_FOREGROUND(hwnd, VK_ESCAPE)) { printf("游戏退出\n"); break; } player->checkmove(hwnd); // 移动按键检测 player->checkatk(hwnd); // 攻击按键检测 player->checkspeed(); // 速度检测 player->move(); // 玩家移动 player->checkboundary(); // 边界检测 bkmap->fresh(gamemap); // 清空操作网格区数据 player->putinmap(bkmap); // 玩家位置写入操作网格区 switch (player->is_atk) // 攻击选择方式 { case 1: ak->atking(bkmap,player); // 攻击写入操作网格区 break; case 2: akv2->atkingv2(bkmap,player); break; } cout