Bonjour,

Je rencontre un petit soucis avec mon code. J'essaie de créer a l'aide d'une image de Perlin une carte. J'arrive a avoir une carte mais pas très esthétique malheureusement. (Voici mon code GitHub si vous voulez la voir : https://github.com/CrisDossierPJ/tower-defense).
J'aimerai, afin de rendre mon code plus esthétique et afin de mettre des coordonnées, mettre mon Canvas en sorte de carrés de 50x50p, afin de faire un peu comme une carte de Pokémon, et de rendre ainsi ma carte plus esthétique.
Voici mon code :

var inc = 0.0045;

function setup() {
  createCanvas(1500 , 900 );
  pixelDensity(1);

}

function draw() {

  var yoff = 0;

  var plaine = 0;
  var montagne = 0;
  var ocean = 0;
  var plage = 0;

  var ctx = canvas.getContext("2d");

  loadPixels();

  for (var y = 0; y < height; y++) {
    var xoff = 0;

    for (var x = 0; x < width; x++) {
      var index = (x + y * width) * 4;
      //var r = random(255);
      var r = noise(xoff, yoff) * 255;

      pixels[index ] = r;
      pixels[index + 1] = r;
      pixels[index + 2] = r;
      pixels[index + 3] = 255;

         for (var y = 0; y < 50; y++) {

    for (var x = 0; x < 50; x++) {

      for (var i = 0; i <= 4; i++) {

        if ( pixels[index + i] < 255 &&  pixels[index + i] >= 150) //ocean.         
      {

      pixels[index + 0] = 0;
      pixels[index + 1] = 0;
      pixels[index + 2] = 204;
      pixels[index + 3] = 255;

      }
      if ( pixels[index + i] < 151 &&  pixels[index + i] > 147) //plage.
      {

      pixels[index + 0] = 255;
      pixels[index + 1] = 255;
      pixels[index + 2] = 0;
      pixels[index + 3] = 255;

      }
      if ( pixels[index + i] < 148 &&  pixels[index + i] > 100) //plaine.
      {

      pixels[index + 0] = 0;
      pixels[index + 1] = 100;
      pixels[index + 2] = 0;
      pixels[index + 3] = 255;

      }
      if ( pixels[index + i] < 31 &&  pixels[index + i] > 0) //neige.
      {

      pixels[index + 0] = 255;
      pixels[index + 1] = 255;
      pixels[index + 2] = 255;
      pixels[index + 3] = 255;

      }

       if ( pixels[index + i] < 71 &&  pixels[index + i] > 30) //montagne.
      {

      pixels[index + 0] = 0;
      pixels[index + 1] = 0;
      pixels[index + 2] = 0;
      pixels[index + 3] = 255;

      }
      if ( pixels[index + i] < 101 &&  pixels[index + i] > 70) //plaine.
      {

       pixels[index + 0] = 0;
      pixels[index + 1] = 100;
      pixels[index + 2] = 0;
      pixels[index + 3] = 255;

      }
    }
  }

      }

      xoff += inc;

    }

    yoff += inc;

  }

  updatePixels();

  noLoop();

}

Je veux obtenir une carte 'belle', plus esthétique.

J'obtiens une boucle infinie, je ne comprends pas pourquoi :(.
Merci de vos réponses et de votre aide !

23 réponses


hum... ça veut dire koi plus 'esthétique' ?
je découvre cette lib p5.js, ça à l'air intéressant....

c.attia
Auteur

Lorsque je lance mon programme, je tombe sur une île pas très esthétique donc pas très jouable. J'ai des petits lacs partout c'est pas très sympa pour ce que je veux en faire. J'aimerai classer mon canvas en grille de carré de 50x50p, et que je puisse colorer chaque carré individuellement, pour avoir une map un peu comme celle de pokémon.
Oui cette lib est vraiment intéressante, je te conseille le youtuber The Coding Train qui utilise cette lib.

hello, je connais pas pokemon (je suis trop vieux ;)).
tu dessines une carte aléatoire avec noise donc forcé que tu ais un rendu comme tel...
à mon avis, il faut revoir la technique : pourquoi pas des textures images que tu pourras placer aléatoirement ?

c.attia
Auteur

Bonjour,
J'aimerai que cette carte soit justement moins aléatoire, ainsi je prendrai chaque cases, je compte a l'intérieur combien de cases sont vertes/bleu/noires enfin je colorie la case de la couleur ou il y a le plus de pixel. C'est un peu confus désolé.
Je dois utiliser une image de briut

hello, difficile de comprendre...
ça veut dire koi :
"Je dois utiliser une image de bruit" ?

c.attia
Auteur

J'ai une obligation a utiliser une image de bruit , mais je l'ai déjà réaliser :).
Ma question principale c'est de savoir si l'on pourrait colorer une partie du canvas de la couleur que l'on souhaite, par exemple pour la premiere cases de 50x50p, j'aimerai qu'il soit vert (par exemple).

c.attia
Auteur

J'ai essayer quelque trucs :

 for (var m = 0; m * 50 < 1500 ;m++)
  {
    monTab[m] = new Array();
  }

  //Pour chaque case
  for (var e = 0; e * 50 < 1500 ;e++)
  {
    for (var f = 0; f * 50 < 900 ; f++)
    {
      //On regarde chaque pixel
      for (var g = 0; g < 50; g++)
      {
        for(var h = 0; h < 50; h++)
        {
        console.log("Salut Je suis dans le i");

          if(canvas.x < 255 &&  pixels[index + 0] >= 150)
          {
            ocean++;
            //alert("ocean");
          }

          else if(pixels[index + 1] < 150 &&  pixels[index + 1] >= 70)
          {
            plaine++;
            //alert("plaine");
          }

          else if(pixels[index + 2] < 70 &&  pixels[index + 2] >= 1)
          {

            montagne++;
            //alert("montagne");
          }

         }
        }

      if(montagne>plaine && montagne>ocean)
      {
         monTab[g][h] = "montagne";

      }

      if(ocean>plaine && ocean>montagne)
      {

        monTab[g][h] = "ocean";

      }

      if(plaine>ocean && plaine>montagne)
      {
        monTab[g][h] = "plaine";

      }
      ocean = 0;
      plaine = 0;
      montagne = 0;        

    }

  }

  for (var g = 0; g * 25 < 100 ;g++)
  {
    console.log(monTab[g][h] ); // h est undefined
    for (var h = 0; h * 25 < 100 ; h++)
    {

      console.log(monTab[g][h]);

    }
  }

Salut tu peux faire un tableau à deux dimensions genre ca ou le 1 correspond à du bleu , le 2 à du vert etc.....

var map = [   [1,2,3,4],
                       [5,6,7,8]
                    ];

et une boucle pour colorer et générer

var initX = 0;
var initY = 0;
//parcours les lignes
for(var i = 0 ; i<map.length;i++)
{
    //parcours les colones
    for(var j = 0; j< map[i].length;j++)
    {
        //on dessine le carré au coordonnée initX,initY avec la couleur map[i][j]
        initX+=50;
    }
    initY+=50;
}

oulala, c koi ce code ?
g pas envie de tout lire.........
on va essayer de se comprendre :
tu as une 'map' bruitée (chaque 'pixel' de ta map à une profondeur de n) et tu veux la 'scaller' à 50x50 en donnant une couleur en fct de n ?
c ça ?

c.attia
Auteur

Enfait dans chaque cases de 50x50p je compte le nombre de pixel qui ont une profondeur de n et je seuille cette image. J'ai trois seuillage : Montagne, Océan et plaine. Ainsi, je compte le nombre de pixel appartenant a Océan par exemple et si il y a plus de pixel Ocean que de Pixel Montagne et Plaine, je colore la case en bleu. Si il y a plus de Pixels Plain que Montagne et Océan, je colores la case en vert, etc. Ensuite je passe a la deuxième case, je compte le nombre de pixel et ainsi de suite.

c.attia
Auteur

@cattleyas10 Je comprends pas trop ton code.. Mais ce qu'il me manque c'est de stocker puis d'afficher les données que je garde dans mon tableau.

hello, donc on est ok, j'avais bien compris (enfin je crois ;))...
alors pourquoi tu fais tout ça :) ?
si tu réfléchis bien :

  • l' "unité" de ta carte est de 50x50 (cad que tu n'auras pas plus de détails)
  • il te suffit donc de crées une map bruitée (ou aléatoire comme tu veux) 50x plus petite
  • et tu la scale à 50...
    tout simple. @cattleyas10 a raison :) à par que j'aime pas ses initX et initY (mais il est au courant :))

jquelques lignes de code valent mieux qu'un long discours :

var map = [];
// tu veux une carte de 1500 X 900
var mapW = 1500;
var mapH = 900;
// tu veux des cases de 50 x 50
var cW = 50;
var cH = 50;
    // on sait jamais, c peut-être pas divisible, donc on arrondit
    mapW = Math.ceil(mapW/cW)*cW; 
    mapH = Math.ceil(mapH/cH)*cH;

// profondeur de la map : 3 car océan, plaine, montagne
var p = 3;

//on fabrique une map réduite en fonction de tes $cW et $cH
// a toi de def l' "orientation" de ta map x,y ou y,x. ici x,y
for(var x = 0; x<mapW/cW ; x++){
    map[x] = [];
    for(var y = 0; y<mapH/cH ; y++){
        // ici je construis une map au hasard
        // c pour l'exemple
        // à toi de développer un algo plus "esthétique"
        // avec de grandes régions par exemple : c une idée comme ça ;)
        map[x][y] = Math.round(Math.random() * p) + 1; // un nb au hasard entre 1, 2 et 3
    }
}

//on va rendre tout ça sans p5.js car c pour l'exemple :

// on définit les couleurs
var colors = { //je préfère les object au array......
    1 : "rgb(200,0,0)", // océan
    2 : "rgb(0,200,0)", // plaine
    3 : "rgb(0,0,200)" // montagne
};

// on récupère notre dom canvas
var canvas = document.querySelector('#canvas');
// on le dimensionne bien
canvas.setAttribute("width", mapW);
canvas.setAttribute("height", mapH);

// on récupère notre espace de dessin
var ctx = canvas.getContext("2d");

// c parti
for(x = 0; x < map.length ; x++){
    for(y = 0; y < map[x].length; y++){
        ctx.fillStyle = colors[map[x][y]];
        ctx.fillRect (x*cW, y*cH, x*cW+cW, y*cH+cH);
    }
}
// et vala

20 lignes de codes maxi. pour une map aléatoire
qq lignes de plus pour l' "esthétique"...
ct ta question de départ... mais là, tu as un code de base à ta réflexion

et je rajouterais : un code propre... ;)

c.attia
Auteur

Si je crée une image de bruit a pour chaque cases ce sera trop "aléatoire". C'est pour cela que j'aimerai pouvoir isoler des carrés de 50x50p. J'ai commencer un autre code :

var inc = 0.0045;

function setup() {
  pixelDensity(1);
  createCanvas(1500 , 900 );

}

function draw() {

  var ctx = canvas.getContext("2d");

  var yoff = 0;

  var plaine = 0;
  var montagne = 0;
  var ocean = 0;
  var plage = 0;

  loadPixels();

  for (var y = 0; y < height; y++) {
    var xoff = 0;

    for (var x = 0; x < width; x++) {
      var index = (x + y * width) * 4;
      //var r = random(255);
      var r = noise(xoff, yoff) * 255;

      pixels[index + 3] = 255;

         if ( r < 255 &&  r >= 150) //ocean.     
         {              
         pixels[index + 0] = 0;
         pixels[index + 1] = 0;
         pixels[index + 2] = 
         ocean++;
         }
         if ( r < 151 &&  r > 147) //plage.
         {

         pixels[index + 0] = 255;
         pixels[index + 1] = 255;
         pixels[index + 2] = 0;

         }
         if ( r < 148 &&  r > 70) //plaine.
         {

         pixels[index + 0] = 0;
         pixels[index + 1] = 100;
         pixels[index + 2] 
         plaine++;
         }
         if ( r < 31 && r > 0) //neige.
         {

         pixels[index + 0] = 255;
         pixels[index + 1] = 255;
         pixels[index + 2] = 255;
         }

          if ( r < 71 &&  r > 30) //montagne.
         {             
         pixels[index + 0] = 0;
         pixels[index + 1] = 0;
         pixels[index + 2] = 0;
         montagne++;
         }

      xoff += inc;

    }

    yoff += inc;

  }

updatePixels();
  for(var g = 0; g < 50 ; g++)
  {
    for(var h = 0; g < 50; h++)
    {
    if(montagne>plaine && montagne > ocean)
     {
        ctx.beginPath();
       ctx.fillStyle = "black";
       ctx.fillRect(0,0,50,50);
        ctx.closePath();
      }    
      if(plaine>ocean && plaine > ocean)
      {
        ctx.beginPath();
       ctx.fillStyle = "green";
       ctx.fillRect(0,0,50,50);
        ctx.closePath();
      }
      if(ocean>plaine && ocean > montagne)
      {
        ctx.beginPath();
       ctx.fillStyle = "blue";
       ctx.fillRect(0,0,50,50);
        ctx.closePath();
      }
    }
  }

  console.log("montagne " + montagne);
  console.log("plaine " + plaine );
  console.log("ocean " + ocean);

  noLoop();

}

je comprends toujours pas cette logorrhée de code...
donne nous un lien vers le rendu. ça aidera à comprendre.

Je ne connais pas la lib qui utilise pixel .. en revanche dans tes deux boucles à la fin c'est quoi le but de dessiner un rectangle au même endroit ? car dans tout tes if ca dessine au même endroit à savoir ici dans l'angle haut gauche du canvas..

c.attia
Auteur

Oui du coup j'utilise des rectangles que je colore par la suite, mais d'abord j'aimerai compter le nombre de pixels pour chaque seuillage. Par exemple, pour les pixels compris entre 0 et 50, je compte le nombre de pixels qui sont verts, puis qui sont rouges puis qui sont bleus. Ensuite je prends le plus grand nombre et je colore la case entière dans la couleur (Si bleu > rouge et bleu > vert alors je colore toute la case en bleu). Puis je ferai la meme chose entre 50 et 100 puis 100 et 150 et ainsi de suite.
Voici ce que j'ai commencer a faire :

var inc = 0.0045;

function setup() {
  pixelDensity(1);
  createCanvas(1500 , 900 );

}

function draw() {

  var ctx = canvas.getContext("2d");

  var yoff = 0;

  var plaine = 0;
  var montagne = 0;
  var ocean = 0;
  var plage = 0;

  loadPixels();

  for (var y = 0; y < height; y++) {
    var xoff = 0;

    for (var x = 0; x < width; x++) {
      var index = (x + y * width) * 4;
      //var r = random(255);
      var r = noise(xoff, yoff) * 255;

      pixels[index + 3] = 255;

         if ( r < 255 &&  r >= 150) //ocean.     
         {              
         pixels[index + 0] = 0;
         pixels[index + 1] = 0;
         pixels[index + 2] = 
         ocean++;
         }
         if ( r < 151 &&  r > 147) //plage.
         {

         pixels[index + 0] = 255;
         pixels[index + 1] = 255;
         pixels[index + 2] = 0;

         }
         if ( r < 148 &&  r > 70) //plaine.
         {

         pixels[index + 0] = 0;
         pixels[index + 1] = 100;
         pixels[index + 2] 
         plaine++;
         }
         if ( r < 31 && r > 0) //neige.
         {

         pixels[index + 0] = 255;
         pixels[index + 1] = 255;
         pixels[index + 2] = 255;
         }

          if ( r < 71 &&  r > 30) //montagne.
         {             
         pixels[index + 0] = 0;
         pixels[index + 1] = 0;
         pixels[index + 2] = 0;
         montagne++;
         }

      xoff += inc;

    }

    yoff += inc;

  }

updatePixels();

    if(montagne>plaine && montagne > ocean)
     {
        ctx.beginPath();
       ctx.fillStyle = "black";
       ctx.fillRect(0,0,50,50);
        ctx.closePath();
      }    
      if(plaine>ocean && plaine > ocean)
      {
        ctx.beginPath();
       ctx.fillStyle = "green";
       ctx.fillRect(0,0,50,50);
        ctx.closePath();
      }
      if(ocean>plaine && ocean > montagne)
      {
        ctx.beginPath();
       ctx.fillStyle = "blue";
       ctx.fillRect(0,0,50,50);
        ctx.closePath();
      }

  console.log("montagne " + montagne);
  console.log("plaine " + plaine );
  console.log("ocean " + ocean);

  noLoop();

}

pourrais tu nous donner un échantillon des valeurs de r (les 100 premiers par exemple) que je comprenne pourquoi tu ne veux pas utiliser le code donné plus haut ?

c.attia
Auteur

Le tableau pixels[] correspond au RGB(Red Green Blue) du canvas, pour colorer le canvas.

hummm, g po demandé pixel mais r dans ton code :

var r = noise(xoff, yoff) * 255;

puisque c la seule différence avec ma proposition....

sorry, mais je pense que l'on ne se comprend pas.
soit c moi, soit c toi, qui s'exprime mal...
donc, si c moi qui ait mal compris, je peux pas faire mieux...
++

c.attia
Auteur

C'est bon j'y suis arrivé, merci quand même de votre temps !

c.attia
Auteur

Je met mon code au cas ou il y ai quelqu'un qui bloque ici : (librairie utilisée p5.js)

var inc = 0.0045;

function setup() {

  createCanvas(1300 , 900 );
  pixelDensity(1);

}

function draw()
{

  var ctx = canvas.getContext("2d");
  var lignePix = -1;
  var lignePixH = -1;

  var yoff = 0;

  var plaine = 0;
  var montagne = 0;
  var ocean = 0;
  var plage = 0;
  var tabR = new Array();

  loadPixels();

  for (var y = 0; y < height; y++)
  {
        var xoff = 0;

    for (var x = 0; x < width; x++)
    {

      var index = (x + y * width) * 4;
      //var r = random(255);
      var r = noise(xoff, yoff) * 255;

      tabR[x+(y*width)] = r;
      //console.log(tabR[x]);

      pixels[index ] = r
      pixels[index + 1 ] = r
      pixels[index + 2 ] = r
      pixels[index + 3] = 255;

        if ( r < 255 &&  r >= 170) //ocean.
        {
         pixels[index + 0] = 0;
         pixels[index + 1] = 0;
         pixels[index + 2] = 204;
         ocean++;
        }

         if ( r < 170 &&  r > 85) //plaine.
        {
         pixels[index + 0] = 0;
         pixels[index + 1] = 100;
         pixels[index + 2] = 0;
         plaine++;
         }

          if ( r < 85 &&  r > 0) //montagne.
         {
         pixels[index + 0] = 0;
         pixels[index + 1] = 0;
         pixels[index + 2] = 0;
         montagne++;
         }

          xoff += inc;
    }
        yoff += inc;
        //console.log("r" + r);
  }
  updatePixels();

for(var a = 0; a<height; a+=25)// faire des bons en hauteur
{

  for(var b =0; b< width; b+=25)//faire des bons en largeur
  {

    var vert=0;
    var bleu = 0;
    var noir =0;

    for(var g = 0; g < 25; g++)//on s'occupe pixel par pixel hauteur
    {
        ocean = 0;
        plaine = 0;
        montagne = 0;

            for(var h = 0; h < 25; h++)//on s'occupe pixel par pixel largeur
            {
                var pix = h+(g*width)+b+(a*width);
                var y = tabR[pix];

                if ( y< 255 &&  y >= 170) //ocean.
                {
                  ocean++;
                }

                if ( y < 170 &&  y > 85) //plaine.
                {

                 plaine++;
                }

                if ( y <= 85 &&  y >= 0) //montagne
                {
                 montagne++;
                }

            }

       if(montagne > plaine && montagne > ocean )
       {
          noir++;
       }

       if(plaine > montagne && plaine > ocean)
       {
         vert++;
       }

       if(ocean > plaine && ocean > montagne)
       {
        bleu++;
       }

     }

     /*console.log("montagne " + noir);
           console.log("plaine " + vert );
           console.log("ocean " + bleu);*/
      if(noir>vert && noir>bleu)
      {
      ctx.beginPath();
      ctx.fillStyle = "#996633";
      ctx.fillRect(b,a,25,25);
      ctx.closePath();
      }
     else if(vert>noir && vert>bleu)
      {
      ctx.beginPath();
      ctx.fillStyle = "green";
      ctx.fillRect(b,a,25,25);
      ctx.closePath();
      }
      else if(bleu>vert && bleu>noir)
      {
      ctx.beginPath();
      ctx.fillStyle = "blue";
      ctx.fillRect(b,a,25,25);
      ctx.closePath();
      }
      else {
      ctx.beginPath();
      ctx.fillStyle = "green";
      ctx.fillRect(b,a,25,25);
      ctx.closePath();
      }

  }

}

  noLoop();
}