# Raycasting (wolfenstein like 2d game)

This topic is 2792 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi,

So yesterday I started working on a wolfenstein like 2d game, using raycasting to visualize the world. (by using permadi's tutorial)

But it's giving me ton of headaches, usually I can figure out or solve things myself, but this time, I'm gonna need help from more experienced people.

Small note, it's a bit messy, and just code to test it, so it's not gonna be used like this, just wanted to get the actually running before I start thinking about optimizing it, and writing it more cleanly.

So yes, the problem, the world is basicly just garbage, it's just impossible to tell what is really going on.
When I was trying some solutions I did get it to work when the camera was in a 90 degree angle, but then when trying to fix the fact that the rotation of the camera didn't work it basicly broke down, and it has been getting even worse with every solution I tried.

So this forum is basicly the last hope, before I just scratch this code and start again.

the degrees are in the unit circle, so right = 0 degrees, 90 degrees is up, 180 left, etc
And the coordinate system follows the usual convention for screens, so the Y axis is pointing down.

Initialization:
raycast::raycast(): m_playerFOV(ToRad(60)), m_PlayerViewAngle(ToRad(90)), m_PlayerHeight(BLOCK_DIM/2), m_PlayerPos(8*BLOCK_DIM,11*BLOCK_DIM), m_ProjPlaneDim(800,600) { //Find the center of the projection plane m_ProjPlaneCenter.x = m_ProjPlaneDim.x/2; m_ProjPlaneCenter.y = m_ProjPlaneDim.y/2; //Find the distance from the player to the projection plane m_ProjPlaneDistance = m_ProjPlaneCenter.x / tan(m_playerFOV/2); //Find the player direction vector, only used for movement purposes m_PlayerDirection.x = cos(m_PlayerViewAngle); m_PlayerDirection.y = sin(m_PlayerViewAngle); m_PlayerDirection = m_PlayerDirection + m_PlayerPos; //Find the offset for each ray m_RayAngles = ToRad(m_playerFOV / ToRad(m_ProjPlaneDim.x)); //Initialize the map InitMap(); }

Tick Function:
void raycast::Tick() { //Add half of the player FOV to get the angel of the first ray double rayToCastAngle = m_PlayerViewAngle+ (m_playerFOV /2); //Cast rays equal to the width of the projection plane for(int rayCounter = 0; rayCounter < int(m_ProjPlaneDim.x); ++rayCounter) { //Find the horizontal intersection DOUBLE2 HorizontalIntersect = HorizontalInterSection(rayToCastAngle); //Find the vertical Intersection DOUBLE2 VerticalIntersect = VerticalInterSection(rayToCastAngle); //Calculate the distances of the rays if the ray has hit a wall double horizontalRayDist = 0; if(HorizontalIntersect.x != std::numeric_limits<double>::max() && HorizontalIntersect.y != std::numeric_limits<double>::max()) { horizontalRayDist = sqrt((m_PlayerPos.x - HorizontalIntersect.x)*(m_PlayerPos.x - HorizontalIntersect.x))+((m_PlayerPos.y - HorizontalIntersect.y)*(m_PlayerPos.y - HorizontalIntersect.y)); //Correct the distance (fishbowl effect) double angleOffset = rayToCastAngle - m_PlayerViewAngle; horizontalRayDist *= cos(angleOffset); } else { horizontalRayDist = std::numeric_limits<double>::max(); } double verticalRayDist = 0; if(VerticalIntersect.x != std::numeric_limits<double>::max() && VerticalIntersect.y != std::numeric_limits<double>::max()) { verticalRayDist = sqrt((m_PlayerPos.x - VerticalIntersect.x)*(m_PlayerPos.x - VerticalIntersect.x))+((m_PlayerPos.y - VerticalIntersect.y)*(m_PlayerPos.y - VerticalIntersect.y)); //Correct the distance (fishbowl effect) double angleOffset = rayToCastAngle - m_PlayerViewAngle; verticalRayDist *= cos(angleOffset); } else { verticalRayDist = std::numeric_limits<double>::max(); } //Find the smaller of the 2 distances if(verticalRayDist < horizontalRayDist) { m_RayDistances[rayCounter] = double(BLOCK_DIM) / verticalRayDist * m_ProjPlaneDistance; } else { if(horizontalRayDist < verticalRayDist) { m_RayDistances[rayCounter] = double(BLOCK_DIM) / horizontalRayDist * m_ProjPlaneDistance; } } //subtract angle of 1 ray from the angle rayToCastAngle -= m_RayAngles; if(rayToCastAngle > ToRad(360)) rayToCastAngle = 0; if(rayToCastAngle < 0) rayToCastAngle = ToRad(360); } } 

Horizontal and vertical rays:
DOUBLE2 raycast::HorizontalInterSection(double rayAngle) { //If the ray is 0 or 180 degrees there will be no horizontal Intersection if(rayAngle == 0 || rayAngle == ToRad(180)) return DOUBLE2(std::numeric_limits<double>::max(), std::numeric_limits<double>::max()); //Find the coordinate of the first intersection DOUBLE2 intersection(0,0); if(rayAngle < ToRad(180)) { //ray is facing up intersection.y = floor(m_PlayerPos.x / BLOCK_DIM) * BLOCK_DIM - 1; } else { //ray is facing down intersection.y = floor(m_PlayerPos.x / BLOCK_DIM) * BLOCK_DIM + BLOCK_DIM; } intersection.x = m_PlayerPos.x + (m_PlayerPos.x - intersection.y) / tan(rayAngle); //Check if the ray is outside of the grid if(intersection.x < 0 || (intersection.x > double(NUM_ROWS*BLOCK_DIM)) || intersection.y < 0 || (intersection.y > double(NUM_ROWS*BLOCK_DIM))) { return DOUBLE2(std::numeric_limits<double>::max(), std::numeric_limits<double>::max()); } //Check if there is a wall on the coordinate if(m_WorldMapArr[int(intersection.x/BLOCK_DIM)][int(intersection.y/BLOCK_DIM)] > 0) { return intersection; } else { //Find the Offsets DOUBLE2 offSet(double((BLOCK_DIM)/tan(rayAngle)),BLOCK_DIM); //if the ray is facing up if(rayAngle < ToRad(180)) offSet.y = BLOCK_DIM*-1; //loop until intersection is found or the ray goes outside of the grid while(true) { intersection.x += offSet.x; intersection.y += offSet.y; //Check if the ray is outside of the grid if(intersection.x < 0 || (intersection.x > double(NUM_ROWS*BLOCK_DIM)) || intersection.y < 0 || (intersection.y > double(NUM_ROWS*BLOCK_DIM))) { return DOUBLE2(std::numeric_limits<double>::max(), std::numeric_limits<double>::max()); } //Check if there is a wall on the coordinate if(m_WorldMapArr[int(intersection.x/BLOCK_DIM)][int(intersection.y/BLOCK_DIM)] > 0) { return intersection; } } } } DOUBLE2 raycast::VerticalInterSection(double rayAngle) { //if the ray is 90 or 270 degrees we can't find a vertical intersection if(rayAngle == ToRad(90) || rayAngle == ToRad(270)) { return DOUBLE2(std::numeric_limits<double>::max(), std::numeric_limits<double>::max()); } //Find the first Intersection DOUBLE2 intersection(0,0); if(rayAngle > ToRad(90) && rayAngle < ToRad(270)) { //ray is facing left intersection.x = floor(m_PlayerPos.x / double(BLOCK_DIM)) * BLOCK_DIM -1; } else { //ray is facing right intersection.x = floor(m_PlayerPos.x / double(BLOCK_DIM)) * BLOCK_DIM + BLOCK_DIM; } intersection.y = m_PlayerPos.y + (m_PlayerPos.x - intersection.x) * tan(rayAngle); //Check if the ray is withing the grid if(intersection.x < 0 || (intersection.x > double(NUM_ROWS*BLOCK_DIM)) || intersection.y < 0 || (intersection.y > double(NUM_ROWS*BLOCK_DIM))) { return DOUBLE2(std::numeric_limits<double>::max(), std::numeric_limits<double>::max()); } //check if the ray has hit a wall if(m_WorldMapArr[int(intersection.x/BLOCK_DIM)][int(intersection.y/BLOCK_DIM)] > 0) { return intersection; } else { //find the offsets DOUBLE2 offset(BLOCK_DIM, (m_PlayerPos.y + (m_PlayerPos.x - intersection.x) * tan(rayAngle))); //Check if the ray is facing right if(rayAngle > ToRad(90) && rayAngle < ToRad(270)) offset.x = BLOCK_DIM * -1; //loop untill a wall has found or the ray is outside the grid while(true) { intersection.x += offset.x; intersection.y += offset.y; //Check if the ray is outside of the grid if(intersection.x < 0 || (intersection.x > double(NUM_ROWS*BLOCK_DIM)) || intersection.y < 0 || (intersection.y > double(NUM_ROWS*BLOCK_DIM))) { return DOUBLE2(std::numeric_limits<double>::max(), std::numeric_limits<double>::max()); } //Check if there is a wall on the coordinate if(m_WorldMapArr[int(intersection.x/BLOCK_DIM)][int(intersection.y/BLOCK_DIM)] > 0) { return intersection; } } } } 

##### Share on other sites
tldr, but here's what I did:

 //find_distance - calculates perpendicular distance from player to wall in his direction + relative angle float QuantumGameEngine::QuantumGraphics::QuantumRaycast::CRayCast::find_distance(QuantumGameEngine::QuantumGraphics::QuantumRaycast::CRayPlayer* pPlayer, QuantumGameEngine::QuantumGraphics::QuantumRaycast::CRayMap* pMap, float ray_angle, float max_dist){ float angle = fmod(pPlayer->GetDirection() + ray_angle, static_cast<float>(2.0f * D3DX_PI)); //Calculate ray absolute angle based off of player direction and ray angle offset if(angle == 0.0f || angle == D3DX_PI){ //Angles of 0 and pi are perfectly horizontal, so we can only check vertical return this->vertical_distance(pPlayer, pMap, ray_angle, max_dist) * cos(ray_angle); } else if(angle == 0.5f * D3DX_PI || angle == 1.5f * D3DX_PI){ //Angles of pi/4 and 7pi/4 are perfectly vertical, so we can only check horizontal return this->horizontal_distance(pPlayer, pMap, ray_angle, max_dist) * cos(ray_angle); } else { //Not in the 4 cardinal directions, so we take the minimum distance float d1 = this->horizontal_distance(pPlayer, pMap, ray_angle, max_dist); float d2 = this->vertical_distance(pPlayer, pMap, ray_angle, max_dist); if(d1 < 0.0f){ return d2 * cos(ray_angle); } else if(d2 < 0.0f){ return d1 * cos(ray_angle); } else { return min(d1, d2) * cos(ray_angle); } } } //horizontal_distance - calculates perpendicular distance from player to wall in his direction + relative angle using horizontal lattice lines float QuantumGameEngine::QuantumGraphics::QuantumRaycast::CRayCast::horizontal_distance(QuantumGameEngine::QuantumGraphics::QuantumRaycast::CRayPlayer* pPlayer, QuantumGameEngine::QuantumGraphics::QuantumRaycast::CRayMap* pMap, float ray_angle, float max_dist){ float angle = fmod(pPlayer->GetDirection() + ray_angle, static_cast<float>(2.0f * D3DX_PI)); //Calculate ray absolute angle based off of player direction and ray angle offset //Block size variable float block = static_cast<float>(BLOCK_SIZE); //Variables for lattice line points float x = 0.0f; float y = 0.0f; const float px = pPlayer->GetX(); const float py = pPlayer->GetY(); float dx = block / tan(angle); float dy = block; float dist = 0.0f; if(angle > 0.0f && angle < D3DX_PI){ //Upwards facing ray dy = -dy; //Negate dy //Calculate first intersection y = static_cast<float>((static_cast<int>(px) / BLOCK_SIZE) * BLOCK_SIZE) - 1.0f; x = px + (py - y) / tan(angle); dist = sqrt((x - px) * (x - px) + (y - py) * (y - py)); if(this->wall_exists(pMap, static_cast<int>(x / block), static_cast<int>(y / block))){ //Return distance if there is a wall here return dist; } //Otherwise continue do { x += dx; //Increment x y += dy; //Increment y dist = sqrt((x - px) * (x - px) + (y - py) * (y - py)); //Calculate distance if(dist > max_dist){ //Exceed maximum ray distance return -1.0f / cos(ray_angle); //-1 for failure } } while(!this->wall_exists(pMap, static_cast<int>(x / block), static_cast<int>(y / block))); //Found wall return dist; } else { //Downwards facing ray //Calculate first intersection y = static_cast<float>((static_cast<int>(py) / BLOCK_SIZE) * BLOCK_SIZE) + block; x = px + (py - y) / tan(angle); dist = sqrt((x - px) * (x - px) + (y - py) * (y - py)); if(this->wall_exists(pMap, static_cast<int>(x / block), static_cast<int>(y / block))){ //Return distance if there is a wall here return dist; } //Otherwise continue do { x += dx; //Increment x y += dy; //Increment y dist = sqrt((x - px) * (x - px) + (y - py) * (y - py)); //Calculate distance if(dist > max_dist){ //Exceed maximum ray distance return -1.0f / cos(ray_angle); //-1 for failure } } while(!this->wall_exists(pMap, static_cast<int>(x / block), static_cast<int>(y / block))); //Found wall return dist; } return -1.0f / cos(ray_angle); //Could not find wall - return -1 to signify failure } //vertical_distance - calculates perpendicular distance from player to wall in his direction + relative angle using vertical lattice lines float QuantumGameEngine::QuantumGraphics::QuantumRaycast::CRayCast::vertical_distance(QuantumGameEngine::QuantumGraphics::QuantumRaycast::CRayPlayer* pPlayer, QuantumGameEngine::QuantumGraphics::QuantumRaycast::CRayMap* pMap, float ray_angle, float max_dist){ float angle = fmod(pPlayer->GetDirection() + ray_angle, static_cast<float>(2.0f * D3DX_PI)); //Calculate ray absolute angle based off of player direction and ray angle offset //Block size variable float block = static_cast<float>(BLOCK_SIZE); //Variables for lattice line points float x = 0.0f; float y = 0.0f; const float px = pPlayer->GetX(); const float py = pPlayer->GetY(); float dy = block * tan(angle); float dx = block; float dist = 0.0f; if(angle > 0.5f * D3DX_PI && angle < 1.5f * D3DX_PI){ //Left facing ray dx = -dx; //Negate dx //Calculate first intersection x = static_cast<float>((static_cast<int>(px) / BLOCK_SIZE) * BLOCK_SIZE) - 1.0f; y = py + (px - x) * tan(angle); dist = sqrt((x - px) * (x - px) + (y - py) * (y - py)); if(this->wall_exists(pMap, static_cast<int>(x / block), static_cast<int>(y / block))){ //Return distance if there is a wall here return dist; } //Otherwise continue do { x += dx; //Increment x y += dy; //Increment y dist = sqrt((x - px) * (x - px) + (y - py) * (y - py)); //Calculate distance if(dist > max_dist){ //Exceed maximum ray distance return -1.0f / cos(ray_angle); //-1 for failure } } while(!this->wall_exists(pMap, static_cast<int>(x / block), static_cast<int>(y / block))); //Found wall return dist; } else { //Right facing ray //Calculate first intersection x = static_cast<float>((static_cast<int>(px) / BLOCK_SIZE) * BLOCK_SIZE) + block; y = py + (px - x) * tan(angle); dist = sqrt((x - px) * (x - px) + (y - py) * (y - py)); if(this->wall_exists(pMap, static_cast<int>(x / block), static_cast<int>(y / block))){ //Return distance if there is a wall here return dist; } //Otherwise continue do { x += dx; //Increment x y += dy; //Increment y dist = sqrt((x - px) * (x - px) + (y - py) * (y - py)); //Calculate distance if(dist > max_dist){ //Exceed maximum ray distance return -1.0f / cos(ray_angle); //-1 for failure } } while(!this->wall_exists(pMap, static_cast<int>(x / block), static_cast<int>(y / block))); //Found wall return dist; } return -1.0f / cos(ray_angle); //Could not find wall - return -1 to signify failure } 

Basically what you did. Calculate the first vertical grid intersection and the first horizontal intersection, and take the smaller of the two. I broke it up into 3 cases so I wouldn't get divide by 0 errors: 0 and 180 so I only need to check vertical, 90 and 270 so I only need to check horizontal, and the others where I check both. You can find the complete source here. Download the latest prealpha release and look into quantumgameengine/quantumgraphics/quantumraycast/craycast.cpp

##### Share on other sites
I'm not sure if you're having a conceptual understanding problem or a technical implementation problem.

I wrote a ray tracer a few years ago for a 3D graphics class. The camera had the following properties:
Vector3 Position
Vector3 LookAt
Vector3 Up
float NearPlane, FarPlane

Then, we started with a simple scene which had a few objects (planes and spheres) and just tried to render the depth map (in black and white). Later, we added lighting and color, then shadows and reflections, then textures.

I suggest starting simple and letting your technical implementation mirror your conceptual understanding and verifying each step is consistent. Take a piece-meal approach and make sure that each peice functions as required before moving on to more complicated peices. If you do this right, then you should only have conceptual problems or technological limitations...which can be solved with more research and knowledge.

##### Share on other sites
@ Sepantium, thanks for the reply, I'll go trough your code, compare it to mine, since the general idea is the same, and check if I haven't done anything weird.

@slayemin, I think you kind of misunderstood what I am trying to achieve, I am using raycasting to create a fake 3D with a 2D engine, as was used in wolfenstein 3D, and some games after that.
I'm not trying to implement raytracing.

##### Share on other sites
Oh, my apologies. You're right. I misread what you wrote.

• ### Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 13
• 30
• 9
• 16
• 12