Java Game Engine: Стены Raycasted полые, сломанные и выглядят как фекалии.

введите здесь описание изображенияЯ пытаюсь написать механизм рейкастинга.

Я изучил учебник, найденный по адресу http://www.instructables.com/id/Making-a-Basic-3D-Engine-in-Java/ и руководства по raycasting C++, которые можно найти по адресу http://lodev.org/cgtutor/raycasting.html., и после нескольких попыток я получил лучи, направленные в правильном направлении, и, следовательно, получил (полу) работающий выход.

Я заставил стены появляться в мире, и я добавил движение в игру, и я смог передвигаться. Однако стены (которые должны быть кубом) показывают только две стороны куба, независимо от того, в каком направлении я смотрю в мире. Таким образом, вместо того, чтобы показывать сплошной куб, он будет прыгать со стороны куба, которая на самом деле ближе всего к камере, и вместо этого показывать дальнюю сторону куба, и это происходит только тогда, когда я смотрю в начало координат (0,0 ) массива 2d, в котором хранится моя карта. Эта ошибка показана на изображении выше.

Я думаю, что эта ошибка связана с целочисленным округлением и расположением стен, обнаруженных лучом, округленным в меньшую сторону, но я не могу найти решение. На самом деле я отбрасываю два луча для каждого столбца пикселей, один для обнаружения вертикальных стен и один для обнаружения горизонтальных стен. Расстояния каждого из них рассчитываются и сравниваются, а затем рисуется стена с кратчайшим расстоянием.

У меня вопрос, как заставить стены правильно рисоваться

 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;
     }     }

person xonerex    schedule 14.06.2016    source источник
comment
Пожалуйста, научитесь использовать абзацы. Огромная стена текста без логических разрывов просто отталкивает людей и заставляет их пропускать ваш вопрос и/или голосовать против. Тогда, когда будете писать свой вопрос, будьте кратки и по существу. Опустите всю постороннюю информацию. Нам на самом деле не нужно знать всю историю того, как вы дошли до этого момента. Просто задайте конкретный вопрос.   -  person Jim Garrison    schedule 14.06.2016
comment
Я отредактировал ваш пост для ясности и краткости. Если вы считаете, что я удалил слишком много, не стесняйтесь вернуться и отредактировать себя. Однако вам, вероятно, будет очень полезно попытаться сократить свой код до минимального примера, демонстрирующего проблему. Как и в прозе, удалите все, что не связано с проблемой, и создайте минимально воспроизводимый пример. Этот процесс, вероятно, многому вас научит, и вы даже сможете решить свою собственную проблему. У меня всегда так работает.   -  person Jim Garrison    schedule 14.06.2016
comment
Спасибо, что действительно оставили конструктивную критику в моем посте вместо того, чтобы сразу проголосовать и уйти. Я учту то, что вы сказали, в следующий раз, когда я опубликую. На самом деле я как раз собирался сам удалить все ненужные биты, так что спасибо за это.   -  person xonerex    schedule 14.06.2016
comment
У вас явно есть талант, и вы могли бы стать хорошим разработчиком, но, как вы признаете, вы молоды и все еще новичок. Поэтому я не хочу вас обескураживать. Четкое и лаконичное общение — чрезвычайно важный навык, равно как и способность исследовать проблему и извлекать важные части из шума. Удачи!   -  person Jim Garrison    schedule 14.06.2016
comment
Спасибо вам за помощь! Это также не помогает, что я невероятно недосыпаю в данный момент.   -  person xonerex    schedule 14.06.2016
comment
вам нужно начать отладку. См. мои примеры изображений Wolfenstein в этом QA stackoverflow.com/a/32733650/2521214, особенно предварительный просмотр 2D-карты в верхнем левом углу с включены лучи. Попробуйте добавить то же самое в свое приложение, чтобы вы могли сразу увидеть, что не так с вашими лучами. Если они верны, проверьте визуализацию V-линий, если они правильно вычислили положение и размер. Наконец, проверьте анти-рыбий глаз. ... вы также должны опубликовать несколько изображений, чтобы кто-то мог что-нибудь заметить ,,,   -  person Spektre    schedule 14.06.2016
comment
@xonerex вы можете начать с рендеринга точки для каждого столкновения луча со стеной (не останавливайтесь на ближайшей стене) и иметь отдельные цвета для столкновения со стеной по горизонтали и вертикали. Таким образом, вы могли бы увидеть, нет ли в проблеме какой-то закономерности, раскрывающей себя.   -  person Spektre    schedule 14.06.2016
comment
Как бы странно это ни звучало, попросить дополнительный код, не могли бы вы дать ссылку на весь проект? Я понимаю, что такие вещи трудно свести к минимуму, поэтому возможность взаимодействия с кодом действительно поможет.   -  person Zarkonnen    schedule 14.06.2016
comment
Спасибо за предложения Спектр. Я обязательно попробую это. И, Зарконнен, вот ссылка на весь проект: dropbox.com/sh/ eyfy34v8h2ke7a0/AADv3Pb7NTsv1DHV_sLSGttQa?dl=0 (всё разобрано, так что если вы хотите его запустить, вам придётся создать несколько папок и переместить некоторые файлы).   -  person xonerex    schedule 14.06.2016
comment
Слепая, полуобразованная догадка: попробуйте изменить порядок (ориентацию) граней вашего куба. Итак, если один треугольник теперь состоит из вершин (v0, v1, v2), измените его на (v0, v2, v1) и посмотрите, работает ли он. Если не работает то, можно приступать к серьезной отладке.   -  person Marco13    schedule 14.06.2016
comment
@xonerex изображение выглядит так, как будто вы выбираете более дальний перекресток, а не ближайший к зрителю.   -  person Spektre    schedule 14.06.2016


Ответы (1)


Хорошо, так что я смог это исправить. Как оказалось, проблема возникла из-за целочисленного округления (координаты стены будут округлены в меньшую сторону), как я и думал. Когда лучи отбрасывались в направлении, где x или y (или оба) приближались к нулю в массиве 2d, координаты стены округлялись в меньшую сторону, расстояние до стены вычислялось неправильно, и результат выглядел бы как на картинке выше.

Я понял, что это происходит, потому что я хранил координаты стены в двойных числах, и хотя двойные значения, безусловно, более точны, чем целые числа, они все же не ТОЧНЫ. Происходило то, что координаты стены были бы очень близки к тому, что они должны были быть, но немного отклонялись, и когда я приводил эти значения к целому числу при проверке столкновений луча со стеной, они округлялись бы до значения ниже фактического координаты и предоставить мне неправильное расстояние.

Поэтому, чтобы исправить это, я добавил очень маленькое значение (около 0,0001), умноженное на направление шага луча (направление шага может быть как положительным, так и отрицательным 1 и определяет перпендикулярное расстояние между последующими вертикальными/горизонтальными линиями сетки массива) к лучу. координаты при проверке столкновений луча со стеной, чтобы сбалансировать небольшую неточность моего алгоритма. Короче говоря, это привело к тому, что обнаруженная стена приблизилась к игроку на 0,0001 единицы, тем самым обойдя неточность и заставив координаты луча успешно округлиться до фактических координат стены.

person xonerex    schedule 17.06.2016
comment
никогда не сравнивайте числа с плавающей запятой на равенство. всегда сравнивайте разницу с дельтой. - person MK.; 17.06.2016
comment
Да, я понимаю это сейчас. Значения с плавающей запятой просто нельзя доверять - person xonerex; 23.06.2016
comment
Вам предстоит многому научиться. Это не просто плавающие числа. Никаким цифрам нельзя доверять. Или людей, если уж на то пошло. - person MK.; 24.06.2016