本教程,我创建了增加光线深度的代码(我知道这是低效且不准确的),直到它撞到墙上。然后我渲染墙,其大小取决于玩家与光线击中位置之间的距离。
用户位于横 3 格、下 4 格的位置,面朝北 30°。预期的结果是在黑色背景上呈现白色倾斜的墙壁。以下是该程序的结果: 我有一个名为Canvas
的类,我用它来设置纹理上的像素以使用
SpriteBatch
进行渲染。
Canvas.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Raycaster
{
public class Canvas
{
Texture2D texture;
Color[] pixels;
bool changed = false;
public Canvas(GraphicsDevice graphicsDevice, int width, int height)
{
texture = new Texture2D(graphicsDevice, width, height);
pixels = new Color[width * height];
}
public void Draw(SpriteBatch _spriteBatch)
{
if (changed)
{
changed = false;
texture.SetData(pixels);
}
_spriteBatch.Draw(texture, new Vector2(0, 0), Color.White);
}
public void SetPixel(int x, int y, Color color)
{
int position = x + (y * texture.Width);
if (position >= 0 && position < pixels.Length)
{
pixels[position] = color;
changed = true;
}
}
public void Clear()
{
pixels = new Color[texture.Width * texture.Height];
}
}
}
以下是我在Game1.cs
const int Width = 1024;
const int Height = 700;
int[,] map = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 1, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 1, 0, 1 },
{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 1 },
{ 1, 0, 1, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 1, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
Vector2 playerPosition = new Vector2(192, 256);
double playerRotation = 30;
const double playerFOV = 60;
const double deltaAngle = playerFOV / Width;
const int unitsPerSquare = 64;
Canvas canvas;
这是我在Draw
方法中使用的代码,在
Game1
中:
GraphicsDevice.Clear(Color.Black);
canvas.Clear();
double angle = playerRotation - (playerFOV / 2);
for (int column = 0; column < Width; column++)
{
int depth = 0;
bool wallFound = false;
Vector2 wallHit = new Vector2(0, 0);
while (!wallFound)
{
if (depth > 1024) break;
double x = playerPosition.X + depth * Math.Cos(angle);
double y = playerPosition.Y + depth * Math.Sin(angle);
if (x < 0 || y < 0 || x > Width || y > Height) break;
for (int i = 0; i < map.GetLength(0); i++)
{
if (wallFound) break;
for (int j = 0; j < map.GetLength(1); j++)
{
if (wallFound) break;
int value = map[i, j];
if (value == 0) continue;
else if (value == 1)
{
Vector2 currentPosition = new Vector2((float)x, (float)y);
Rectangle cubeRectangle = new Rectangle(i * unitsPerSquare, j * unitsPerSquare, unitsPerSquare, unitsPerSquare);
if (cubeRectangle.Contains(currentPosition))
{
wallFound = true;
wallHit = currentPosition;
}
}
}
}
depth++;
}
if (wallFound)
{
float distance = Vector2.Distance(playerPosition, wallHit);
int wallRenderHeight = (int)Math.Round(Width * (1 / (distance / unitsPerSquare)));
int wallRenderY = (Height / 2) - (wallRenderHeight / 2);
for (int i = 0; i < wallRenderHeight; i++) canvas.SetPixel(column, i + wallRenderY, Color.White);
}
angle += deltaAngle;
}
_spriteBatch.Begin();
canvas.Draw(_spriteBatch);
_spriteBatch.End();
base.Draw(gameTime);
Math.Sin
和
Math.Cos
采用弧度而不是角度。在使用三角函数之前,我通过将角度(以度为单位)转换为弧度来修复该程序:
double angleRadians = Math.PI * angle / 180;
double x = playerPosition.X + depth * Math.Cos(angleRadians);
double y = playerPosition.Y + depth * Math.Sin(angleRadians);