好的!我们将继续改进这个光线投射程序,加入以下两个功能:
---
## ✅ 新增功能:
1. **按 `Esc` 键退出程序**:使用 `WM_KEYDOWN` 消息检测 `VK_ESCAPE`。
2. **取消鱼眼效果**:在光线投射中使用**垂直视角偏移**,并优化投影逻辑,使画面更真实。
---
## ✅ 主要改动说明:
- 在消息处理中加入对 `Esc` 的检测,退出主循环。
- 在 `castRays()` 中优化投影方式:
- 使用垂直视角(FOV)计算正确投影高度。
- 使用 `cos(rayAngle - playerDir)` 进行精确距离校正。
- 去除简单的线段绘制,改为矩形绘制,增强真实感。
---
## ✅ 改进后的完整代码:
```cpp
#include <graphics.h>
#include <conio.h>
#include <math.h>
#include <windows.h>
#include <stdio.h>
// 地图尺寸
const int MAP_WIDTH = 10;
const int MAP_HEIGHT = 10;
// 屏幕分辨率
const int SCR_WIDTH = 1024;
const int SCR_HEIGHT = 768;
// 光线数量
const int NUM_RAYS = SCR_WIDTH;
// 墙高缩放因子
const double WALL_HEIGHT = 1.0;
// 视角旋转速度和移动速度
const double ROT_SPEED = 0.05;
const double MOVE_SPEED = 0.05;
// 视角 FOV(水平视野角度)
const double FOV = M_PI / 3.0;
// 定义 M_PI
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// 地图定义(true 表示墙)
bool map[MAP_WIDTH][MAP_HEIGHT] = {
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,1,0,0,0,1},
{1,0,0,0,0,1,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
// 玩家位置
double playerX = 1.5;
double playerY = 1.5;
double playerDir = 0;
// 光线投射函数
void castRays() {
for (int x = 0; x < NUM_RAYS; x++) {
double rayAngle = (playerDir - FOV / 2 + (x * FOV / NUM_RAYS));
double distToWall = 0;
bool hitWall = false;
double rayX = cos(rayAngle);
double rayY = sin(rayAngle);
while (!hitWall && distToWall < 20) {
distToWall += 0.05;
int testX = (int)(playerX + rayX * distToWall);
int testY = (int)(playerY + rayY * distToWall);
if (testX < 0 || testX >= MAP_WIDTH || testY < 0 || testY >= MAP_HEIGHT) {
hitWall = true;
distToWall = 20;
} else if (map[testX][testY]) {
hitWall = true;
distToWall *= cos(rayAngle - playerDir); // 校正鱼眼效果
}
}
// 真实投影高度(垂直视角)
int wallHeight = (int)(WALL_HEIGHT / distToWall * SCR_HEIGHT);
// 计算顶部和底部位置(居中显示)
int startY = (SCR_HEIGHT - wallHeight) / 2;
int endY = (SCR_HEIGHT + wallHeight) / 2;
COLORREF color = RGB(255 - (int)(distToWall * 10), 255 - (int)(distToWall * 10), 255 - (int)(distToWall * 10));
setfillcolor(color);
solidrectangle(x, startY, x + 1, endY);
}
}
// 玩家移动函数
void movePlayer(bool keys[256]) {
double moveX = 0, moveY = 0;
if (keys['W']) {
moveX = cos(playerDir) * MOVE_SPEED;
moveY = sin(playerDir) * MOVE_SPEED;
}
if (keys['S']) {
moveX = -cos(playerDir) * MOVE_SPEED;
moveY = -sin(playerDir) * MOVE_SPEED;
}
if (keys['A']) {
moveX = cos(playerDir - M_PI / 2) * MOVE_SPEED;
moveY = sin(playerDir - M_PI / 2) * MOVE_SPEED;
}
if (keys['D']) {
moveX = cos(playerDir + M_PI / 2) * MOVE_SPEED;
moveY = sin(playerDir + M_PI / 2) * MOVE_SPEED;
}
int testX = (int)(playerX + moveX);
int testY = (int)(playerY + moveY);
if (!map[testX][testY]) {
playerX += moveX;
playerY += moveY;
}
}
int main() {
initgraph(SCR_WIDTH, SCR_HEIGHT); // 初始化窗口
POINT oldMousePos;
GetCursorPos(&oldMousePos);
bool keys[256] = { false };
bool running = true;
ExMessage msg;
while (running) {
// 处理消息队列
while (peekmessage(&msg)) {
if (msg.message == WM_KEYDOWN) {
keys[msg.vkcode] = true;
if (msg.vkcode == VK_ESCAPE) {
running = false; // 按下 Esc 退出
}
} else if (msg.message == WM_KEYUP) {
keys[msg.vkcode] = false;
}
}
// 鼠标控制视角
POINT currentMousePos;
GetCursorPos(¤tMousePos);
int deltaX = currentMousePos.x - oldMousePos.x;
playerDir += deltaX * ROT_SPEED / 10;
SetCursorPos(oldMousePos.x, oldMousePos.y); // 锁定鼠标位置
// 移动玩家
movePlayer(keys);
// 双缓冲绘图
BeginBatchDraw();
cleardevice();
castRays();
EndBatchDraw();
Sleep(10);
}
closegraph();
return 0;
}
```
---
## ✅ 代码解释:
### ✅ 1. `Esc` 退出功能:
- 使用 `WM_KEYDOWN` 检测按键消息。
- 如果 `msg.vkcode == VK_ESCAPE`,设置 `running = false` 退出主循环。
### ✅ 2. 取消鱼眼效果:
- 光线角度使用 `playerDir - FOV / 2 + (x * FOV / NUM_RAYS)`,实现完整 FOV。
- 使用 `distToWall *= cos(rayAngle - playerDir)` 对距离进行精确校正。
- 使用 `solidrectangle()` 绘制墙块,增强真实感。
---
## ✅ 编译运行说明:
- 使用 Dev-C++ 创建一个 C++ 项目。
- 确保已安装 EasyX 库(推荐使用 20201210 或 20220900 版本)。
- 将上述代码粘贴进主源文件。
- 编译并运行程序。
- 按 `Esc` 键可退出。
---
## ✅ 功能总结:
| 功能 | 实现方式 |
|------------------|----------|
| 按 Esc 退出 | 使用 `WM_KEYDOWN` 检测 `VK_ESCAPE` |
| 消除鱼眼效果 | 使用 `cos(rayAngle - playerDir)` 校正距离 |
| 更真实的投影 | 使用 `solidrectangle()` 绘制墙块 |
| 双缓冲绘图 | 使用 `BeginBatchDraw()` 和 `EndBatchDraw()` |
---
##