Moteur de jeu Java: Les murs raycastés sont creux, cassés et ressemblent à de la matière fécale


entrez la description de l'image icije suis d'essayer d'écrire un raycasting moteur.

J'ai étudié le tutoriel trouvé à http://www.instructables.com/id/Making-a-Basic-3D-Engine-in-Java/ et les tutoriels C++ raycasting trouvés à http://lodev.org/cgtutor/raycasting.html , et après quelques tentatives, j'ai obtenu que les rayons soient lancés dans la bonne direction et j'ai donc obtenu une sortie (semi) fonctionnelle.

J'ai eu les murs pour apparaître dans le monde et j'ai ajouté du mouvement dans le jeu et j'étais mesure de se déplacer. Cependant, les murs (qui sont censés être un cube) ne montrent que deux côtés du cube, quelle que soit la direction à laquelle je suis confronté dans le monde. Donc, au lieu de montrer un cube solide, il va sauter du côté du cube qui est réellement le plus proche de la caméra, et montrer le côté éloigné du cube à la place, et cela ne se produit que lorsque je suis tourné vers l'origine (0,0) du tableau 2d dans lequel ma carte est stockée. Cette erreur est affichée dans l'image ci-dessus.

Je pense que cette erreur est due pour arrondir les nombres entiers et les emplacements des murs détectés par le rayon étant arrondis vers le bas, mais je ne peux pas sembler trouver une solution. Je lance en fait deux rayons pour chaque colonne de pixels, un pour détecter les murs verticaux et un pour détecter les murs horizontaux. Les distances de chacun sont calculées et comparées, puis le mur de distance le plus court est dessiné.

Ma question est de savoir comment dessiner correctement les murs

 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;
     }     }
Author: xonerex, 2016-06-14

1 answers

D'accord, j'ai donc pu le réparer. Il s'avère que le problème était dû à l'arrondi des entiers (les coordonnées du mur seraient arrondies) comme je l'avais pensé. Lorsque les rayons étaient projetés dans une direction où x ou y (ou les deux) approchaient de zéro dans le tableau 2d, les coordonnées du mur seraient arrondies vers le bas, la distance au mur serait calculée de manière incorrecte et le résultat ressemblerait à l'image ci-dessus.

J'ai compris que cela se passait parce que je stockais les coordonnées du mur en doubles, et bien que les doubles soient certainement plus précis que les entiers, ils ne le sont toujours pas. Donc, ce qui se passait, c'était que les coordonnées du mur seraient très proches de ce qu'elles auraient dû être encore légèrement décalées, et quand j'ai converti ces valeurs en un entier tout en vérifiant les collisions ray-wall, elles arrondiraient à la valeur sous la coordonnée réelle et me fourniraient une distance incorrecte.

Donc, pour résoudre ce problème, j'ai ajouté une très petite valeur (environ 0,0001) multipliée par la direction du pas du rayon (la direction du pas peut être positive ou négative 1 et détermine la distance perpendiculaire entre les lignes de grille de tableau verticales/horizontales suivantes) aux coordonnées du rayon tout en vérifiant les collisions rayon-mur afin d'équilibrer la légère inexactitude de mon algorithme. En bref, cela a permis de rapprocher le mur détecté des unités 0.0001 du joueur, contournant ainsi l'inexactitude et permettant d'arrondir avec succès les coordonnées des rayons à les coordonnées réelles du mur.

 2
Author: xonerex, 2016-06-17 03:20:03