说是FPS其实就是一个走迷宫😓。
跟着b站上的视频学的打的,加了点注释
#include<iostream>
#include<Windows.h>
#include<vector>
#include <algorithm>
/*
关于chrono
是c++的一个时间库
https://blog.csdn.net/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.csdn.net/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.csdn.net/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.csdn.net/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;
}