C++第一人称FPS1.0

本文介绍了使用C++编写一个简单的2D第一人称视角游戏,玩家在迷宫中移动并避开障碍物,利用chrono库处理时间间隔,控制台输出显示游戏状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说是FPS其实就是一个走迷宫😓。

跟着b站上的视频学的打的,加了点注释

#include<iostream>
#include<Windows.h>
#include<vector>
#include <algorithm>
/*
关于chrono
是c++的一个时间库
https://blog.youkuaiyun.com/albertsh/article/details/105468687
*/
#include<chrono>
using namespace std;
#define pi 3.1415926535

//屏幕
int nScreenWidth = 120;//屏宽120
int nScreenHeight = 40;//屏高40
wchar_t* screen = nullptr;
//屏幕初始化函数
void init_Screen() {
	screen = new wchar_t[nScreenWidth * nScreenHeight];
}


//玩家信息
float fPlayerX = 8.0f;//玩家坐标
float fPlayerY = 8.0f;
float fPlayerA = 0.0f;//?
float fFov = pi / 4.0;//视野范围
float fDepth = 16;//视野深度



//场地
int nMapWidth = 16;
int nMapHeight = 16;
wstring map;
//场地初始化函数
void init_map(){
	map += L"################";
	map += L"#              #";
	map += L"#              #";
	map += L"#              #";
	map += L"#      #       #";
	map += L"#      #       #";
	map += L"#              #";
	map += L"#         ######";
	map += L"#              #";
	map += L"#              #";
	map += L"#              #";
	map += L"#              #";
	map += L"#              #";
	map += L"#              #";
	map += L"#              #";
	map += L"################";
}


//哈希函数的到离玩家不同距离的墙壁的符号
void GetWallHash(short &nshade ,float fDistanceToWall) {
	//扩展ASCII码字符对应UNICODE编码
	//https://blog.youkuaiyun.com/weixin_40013868/article/details/78072168
	if (fDistanceToWall <= fDepth / 4.0f)     nshade = 0x2588;
	else if (fDistanceToWall < fDepth / 3.0f) nshade = 0x2593;
	else if (fDistanceToWall < fDepth / 2.0f) nshade = 0x2592;
	else if (fDistanceToWall < fDepth)        nshade = 0x2591;
	else                                      nshade = ' ';//太远看不见
}
//哈希函数的到离玩家不同距离的地面的阴影
void GetFloorHash(short& nshade, float fDistanceToFlayer) {
	if (fDistanceToFlayer < 0.25)      nshade = '*';
	else if (fDistanceToFlayer < 0.5) nshade = '+';
	else if (fDistanceToFlayer < 0.75) nshade = '-';
	else if (fDistanceToFlayer < 0.9) nshade = '.';
	else                               nshade = ' ';
}
//判断玩家向此方向运动是否会撞墙
bool HitTheWall(int x,float felapsedTime) {
	float fX = fPlayerX; 
	float fY = fPlayerY; 
	bool WillItCrash = FALSE;
	switch (x)
	{
	case 1:
		fX+=sinf(fPlayerA) * 5.0f * felapsedTime;
		fY+=cosf(fPlayerA) * 5.0f * felapsedTime;
		if (map[(int)fY * nMapWidth + (int)fX] == '#')
			WillItCrash = TRUE;
		break;
	case 2:
		fX -= sinf(fPlayerA) * 5.0f * felapsedTime;
		fY -= cosf(fPlayerA) * 5.0f * felapsedTime;
		if (map[(int)fY * nMapWidth + (int)fX] == '#')
			WillItCrash = TRUE;
		break;
	default:
		break;
	}
	return WillItCrash;
}
int main() {
	//初始化屏幕
	init_Screen();
	//获取控制台地址
	HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
	SetConsoleActiveScreenBuffer(hConsole);
	DWORD dwBytesWritten = 0;



	//初始化地图
	init_map();
	//得到时间戳  何为时间戳? 
	// 时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数
	auto tp1 = chrono::system_clock::now();
	auto tp2 = chrono::system_clock::now();
	//game loop 
	while (1) {
		tp2 = chrono::system_clock::now();
		//duration类的对象表示时间间隔,单位秒
		chrono::duration<float>elapsedTime = tp2 - tp1;
		tp1 = tp2;
		//得到这次循环和上次循环的时间差
		float felapsedTime = elapsedTime.count();
		//游戏控件
		 /*
		关于GetAsyncKeyState
			是判断一个按键是否被按下
			https ://blog.youkuaiyun.com/FlowShell/article/details/5422395
		*/
		//旋转
		if (GetAsyncKeyState((unsigned short)'A') & 0x8000)
			fPlayerA -= (0.8f)* felapsedTime;
		if (GetAsyncKeyState((unsigned short)'D') & 0x8000)
			fPlayerA += (0.8f)* felapsedTime;
		//前进后退
		if (GetAsyncKeyState((unsigned short)'W') & 0x8000) {
			//撞墙检测
			if (!HitTheWall(1, felapsedTime)) {
				fPlayerX += sinf(fPlayerA) * 5.0f * felapsedTime;
				fPlayerY += cosf(fPlayerA) * 5.0f * felapsedTime;
			}
		}
		if (GetAsyncKeyState((unsigned short)'S') & 0x8000) {
			if (!HitTheWall(2, felapsedTime)) {
				fPlayerX -= sinf(fPlayerA) * 5.0f * felapsedTime;
				fPlayerY -= cosf(fPlayerA) * 5.0f * felapsedTime;
			}
		}

		for (int x = 0; x < nScreenWidth; x++) {
			
			float fRayAngle = (fPlayerA - fFov / 2.0f) + ((float)x / (float)nScreenWidth * fFov);
			//到墙的距离
			float fDistanceToWall = 0;
			//撞墙标志
			bool bHitWall = FALSE;
			//边界标志
			bool bBoundary = FALSE;
			//视野的单位向量,表示玩家看的方向
			float fEyeX = sinf(fRayAngle);
			float fEyeY = cosf(fRayAngle);
			
			while (!bHitWall && fDistanceToWall < fDepth) {
				fDistanceToWall += 0.1f;
				//看到的坐标                    
				int nTestX = (int)(fPlayerX + fEyeX * fDistanceToWall);
				int nTestY = (int)(fPlayerY + fEyeY * fDistanceToWall);
				//出界?
				if (nTestX < 0 || nTestX >= nMapWidth || nTestY < 0 || nTestY >= nMapHeight){
					bHitWall = TRUE;
					fDistanceToWall = fDepth;
				}
				else{
					//撞墙?
					//光线是入站的,测试以查看光线单元是否为墙块
					if (map[nTestY * nMapWidth + nTestX] == '#'){
						bHitWall = TRUE;
						//对墙壁增加边界
						vector<pair<float, float>>p;
						for(int tx=0;tx<2;tx++)
							for (int ty = 0; ty < 2; ty++){
								//墙角指向玩家的向量
								float vy = (float)nTestY + ty - fPlayerY;
								float vx = (float)nTestX + tx - fPlayerX;
								//墙角到玩家的距离
								float d = sqrt(vx * vx + vy * vy);
								float dot = (fEyeX * vx / d) + (fEyeY * vy / d);
								p.push_back({ d,dot });
							}
						//lambda表达式实现vector中pair的排序方法
						//实现墙角到玩家按距离排序
						sort(p.begin(), p.end(),
						[](const pair<int, int>& a, const pair<int, int>& b) { return a.first < b.first; });

						float fBound = 0.01;
						//三维层面只能看到立方体的三条边
						if (acos(p.at(0).second) < fBound)bBoundary = TRUE;
						if (acos(p.at(1).second) < fBound)bBoundary = TRUE;
						//if (acos(p.at(2).second) < fBound)bBoundary = TRUE;
					}
				}
				//天花板
				int nCeiling = (float)(nScreenHeight / 2.0) - nScreenHeight / ((float)fDistanceToWall);
				//地板
				int nFloor = nScreenHeight - nCeiling;

				short Wall_hash = ' '; 
				GetWallHash(Wall_hash,fDistanceToWall);
				if (bBoundary) Wall_hash = ' ';//边界
				for (int y = 0; y < nScreenHeight; y++) {
					if (y < nCeiling) {
						screen[y * nScreenWidth + x] = ' ';
					}
					else if (y > nCeiling && y <= nFloor) {
						screen[y * nScreenWidth + x] = Wall_hash;
					}
					else {
						short Floor_hash = ' ';
						float b = 1.0f - (((float)y - nScreenHeight / 2.0f) / ((float)nScreenHeight / 2.0f));
						GetFloorHash(Floor_hash, b);
						screen[y * nScreenWidth + x] = Floor_hash;
					}
				}
			}
		}
		//显示数据
		/*
		关于sprintf、swprintf与wsprintf的用法与区别
		https://blog.youkuaiyun.com/Zz22333/article/details/89084562
		*/
		swprintf_s(screen, 40, L"X=%3.2f, Y=%3.2f, A=%3.2f, FPS=%3.2f", fPlayerX, fPlayerY, fPlayerA, 1.0f / felapsedTime);
		//显示地图
		for (int nx = 0; nx < nMapWidth; nx++)
			for (int ny = 0; ny < nMapHeight; ny++)
				screen[(ny + 1) * nScreenWidth + nx] = map[ny * nMapWidth + nx];
		//显示玩家位置
		screen[((int)fPlayerY + 1) * nScreenWidth + (int)fPlayerX] = '*';
		screen[nScreenWidth * nScreenHeight - 1] = '\0';
		//绘制场地
		WriteConsoleOutputCharacter(hConsole, screen, nScreenHeight * nScreenWidth, { 0 , 0 }, &dwBytesWritten);
	}
	return 0;
}


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值