2016-06-14 3 views
3

enter image description here raycasting 엔진을 작성하려고합니다.Java Game Engine : Raycasted 벽이 투명하고 부서지며 대변처럼 보입니다.

http://www.instructables.com/id/Making-a-Basic-3D-Engine-in-Java/에있는 자습서와 http://lodev.org/cgtutor/raycasting.html에있는 C++ 레이캐스팅 자습서를 공부하고 몇 가지 시도를 한 후에 광선을 올바른 방향으로 전송하여 작업 결과를 얻었습니다.

나는 세계에 나타나기 위해 벽을 얻었고 나는 게임으로 움직임을 추가했고 나는 움직일 수 있었다. 그러나 벽 (큐브라고 생각되는)은 내가 세상에서 직면하고있는 방향에 관계없이 입방체의 두 면만 보여줍니다. 그래서 단단한 입방체를 보여주는 대신 실제로 카메라에 가장 가까운 입방체의 측면에서 점프하여 대신 큐브의 먼 쪽을 보여줍니다. 이것은 원점을 향한 메신저 (0,0) 내지도가 저장되어있는 2 차원 배열의.이 오류는 위의 이미지에 표시됩니다.

이 오류는 정수 반올림과 광선에 의해 감지되는 광선의 위치가 반올림 됨으로 인한 것 같지만 아직 해결 방법을 찾지 못하는 것 같습니다. 저는 실제로 각 픽셀 열에 대해 두 개의 광선을 주조했습니다. 하나는 수직 벽을 감지하고 다른 하나는 수평 벽을 감지합니다. 각 거리가 계산되고 비교 된 다음 최단 거리 벽이 그려집니다.

내 문제는 내가 그것을 고칠 수 있었다, 그래서 벽이 제대로

public class Screen { 

    //VARIABLE DECLARATIONS 
    //----------------------- 
    int FOV = 60; //field of view in degrees 
    int screenwidth = 800; //variable holds the vertical resolution of the screen 
    int screenheight = 600; //variable holds the horizontal resolution of the screen 
    double camx; //cameras x coordinate 
    double camy; //cameras y coordinate 
    double camAngle; //direction of camera in degrees 
    double rayAngle; //angle of ray being cast in radians 
    int x = 0; //holds the current pixel column being looped through 
    double IncrementAngle = (double)FOV/(double)screenwidth; //calculates the change in the rays angle for each horizontal pixel 

    int[][] map; //stores the 2d map that represents the 3d world of the game 

    public Screen() { 

    public int[] update(int[] pixels, int[][] m, double ca, double cx, double cy, int fov) { 

     FOV = fov; 
     IncrementAngle = (double)FOV/(double)screenwidth; //calculates the change in the rays angle for each horizontal pixel 

     camAngle = ca; 
     camx = cx; 
     camy = cy; 

     map = m; 

     int x = 0; 

     Color c; //declares new color 

     //fills background 
     for (int n = 0; n < pixels.length; n++) pixels[n] = Color.BLACK.getRGB(); 

     for (double ray = (double)(FOV/2); ray > (double)(-FOV/2); ray -= IncrementAngle) { 
      double vdist = Integer.MAX_VALUE, hdist = Integer.MAX_VALUE; 
      double perpendicularDist = 0; 
      double theta; 
      double lineheight; 
      int drawstart, drawend; 
      int side = 0; 

      int r = 0, g = 0, b = 0, a = 0; //variables that determinbe what colours will be drawn (red, green, blue, and alpha for 
transparency) 

      rayAngle = Math.toRadians(camAngle + ray); 

      try { 
       vdist = VertDist(rayAngle); 
      } 
      catch (ArrayIndexOutOfBoundsException e) {} 
      try { 
       hdist = HorDist(rayAngle); 
      } 
      catch (ArrayIndexOutOfBoundsException e) {} 

      theta = Math.abs(rayAngle - Math.toRadians(camAngle)); //angle difference between the ray being cast and the cameras 
direction 

      if (hdist < vdist) { 
       perpendicularDist = hdist * Math.cos(theta); 
       lastSide = 0; 
       r = Color.GRAY.getRed(); 
       g = Color.GRAY.getGreen(); 
       b = Color.GRAY.getBlue(); 
       a = Color.GRAY.getAlpha(); 
      } 
      else { 
       perpendicularDist = vdist * Math.cos(theta); 
       lastSide = 1; 
       r = Color.DARK_GRAY.getRed(); 
       g = Color.DARK_GRAY.getGreen(); 
       b = Color.DARK_GRAY.getBlue(); 
       a = Color.DARK_GRAY.getAlpha(); 
      } 
      //creates pulsating effect with wall colours 
      r -= pulse; 
      g += pulse * 2; 
      b -= pulse; 

      c = new Color(r, g, b, a); 

      lineheight = screenheight/perpendicularDist; 

      drawstart = (int)(-lineheight/2) + (screenheight/2); 
      drawend = (int)(lineheight/2) + (screenheight/2); 

      if (drawstart < 0) drawstart = 0; 
      if (drawend >= screenheight) drawend = screenheight - 1; 

      for (int y = drawstart; y < drawend; y++) { 
       pixels[x + (y * screenwidth)] = c.getRGB(); 
      } 

      if (x < screenwidth) x++; 
      else x = 0; 
     } 

     //returns pixels array to main class to be shown to screen 
     return pixels; 
    } 

    public double VertDist(double angle) { 
     double rx = 0, ry = 0; 
     double stepX = 0, stepY = 0; 
     double FstepX = 0, FstepY = 0; 
     double Fxcomp = 0, Fycomp = 0; 
     double xcomp = 0, ycomp = 0; 
     double mapx = camx, mapy = camy; 
     boolean hit = false; 
     double obliqueDist = 0; 

      rx = Math.cos(angle); 
      ry = Math.sin(angle); 

      if (rx < 0) { 
       stepX = -1; 
       FstepX = (camx - ((int)camx)) * stepX; 
      } 
      else if (rx > 0) { 
       stepX = 1; 
       FstepX = ((int)(camx + 1)) - camx; 
      } 

      ycomp = (stepX * Math.tan(angle) * -1); 
      Fycomp = Math.abs(FstepX) * ycomp; 

      if (map[(int)(mapx)][(int)(mapy)] > 0) hit = true; 

      mapx += FstepX; 
      mapy += Fycomp; 

      if (map[(int)(mapx)][(int)(mapy)] > 0) hit = true; 
      else { 
       while (!hit && mapx > 0 && mapy > 0) { //loops while a wall has not been found and while positive indexes are still being 
checked 
        mapx += stepX; 
        mapy += ycomp; 
        if (map[(int)(mapx)][(int)(mapy)] > 0) { 
         hit = true; 
         //if (Math.toDegrees(rayAngle) < 270 && Math.toDegrees(rayAngle) > 90) { 
         // mapy -= stepX; 
         // mapx -= ycomp; 
         //} 
        } 
       } 
      } 

      mapx = Math.abs(mapx - camx); 
      mapy = Math.abs(mapy - camy); 

      obliqueDist = Math.sqrt((mapx*mapx) + (mapy*mapy)); 
      //change to be not fixed angle based 
       //if (angle > Math.toRadians(135) && angle < Math.toRadians(225)) { 
       // obliqueDist -= Math.sqrt(stepX*stepX + ycomp*ycomp); 
      //} 
      return obliqueDist; 
    } 

    public double HorDist(double angle) { 
     double rx, ry; 
     double stepX = 0, stepY = 0; 
     double FstepX = 0, FstepY = 0; 
     double Fxcomp, Fycomp; 
     double xcomp, ycomp; 
     double mapx = camx, mapy = camy; 
     boolean hit = false; 
     double obliqueDist = 0; 

      rx = Math.cos(angle); 
      ry = Math.sin(angle); 

      if (ry < 0) { 
       stepY = 1; 
       FstepY = ((int)(camy + 1)) - camy; 
      } 
      else if (ry > 0) { 
       stepY = -1; 
       FstepY = (camy - (int)camy) * stepY; 
      } 

      xcomp = stepY/(Math.tan(angle) * -1); 
      Fxcomp = Math.abs(FstepY) * xcomp; 

      if (map[(int)(mapx)][(int)(mapy)] > 0) hit = true; 

      mapx += Fxcomp; 
      mapy += FstepY; 

      if (map[(int)(mapx)][(int)(mapy)] > 0) hit = true; 
      else { 
       while (!hit) { 
        mapx += xcomp; 
        mapy += stepY; 
        if (map[(int)(mapx)][(int)(mapy)] > 0) hit = true; 
       } 
      } 

      mapx = Math.abs(mapx - camx); 
      mapy = Math.abs(mapy - camy); 

      obliqueDist = Math.sqrt((mapx*mapx) + (mapy*mapy)); 
      //change to be not fixed angle based 
      //if (angle > Math.toRadians(45) && angle < Math.toRadians(135)) { 
       // obliqueDist -= Math.sqrt(xcomp*xcomp + stepY*stepY); 
      //} 
      return obliqueDist; 
    }  } 
+2

단락을 사용하는 법을 배우십시오. 논리적 인 단절이없는 거대한 텍스트의 벽은 사람들을 끄고 질문 및/또는 하향 표시를 건너 뜁니다. 그런 다음 질문을 작성할 때 간결하고 요점을 지키십시오. 관련없는 정보는 모두 생략하십시오. 우리는 당신이이 점에 어떻게 도달했는지에 대한 전체 역사를 알 필요가 없습니다. 구체적인 질문을하십시오. –

+0

내가 간결하고 간결하게 게시물을 편집했습니다. 내가 너무 많이 떼어 낸다면 자유롭게 돌아가서 편집 할 수 있습니다. 그러나 문제를 나타내는 *** *** 예제로 코드를 줄이려면 매우 유용 할 것입니다. 산문과 마찬가지로이 문제와 무관 한 것을 제거하고 [mcve]를 만드십시오. 그렇게하는 과정은 아마도 당신에게 꽤 많은 것을 가르쳐 줄 것이고 당신 자신의 문제를 해결할 수도 있습니다. 그것은 항상 저를 위해 그렇게 작동합니다. –

+0

실제로 직접 downvoting 및 떠나는 대신 내 게시물에 건설적인 비판을 떠나 주셔서 감사합니다. 다음에 내가 게시 할 때 염두에두고 말한 것을 계속 유지하십시오. 나는 실제로 불필요한 모든 비트들을 제거하려고하고 있었고, 그렇게 해 주셔서 감사합니다. – xonerex

답변

1

좋아 인출되도록하는 방법이다. 결과적으로, 생각했던 것처럼 정수 반올림 (벽 좌표는 반올림 됨)으로 인한 문제였습니다. 두 번째 배열에서 x 또는 y (또는 둘 다)가 0에 가까워지는 방향으로 광선이 투영되면 벽 좌표가 반올림되고 벽까지의 거리가 잘못 계산되어 결과가 그림처럼 보일 것입니다 위.

벽 좌표를 복소수에 저장했기 때문에이 문제가 발생했는데 복식은 정수보다 정확하지만 확실하지는 않습니다. 그래서 무슨 일이 일어나고 있었는지 벽면 좌표는 그들이 약간 떨어져 있었어야하는 것과 매우 가까울 것입니다. 그리고 광선 - 벽 충돌을 검사하는 동안이 값을 정수로 캐스팅하면 실제 값 아래로 반올림됩니다. 조정하고 잘못된 거리를 알려주십시오.

이 문제를 해결하기 위해 광선의 단계 방향을 곱한 매우 작은 값 (약 0.0001)을 추가했습니다 (단계 방향은 양수 또는 음수 일 수 있으며 이후 수직/수평 배열 격자 선 사이의 수직 거리를 결정합니다)를 광선 좌표에 추가하는 동안 광선 알고리즘 충돌을 검사하여 알고리즘의 약간의 부정확성을 해결합니다. 간단히 말해서, 탐지 된 벽을 0.0001 단위로 플레이어에 더 가깝게 배치하여 정확도를 무시하고 광선 좌표가 벽의 실제 좌표로 성공적으로 반올림되도록하는 것입니다.

+1

부동 소수점 숫자가 같은지 비교할 수 없습니다. 델타와 항상 차이를 비교하십시오. –

+0

네, 이제 이해합니다. 부동 소수점 값은 신뢰할 수 없습니다. – xonerex

+0

많은 것을 배워야합니다. 유동적 인 숫자가 아닙니다. 숫자는 신뢰할 수 없습니다. 또는 그 문제에 대한 사람들. –