2016-06-14 1 views
3

enter image description here J'essaye d'écrire un moteur raycasting.Moteur de jeu Java: Les murs raycasted sont creux, brisés, et ressemblent à la matière fécale

j'ai étudié le tutoriel trouvé à http://www.instructables.com/id/Making-a-Basic-3D-Engine-in-Java/ et C++ raycasting tutoriels trouvés à http://lodev.org/cgtutor/raycasting.html, et après quelques tentatives j'ai eu les rayons d'être jeté dans la bonne direction et donc obtenu un (semi-) sortie de travail.

J'ai fait apparaître les murs dans le monde et j'ai ajouté du mouvement dans le jeu et j'ai pu me déplacer. Cependant, les murs (qui sont censés être un cube) ne montrent que deux côtés du cube, peu importe la direction dans laquelle je me trouve dans le monde. Donc, au lieu de montrer un cube solide, il va sauter du côté du cube qui est le plus proche de la caméra, et montrer le côté éloigné du cube, et cela ne se produit que lorsque je fais face à l'origine (0,0) du tableau 2d dans lequel ma carte est stockée. Cette erreur est visible dans l'image ci-dessus.

Je pense que cette erreur est due à l'arrondi d'entier et à l'emplacement des parois détectées par le rayon étant arrondi vers le bas, mais je n'arrive pas à trouver une solution. Je suis en train de lancer deux rayons pour chaque colonne de pixels, l'un pour détecter les murs verticaux et l'autre 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 tracé.

Ma question est de savoir comment amener les murs à tirer correctement

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

Veuillez apprendre à utiliser les paragraphes. Un mur massif de texte sans interruptions logiques dissuade les gens et les force à ignorer votre question et/ou votre critique. Puis, en écrivant votre question, soyez concis et précis. Omettre toutes les informations superflues. Nous n'avons pas vraiment besoin de connaître toute l'histoire de la façon dont vous êtes arrivé à ce point. Il suffit de poser la question spécifique. –

+0

J'ai modifié votre message pour plus de clarté et de concision. Si vous avez enlevé trop, n'hésitez pas à revenir et à modifier vous-même. Cependant, il vous serait probablement très utile d'essayer de réduire votre code à un *** *** *** exemple qui démontre le problème. Comme pour votre prose, supprimez tout ce qui n'est pas lié au problème et créez un [mcve]. Le processus de ce faire vous apprendra probablement un peu et vous pourriez même résoudre votre propre problème. Cela fonctionne toujours comme ça pour moi. –

+0

Merci d'avoir laissé quelques critiques constructives sur mon poste au lieu d'aller droit au but et de partir. Ill garder ce que vous avez dit à l'esprit la prochaine fois que je poste. J'étais sur le point d'enlever tous les bits inutiles moi-même, merci pour cela. – xonerex

Répondre

1

Bon alors j'ai pu le réparer. Il s'est avéré que le problème était dû à l'arrondissement des nombres entiers (les coordonnées du mur seraient arrondies à la baisse) comme je le pensais. Lorsque les rayons étaient lancés dans une direction où x ou y (ou les deux) s'approchaient de zéro dans le réseau 2d, les coordonnées du mur seraient arrondies vers le bas, la distance au mur serait calculée incorrectement et le résultat ressemblerait à l'image au dessus. J'ai compris que cela se passait parce que je stockais les coordonnées de la paroi en double, et bien que les doubles soient certainement plus précis que les entiers, ils ne sont pas encore EXACT. Donc, ce qui se passait était que les coordonnées du mur seraient très proches de ce qu'elles auraient dû être légèrement décalées, et quand je jetterais ces valeurs à un entier tout en vérifiant les collisions entre les rayons, elles seraient arrondies à la valeur réelle. coordonner et me fournir une distance incorrecte. Donc, pour corriger cela, j'ai ajouté une très petite valeur (environ 0,0001) multipliée par la direction de pas du rayon (le pas peut être positif ou négatif 1 et détermine la distance perpendiculaire entre les lignes de grille suivantes verticales/horizontales) aux coordonnées des rayons tout en vérifiant les collisions entre les rayons et les parois afin d'équilibrer la légère imprécision de mon algorithme. En bref, cela a permis de rapprocher le mur détecté de 0,0001 unités du joueur, en contournant ainsi l'imprécision et en faisant en sorte que les coordonnées du rayon soient arrondies aux coordonnées réelles du mur.

+1

ne jamais comparer les nombres à virgule flottante pour l'égalité. toujours comparer la différence au delta. –

+0

Oui, je comprends maintenant. Valeurs de virgule flottante ne peuvent tout simplement pas faire confiance – xonerex

+0

Vous avez beaucoup à apprendre. Ce ne sont pas seulement des nombres flottants. Aucun numéro ne peut être approuvé. Ou les gens d'ailleurs. –