You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ray-Casting算法实现求助:射线对齐异常与程序崩溃问题

Ray-Casting算法实现问题求助

我正在练习实现Ray-Casting算法,目前遇到两个问题:

  • 投射的射线与玩家视角不对齐,且无法正确表现射线特性(例如碰到墙时不会变短);
  • 程序偶尔崩溃,报错信息为Error -1073741819。

以下是我用SDL库和C++编写的代码:

#include <iostream>
#include <math.h>
#include <SDL2/SDL.h>

#define PI 3.1415926535

using namespace std;

SDL_Event event;
SDL_Window* window;
SDL_Renderer* renderer;
SDL_Surface* surface;
float pdX, pdY, pA;
bool RunState;
int vel = 10;
int mapX = 8, mapY = 9, mapS = 64;
SDL_Rect tileRect = {0, 0, 64, 64};
int map[] = 
{
    1,1,1,1,1,1,1,1,
    1,0,0,0,1,0,0,1,
    1,0,1,1,0,0,0,1,
    1,0,1,0,0,0,0,1,
    1,0,0,0,0,1,0,1,
    1,0,0,0,1,1,0,1,
    1,0,0,0,0,0,0,1,
    1,1,1,1,1,1,1,1,
};

class Player{
    public:
        SDL_Rect dest = {100, 100, 10, 10};
};

Player player;

void DrawRay3d()
{
    int r, mx, my, mp, dof;
    float rx, ry, ra, xo, yo;
    float pX = player.dest.x + player.dest.w/2;
    float pY = player.dest.y + player.dest.h/2;
    // 64条射线,覆盖60度视野(从视角左30度到右30度)
    for(r = 0; r < 64; r++)
    {
        ra = pA - PI/6 + (PI/3)*r/64; // 计算当前射线角度
        dof = 0;
        float aTan = -1/tan(ra);
        if(ra > PI)
        {
            ry = (((int)pY/64)*64) - 0.0001;
            rx = (pY - ry)*aTan + pX;
            yo = -64;
            xo = -yo*aTan;
        }
        if(ra < PI)
        {
            ry = (((int)pY/64)*64) + 64;
            rx = (pY - ry)*aTan + pX;
            yo = 64;
            xo = -yo*aTan;
        }
        if(ra == 0 || ra == PI)
        {
            rx = pX;
            ry = pY;
            dof = 8;
        }
        while(dof < 8)
        {
            mx = (int)(rx)/64;
            my = (int)(ry)/64;
            mp = my*mapX + mx;
            // 防止数组越界
            if(mp >= 0 && mp < mapX*mapY && map[mp] == 1)
            {
                dof = 8;
            }
            else{
                rx += xo;
                ry += yo;
                dof += 1;
            }
        }
        SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
        // 射线起点为玩家中心,终点为墙的交点
        SDL_RenderDrawLine(renderer, pX, pY, rx, ry);
    }
}

void Drawmap()
{
    for(int y = 0; y < mapY; y++)
    {
        for(int x = 0; x < mapX; x++)
        {
            if(map[y*mapX+x]==1) 
            {
                SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
                tileRect.x = x*64;
                tileRect.y = y*64;
                SDL_RenderFillRect(renderer, &tileRect);

                SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
                SDL_RenderDrawLine(renderer, x*64, y*64, x*64 + 64, y*64);
                SDL_RenderDrawLine(renderer, x*64+64, y*64, x*64 + 64, y*64 + 64);
                SDL_RenderDrawLine(renderer, x*64 + 64, y*64 + 64, x*64, y*64 + 64);
                SDL_RenderDrawLine(renderer, x*64, y*64 + 64, x*64, y*64);
            }
            else if(map[y*mapX+x]==0) 
            {
                SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
                tileRect.x = x*64;
                tileRect.y = y*64;
                SDL_RenderFillRect(renderer, &tileRect);

                SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
                SDL_RenderDrawLine(renderer, x*64, y*64, x*64 + 64, y*64);
                SDL_RenderDrawLine(renderer, x*64+64, y*64, x*64 + 64, y*64 + 64);
                SDL_RenderDrawLine(renderer, x*64 + 64, y*64 + 64, x*64, y*64 + 64);
                SDL_RenderDrawLine(renderer, x*64, y*64 + 64, x*64, y*64);
            }
        }
    }
}

int main(int argc, char* args[]){

    //Initialize window
    SDL_Init(SDL_INIT_EVERYTHING);
    window = SDL_CreateWindow("RayCastin'", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 512, SDL_WINDOW_ALLOW_HIGHDPI);
    if(window)
    {
        renderer = SDL_CreateRenderer(window, -1, 0);
        if(renderer)
        {
            RunState = true;
            surface = SDL_GetWindowSurface(window);
            // 初始化玩家方向:朝向上方(PI/2对应90度)
            pA = PI/2;
            pdX = cos(pA)*vel;
            pdY = sin(pA)*vel;
        }
    }

    //Looping
    while(RunState)
    {
        //Handel event
        SDL_PollEvent(&event);
        if(event.type == SDL_QUIT)
        {
            RunState = false;
        }
        if(event.type == SDL_KEYDOWN)
        {
            switch (event.key.keysym.sym){
                case SDLK_UP:
                    // SDL y轴向下,向上移动需减去pdY
                    player.dest.y -= pdY;
                    player.dest.x += pdX;
                    break;
                case SDLK_DOWN:
                    player.dest.y += pdY;
                    player.dest.x -= pdX;
                    break;
                case SDLK_LEFT:
                    pA -= 0.1;
                    if(pA < 0) pA += 2*PI;
                    pdX = cos(pA)*vel; 
                    pdY = sin(pA)*vel;
                    break;
                case SDLK_RIGHT:
                    pA += 0.1;
                    if(pA > 2*PI) pA -= 2*PI;
                    pdX = cos(pA)*vel; 
                    pdY = sin(pA)*vel;
                    break;
            }
        }

        //Render
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);
        
        Drawmap();
        DrawRay3d();

        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
        SDL_RenderFillRect(renderer, &player.dest);
        SDL_RenderDrawLine(renderer, player.dest.x+player.dest.w/2, player.dest.y+player.dest.h/2,  
                           player.dest.x+player.dest.w/2+pdX*5, player.dest.y+player.dest.h/2+pdY*5);
        SDL_RenderPresent(renderer);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    window = NULL;
    renderer = NULL;

    return 0;
}

问题解决说明

  1. 修复程序崩溃:将DrawRay3d中的无限循环改为固定数量的射线遍历(64条),同时计算每条射线对应的视野角度,避免程序卡死。
  2. 射线对齐与碰撞检测修复:
    • 改用玩家中心坐标pX/pY计算射线起点和网格交点,替换之前错误使用的速度分量pdX/pdY;
    • 修正射线绘制的起点为玩家中心,确保射线从玩家位置出发;
    • 添加方向变量初始化,避免初始方向随机;
    • 修正移动逻辑,适配SDL的y轴方向,确保移动方向与视角一致;
    • 碰撞检测时增加数组越界判断,防止访问非法内存。

内容的提问来源于stack exchange,提问作者Gia Khánh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 23:32:05