控制台RPG 游戏实现双缓冲无屏闪,玩家类,地图类

news2024/11/20 15:20:15

 

重整控制台RPG——双缓冲无屏闪以及第一个无中生有的地图类,玩家类_哔哩哔哩_bilibili

是鄙人解说鄙人的代码

#include <iostream>
#include <string.h>
#include <windows.h>

#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; i<height; i++)
			{
				this->gamemap[i]=new int[this->wide];
				for(int j=0; j<this->wide; 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; i<height; i++)
			{
				this->bkmap[i]=new char[this->wide];
				for(int j=0; j<wide; j++)
				{
					this->bkmap[i][j]='\0';
				}
			}
		}
	public:
		char** bkmap;														// 游戏地图操作网格数组
		int wide;															// 操作网格长宽
		int height;
	public:
		// 测试用,程序闪退,检测当时哪里数组溢出,哪里数据缺失
		void adddata()
		{
			for(int i=0; i<this->height; i++)								// 地图数据写入
			{
				for(int j=0; j<this->wide-1; j++)
				{
					this->bkmap[i][j]='*';
				}
				this->bkmap[i][this->wide-1]='\0';							// 截至符号,防止后续粘贴多粘贴其他奇奇怪怪的旧数据
			}
		}
		// 刷新缓冲区,清除之前的数据,防止混合新数据形成奇奇怪怪的bug
		void fresh(Gamemap* gamemap)
		{
			for(int i=0; i<gamemap->height; i++)								// 地图复印到操作网格区
			{
				this->bkmap[i][0]='#';											// 最左侧是边界
				for(int j=1; j<gamemap->wide; 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; j<gamemap->wide; 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<<"NULL";
				cout<<"函数因空指针结束"<<endl;
				return;
			}
			strcpy(showmap,"");										// 清空旧数据 ,实际上是把标志位放到开头,保证从头粘贴数据
			for(int i=0; i<bkmap->height; i++)						// 选区加入到打印缓冲区
			{
				strcat(showmap,bkmap->bkmap[i]);
				strcat(this->showmap,"\n");
			}
		}
		// 一键显示到屏幕
		void show()
		{
			cout<<this->showmap;
		}
};



// 玩家类,移动,攻击,地图修改
class Player
{
	public:
		Player(int x,int y,int limity,int limitx)
		{
			this->playerx=x;
			this->playery=y;
			is_atk=0;
			is_buff=0;
			flag_x=0;
			flag_y=0;
			this->limitx=limitx;
			this->limity=limity;
		}
		~Player();
	public:
		char player='1';
		int playerx;
		int playery;
		int flag_x;
		int flag_y;
		int limitx;
		int limity;
		int is_atk;
		int is_buff;
	public:
		//玩家移动检测
		void checkmove(HWND hwnd)
		{
			flag_x=0;
			flag_y=0;
			if (KEY_DOWN_FOREGROUND(hwnd, 0x41))			// A
			{
				flag_x -= 1;
			}
			if (KEY_DOWN_FOREGROUND(hwnd, 0x57))			// W
			{
				flag_y -= 1;
			}
			if (KEY_DOWN_FOREGROUND(hwnd, 0x44))			// D
			{
				flag_x += 1;
			}
			if (KEY_DOWN_FOREGROUND(hwnd, 0x53))			// S
			{
				flag_y += 1;
			}
		}
		// 打包速度检测
		void checkspeed()
		{
			if (flag_x > 1)														// 速度限制
				flag_x = 1;
			else if (flag_x < -1)
				flag_x = -1;
			if (flag_y > 1)
				flag_y = 1;
			else if (flag_y < -1)
				flag_y = -1;
		}
		// 改变玩家位置
		void move()
		{
			if (flag_x)															// 位移改变
				playerx += flag_x;
			if (flag_y)
				playery += flag_y;
		}
//		边界检测
		void checkboundary()
		{
			if (playerx >= limitx)												// 角色位置限制
				playerx = limitx - 1;
			else if (playerx < 0)												// 左侧边界,可以修改成 1 这样就不会进入栅栏,和'#’重叠了
				playerx = 0;
			if (playery >= limity)
				playery = limity - 1;
			else if (playery < 0)
				playery = 0;
		}
		void putinmap(Bkmap* bkmap)
		{
			bkmap->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; j<10; j++)
				{
					bkmap->bkmap[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; i<10; i++)
					for(int j=0; j<10; j++)
					{
						bkmap->bkmap[player->playery-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<<"冲冲冲"<<endl;
		showmap->adddata(bkmap);											// 操作网格区写入显示缓冲区
//		system("cls");														// 双缓冲时,去除清屏命令
		cout<<"玩家位置:"<<player->playerx<<","<<player->playery<<endl;
		showmap->show();
		cout<<endl;
		cout<<endl;
		cout<<endl;
		cout<<endl;															// 用于顶到顶部,把一些奇奇怪怪的数据顶出屏幕

		Sleep(50);
		ReadConsoleOutputCharacterA(hOutput, data, 4900, coord, &bytes);
		SetConsoleTextAttribute(hOutBuf, FOREGROUND_INTENSITY | FOREGROUND_RED);

		WriteConsoleOutputCharacterA(hOutBuf, data, 4900, coord, &bytes);
		cout<<"测试数据位置。";
	}
	return 0;

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1570240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Prometheus+grafana环境搭建MongoDB(docker+二进制两种方式安装)(五)

由于所有组件写一篇幅过长&#xff0c;所以每个组件分一篇方便查看&#xff0c;前四篇mongodb的exporter坑也挺多总结一下各种安装方式&#xff0c;方便后续考古。 Prometheusgrafana环境搭建方法及流程两种方式(docker和源码包)(一)-CSDN博客 Prometheusgrafana环境搭建rabb…

先进电气技术 —— (控制理论)何为稳定性?

一、系统稳定性 在控制理论中&#xff0c;系统稳定性是一个非常关键的概念&#xff0c;它主要涉及系统对外界扰动或内部变动的响应行为。以下是与系统稳定性相关的一些核心名词及其解释&#xff1a; 基本概念 稳定性&#xff08;Stability&#xff09; 系统稳定性是指当系统受…

【Linux】make 工具和 Makefile 文件的引入

前面提到了 gcc 编译器&#xff0c;那么使用 gcc 编译器肯定就会接触到 Makefile 。当源码文件比较多的时候就不适合通过直接输入 gcc 命令来编译&#xff0c;这时候就需要一个自动化的编译工具 make 。 举例&#xff1a;通过键盘输入两个整形数字&#xff0c;然后计算他们的和…

matlab中角度-弧度转化

在 MATLAB 中进行角度和弧度之间的转换可以使用内置的函数&#xff1a; 1. 将角度转换为弧度&#xff1a; matlab rad deg * pi / 180; 这里 deg 是你想要转换的角度值&#xff0c;pi 是 MATLAB 内置的圆周率常量。 2. 将弧度转换为角度&#xff1a; matlab…

【linux】重定向|缓冲区

read函数 将文件标识符为fd的文件内容读到buf中去&#xff0c;读count个&#xff0c;read的返回值是实际读到的个数&#xff0c;有可能文件中没有count个&#xff0c;但是read返回的还是实际读到的个数 stat函数 调用这个函数可以查到对应文件的属性&#xff0c;比方说文件大小…

BUUCTF-MD5

BUUCTF-MD5 1 注意&#xff1a;得到的 flag 请包上 flag{} 提交 27120bd8-e273-4528-97a9-28dcebe236de.zip flag{admin1} e00cf25ad42683b3df678c61f42c6bdahttps://www.cmd5.com/

3.6k star, 免费开源跨平台的数据库管理工具 dbgate

3.6k star, 免费开源跨平台的数据库管理工具 dbgate 分类 开源分享 项目名: dbgate -- 免费开源跨平台的数据库管理工具 Github 开源地址&#xff1a; GitHub - dbgate/dbgate: Database manager for MySQL, PostgreSQL, SQL Server, MongoDB, SQLite and others. Runs under…

《UE5_C++多人TPS完整教程》学习笔记30 ——《P31 摄像机和弹簧臂(Camera And Spring Arm)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P31 摄像机和弹簧臂&#xff08;Camera And Spring Arm&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&#xff08;…

在云端遇见雨云:一位服务器寻觅者的指南

引言&#xff1a;寻觅一座云端归宿 当我踏入数字世界的边缘&#xff0c;带着对网络的探索与期待&#xff0c;我迫切需要一座安全可靠的数字栖息地。云计算技术正如一场魔法般的变革&#xff0c;而在这片广袤的云端中&#xff0c;雨云就像是一位友善的向导&#xff0c;引领我穿越…

【AAOS车载系统+AOSP14系统攻城狮入门实战课】:正式上线了(二百零三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

代码随想录算法训练营第三十天| 332.重新安排行程, 51. N皇后, 37. 解数独,总结

题目与题解 参考资料&#xff1a;回溯总结 332.重新安排行程 题目链接&#xff1a;332.重新安排行程 代码随想录题解&#xff1a;332.重新安排行程 视频讲解&#xff1a;带你学透回溯算法&#xff08;理论篇&#xff09;| 回溯法精讲&#xff01;_哔哩哔哩_bilibili 解题思路&a…

c语言--动态内存管理(malloc、realloc、free、calloc)

目录 一、为什么要有动态内存分配二、 malloc和free2.1 malloc 三、 calloc和realloc3.1calloc3.2 realloc 四、常见的动态内存的错误4.1 对NULL指针的解引用操作4.2 对动态开辟空间的越界访问4.3 对非动态开辟内存使用free释放4.4 使用free释放⼀块动态开辟内存的一部分4.5对同…

Midjourney艺术家分享|By Moebius

Moebius&#xff0c;本名让吉拉德&#xff08;Jean Giraud&#xff09;&#xff0c;是一位极具影响力的法国漫画家和插画师&#xff0c;以其独特的科幻和幻想风格而闻名于世。他的艺术作品不仅在漫画领域内受到高度评价&#xff0c;也为电影、时尚和广告等多个领域提供了灵感。…

逆向案例十三(1)——贝壳网登录密码逆向

链接地址&#xff1a;合肥房产网_合肥二手房|租房|新房|房地产信息网【合肥贝壳找房】 点击登录&#xff0c;打开开发者工具&#xff0c;点击账号密码登录&#xff0c;输入错误密码和账号 。找到登录的数据包。打开发现有许多参数加密。数据包是authenticate 再登陆一次看哪些…

分类预测 | Matlab实现CPO-LSSVM冠豪猪算法优化最小支持向量机数据分类预测

分类预测 | Matlab实现CPO-LSSVM冠豪猪算法优化最小支持向量机数据分类预测 目录 分类预测 | Matlab实现CPO-LSSVM冠豪猪算法优化最小支持向量机数据分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现CPO-LSSVM冠豪猪算法优化最小支持向量机数据分类预…

机器学习模型:决策树笔记

第一章&#xff1a;决策树原理 1-决策树算法概述_哔哩哔哩_bilibili 根节点的选择应该用哪个特征&#xff1f;接下来选什么&#xff1f;如何切分&#xff1f; 决策树判断顺序比较重要。可以使用信息增益、信息增益率、 在划分数据集前后信息发生的变化称为信息增益&#xff0c…

MySQL故障排查与优化

一、MySQL故障排查 1.1 故障现象与解决方法 1.1.1 故障1 1.1.2 故障2 1.1.3 故障3 1.1.4 故障4 1.1.5 故障5 1.1.6 故障6 1.1.7 故障7​ 1.1.8 故障8 1.1.9 MySQL 主从故障排查 二、MySQL优化 2.1 硬件方面 2.2 查询优化 一、MySQL故障排查 1.1 故障现象与解决方…

Windows Docker 部署 Firefly III 开源记账软件

一、简介 Firefly III是一款开源的记账软件&#xff0c;支持全球多种语言。它可以帮助用户追踪和管理个人账目、预算和账单&#xff0c;减少支出&#xff0c;节省更多。该软件支持多种货币、银行账户和投资账户&#xff0c;并提供了丰富的报表功能&#xff0c;帮助用户更好地了…

Netty客户端发送数据给服务器的两个通道(1)

EventLoopGroup group new NioEventLoopGroup();// 设置的连接group。 Bootstrap bootstrap new Bootstrap().group(group).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) // 超时时间。 .channel(NioSocketChannel.class).handler(new ChannelInitializer() { Ov…

JVM专题——类文件加载

本文部分内容节选自Java Guide和《深入理解Java虚拟机》, Java Guide地址: https://javaguide.cn/java/jvm/class-loading-process.html &#x1f680; 基础&#xff08;上&#xff09; → &#x1f680; 基础&#xff08;中&#xff09; → &#x1f680;基础&#xff08;下&a…