ds_grid_fill (work in progress, wer kann helfen?)

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • ds_grid_fill (work in progress, wer kann helfen?)



      Abend,
      in meinem Spiel dessen Trailer ich erst gezeigt hab, will ich machen das sich das Wasser ausbreitet wenn es auf keinen Widerstand stößt. Wie für normale Blöcke, verwende ich auch für die Wasserblöcke einen Grid. Also habe ich Funktionen geschrieben die eine freie Fläche mit einem Wert befüllt, bis an allen Seiten ein Widerstand ist. Also wie der Farbeimer in Paint.

      Im Video könnt ihr sehen, dass das sehr lange dauert und mache Zellen nicht befüllt werden. Allerdings ist der Grid in meinem Example auch 1024 x 768 groß, pro Pixel eine Zelle. Ich denke für kleinere Grids sind meine Skripte gut brauchbar,die Präzesion kann man noch erhöhen was dann länger dauert aber eben bei kleineren Grids noch schnell genug gehen dürfte.

      DIe Funktionsweise:
      Ich habe drei Skripte geschrieben. SKript 1 füllt die Fläche mit einem Quadtrat, bis dieses am Rand anstößt. Dann werden umliegende Zellen Blockweise gefüllt wenn diese an dem Quadrat angrenzen. Im zweiten Skript mache ich das so, dass ich das feine Hauptgrid in gröbere Blöcke aufteile. Pro Block prüfe ich mit ds_grid_get_sum ob er leer ist, wenn ja befüll ich ihn. Dann werden die übrigig gebliebenen Blöcke an mein drittes Skript übergeben, dass die leeren Stellen pixelgenau füllt.

      Also ich Teile die zubefüllende Fläche quasi in Planquadrate auf wo ich schau ob ein Quadrat in der Summe dem Wert entspricht der geändert werden darf. Dann kann ich das ganze Quadrat füllen und muss nur noch die Qaudrate die übrig bleiben pixelgenau prüfen und befüllen lassen. Eigentlich wollte ich ja googlen wie so eine Fill-Funktion in Paint oder Photoshop gemacht wurde, ob es da einen tollen Algorithmus gibt. Aber ich wollte es selber lösen, bin aber mit dem Ergebnis noch unzufrieden. Große Flächen sind nicht unbedingt ein Problem, da ich das "Grobe" ja mit dem Quadrat ausfüllen lasse. Aber bei großen Flächen mit einer komplexen Form dauert das Ganze dann schon ganz schön lang.

      Vielleicht hat einer von euch ja die Idee, wie man das schneller und effektiver programmieren kann.
      Dateien
    • Hab mir dein Code mal angeschaut, du machst es etwas komisch und verwirrend.
      Du solltest am besten das Zeichnen und die Füll-Logik in unterschiedlichen Scripts machen.

      Ich hab dir mal kurz ein Floodfill geschrieben (ungetestet)
      Du kannst ja mal versuchen das zu implementieren und nachdem dieses Script ausgeführt ist, die Surface neu zu befüllen.

      GML-Quellcode

      1. /// ds_grid_floodfill(grid, x, y, old, new)
      2. var grid = argument[0];
      3. var startX = argument[1];
      4. var startY = argument[2];
      5. var valueOld = argument[3];
      6. var valueNew = argument[4];
      7. var stack = ds_stack_create();
      8. ds_stack_push(stack, stackX, stackY);
      9. while (ds_stack_size(stack) > 0)
      10. {
      11. // Nächste Position zum Checken aus dem Stack holen
      12. var _x = ds_stack_pop(stack);
      13. var _y = ds_stack_pop(stack);
      14. // Out of bounds check
      15. if (_x < 0 || _x >= ds_grid_width(grid) ||
      16. _y < 0 || _y >= ds_grid_height(grid))
      17. continue;
      18. var value = ds_grid_get(grid, _x, _y);
      19. if (value == valueOld)
      20. {
      21. ds_grid_set(grid, _x, _y, valueNew);
      22. // Nachbar position in den Stack packen
      23. ds_stack_push(stack, _x, _y - 1);
      24. ds_stack_push(stack, _x, _y + 1);
      25. ds_stack_push(stack, _x - 1, _y);
      26. ds_stack_push(stack, _x + 1, _y);
      27. }
      28. }
      29. ds_stack_destroy(stack);
      Alles anzeigen
      :saint:
    • Ah ok, mit ds_stack machst du das. Hab ich noch nie verwendet leider. Danke, ich bau das mal ein und berichte dann.

      edit: Irgendwas stimmt in deinem Script nicht, hab einen Kreis gemalt und wollte den dann ausfüllen. Dann wurde er aber nur zur Hälfte ausgefüllt und ganz wo anders im Grid wurde nochmal die Hälfte abgebildet. Ich guck mir das mal genauer an...

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Morpheus ()

    • Funzt wunderbar dein Skript, wenn man die zwei Fehler behebt die es enthält. Hast du absichtlich eingebaut um Noobs daran zu hindern einfach copy&paste zumachen? :D

      edit: Dein Skript gepaart mit mit meiner genialen Idee (bin bescheiden^^) die zufüllende Fläche erst mal mit groben Rechtecken zufüllen revolutioniert gerade zu Floodfill :D
    • Entschuldigt meinen Triplepost.

      Es gibt jetzt noch ein Problem, das ich gerne lösen würde. Wenn man kleine Flächen mit dem Skript von Tice befüllt funzt das wunderbar. Doch große Flächen dauern einfach zu lange. Es muss ja jeden einzelnen Pixel prüfen. Darum war meine Idee, die große Fläche erst grob zu füllen also mit großen Rechtecken auszufüllen. Dazu verwende ich auch sein Skript allerdings prüfe ich nicht eine Zelle sondern die Summe einer Region. Wenn die Summe des Rechtecks dem Vielfachen (also Höhe x Breite hoch 2 mal dem Wert) des Werts entspricht mache ich ds_grid_set_region.
      Das funzt auch super alleine. Nun muss ich aber die Rechtecke die übrig bleiben, also wo eine andere Farbe drin steckt pixelgenau füllen. Also muss ich die großen Zellen des "goben" Skripts an das pixelgenaue Skript übergeben.
      Und da entsteht folgendes Problem: Ich habe ja dann nicht die xy Position eines leeren Pixels sondern nur xy Position des groben Rechtecks. Ich muss also schauen wo im Rechteck leere Pixel sind und da blieb mir nichts anderes übrig als das mit 2 for-Schleifen zu machen und das dauert dann im Endeffekt wieder zu lange. Mein Code sieht so aus:

      Spoiler anzeigen

      GML-Quellcode

      1. ///ds_grid_floodfill_coarse_grained(grid, x,y, old value, new value, gridsize, fill pixel precise);
      2. var grid = argument[0];
      3. var startX = round(argument[1] / argument[5]);
      4. var startY = round(argument[2] / argument[5]);
      5. var valueOld = argument[3];
      6. var valueNew = argument[4];
      7. var stack = ds_stack_create();
      8. ds_stack_push(stack, startX, startY);
      9. while (ds_stack_size(stack) > 0)
      10. {
      11. // Nächste Position zum Checken aus dem Stack holen
      12. var _y = ds_stack_pop(stack);
      13. var _x = ds_stack_pop(stack);
      14. // Out of bounds check
      15. if (_x <= 0 || _x > ds_grid_width(grid) ||
      16. _y <= 0 || _y > ds_grid_height(grid))
      17. continue;
      18. var value = ds_grid_get_sum(grid, _x*argument[5],_y*argument[5],_x*argument[5]+argument[5]-1,_y*argument[5]+argument[5]-1);
      19. if (value == valueOld)
      20. {
      21. ds_grid_set_region(grid, _x*argument[5],_y*argument[5],_x*argument[5]+argument[5]-1,_y*argument[5]+argument[5]-1, valueNew);
      22. draw_rectangle(_x*argument[5],_y*argument[5],_x*argument[5]+argument[5],_y*argument[5]+argument[5], false);
      23. // Nachbar position in den Stack packen
      24. ds_stack_push(stack, _x, _y - 1);
      25. ds_stack_push(stack, _x, _y + 1);
      26. ds_stack_push(stack, _x - 1, _y);
      27. ds_stack_push(stack, _x + 1, _y);
      28. }
      29. else if argument[6] {
      30. // und der code hier drunter dauert einfach zu lang trotz break-Konstruktion
      31. var b = 0;
      32. for(var i=argument[5]; i>=0; i--) {
      33. if b = 1 break;
      34. for(var j=argument[5]; j>=0; j--) {
      35. if grid[# _x*argument[5]+i,_y*argument[5]+j] = valueOld {
      36. ds_grid_floodfill(grid, _x*argument[5]+i,_y*argument[5]+j, valueOld,valueNew, 1);
      37. b = 1;
      38. break;
      39. }
      40. }
      41. }
      42. }
      43. }
      44. ds_stack_destroy(stack);
      Alles anzeigen



      Und selbst wenn das schnell gehen würde besteht da noch das Problem, dass dann vielleicht eine Stelle befüllt wird die außerhalb der zu befüllenden Form liegt.
      Hat jemand eine Idee?

      edit: Habs mal so gemacht:

      Spoiler anzeigen

      GML-Quellcode

      1. ///ds_grid_floodfill_coarse_grained(grid, x,y, old value, new value, gridsize, fill pixel precise);
      2. var grid = argument[0];
      3. var startX = round(argument[1] / argument[5]);
      4. var startY = round(argument[2] / argument[5]);
      5. var valueOld = argument[3];
      6. var valueNew = argument[4];
      7. var stack = ds_stack_create();
      8. ds_stack_push(stack, startX, startY, "center");
      9. while (ds_stack_size(stack) > 0)
      10. {
      11. // Nächste Position zum Checken aus dem Stack holen
      12. var dir = ds_stack_pop(stack);
      13. var _y = ds_stack_pop(stack);
      14. var _x = ds_stack_pop(stack);
      15. // Out of bounds check
      16. if (_x <= 0 || _x > ds_grid_width(grid) ||
      17. _y <= 0 || _y > ds_grid_height(grid))
      18. continue;
      19. var value = ds_grid_get_sum(grid, _x*argument[5],_y*argument[5],_x*argument[5]+argument[5]-1,_y*argument[5]+argument[5]-1);
      20. if (value == valueOld)
      21. {
      22. ds_grid_set_region(grid, _x*argument[5],_y*argument[5],_x*argument[5]+argument[5]-1,_y*argument[5]+argument[5]-1, valueNew);
      23. draw_rectangle(_x*argument[5],_y*argument[5],_x*argument[5]+argument[5],_y*argument[5]+argument[5], false);
      24. // Nachbar position in den Stack packen
      25. ds_stack_push(stack, _x, _y - 1, "up");
      26. ds_stack_push(stack, _x, _y + 1, "down");
      27. ds_stack_push(stack, _x - 1, _y, "left");
      28. ds_stack_push(stack, _x + 1, _y, "right");
      29. }
      30. else if argument[6] {
      31. if dir = "up" {
      32. for(var i=argument[5]; i>0; i-=1) {
      33. if ds_grid_get(grid, _x*argument[5]+i,_y*argument[5]+argument[5]-1) = valueOld {
      34. ds_grid_floodfill(grid, _x*argument[5]+i,_y*argument[5]+argument[5]-1, valueOld, valueNew, 1);
      35. break;
      36. }
      37. }
      38. }
      39. else if dir = "down" {
      40. for(var i=argument[5]; i>0; i-=1) {
      41. if ds_grid_get(grid, _x*argument[5]+i,_y*argument[5]) = valueOld {
      42. ds_grid_floodfill(grid, _x*argument[5]+i,_y*argument[5], valueOld, valueNew, 1);
      43. break;
      44. }
      45. }
      46. }
      47. else if dir = "left" {
      48. for(var i=argument[5]; i>0; i-=1) {
      49. if ds_grid_get(grid, _x*argument[5]+argument[5]-1,_y*argument[5]+i) = valueOld {
      50. ds_grid_floodfill(grid, _x*argument[5]+argument[5]-1,_y*argument[5]+i, valueOld, valueNew, 1);
      51. break;
      52. }
      53. }
      54. }
      55. else if dir = "right" {
      56. for(var i=argument[5]; i>0; i-=1) {
      57. if ds_grid_get(grid, _x*argument[5],_y*argument[5]+i) = valueOld {
      58. ds_grid_floodfill(grid, _x*argument[5],_y*argument[5]+i, valueOld, valueNew, 1);
      59. break;
      60. }
      61. }
      62. }
      63. }
      64. }
      65. ds_stack_destroy(stack);
      Alles anzeigen



      Aber das dauert immernoch gefühlt genauso lange als wenn ich gleich die ganze Fläche pixelgenau befüllen lasse :(

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Morpheus ()

    • Hoffe ich krieg keinen Ärger für soviele Posts hiintereinander :D

      Also ich habs jetzt gelöst. Der Code sieht wie folgt aus:

      Spoiler anzeigen

      GML-Quellcode

      1. ///ds_grid_floodfill_coarse_grained(grid, x,y, old value, new value, gridsize, fill pixel precise);
      2. var grid = argument[0];
      3. var startX = round(argument[1] / argument[5]);
      4. var startY = round(argument[2] / argument[5]);
      5. var valueOld = argument[3];
      6. var valueNew = argument[4];
      7. var stack = ds_stack_create();
      8. var stack2 = ds_stack_create();
      9. ds_stack_push(stack, startX, startY, 0);
      10. while (ds_stack_size(stack) > 0)
      11. {
      12. // Nächste Position zum Checken aus dem Stack holen
      13. var dir = ds_stack_pop(stack);
      14. var _y = ds_stack_pop(stack);
      15. var _x = ds_stack_pop(stack);
      16. // Out of bounds check
      17. if (_x < 0 || _x >= ds_grid_width(grid) ||
      18. _y < 0 || _y >= ds_grid_height(grid))
      19. continue;
      20. var value = ds_grid_get_sum(grid, _x*argument[5],_y*argument[5],_x*argument[5]+argument[5]-1,_y*argument[5]+argument[5]-1);
      21. if (value == valueOld * power(argument[5],2))
      22. {
      23. ds_grid_set_region(grid, _x*argument[5],_y*argument[5],_x*argument[5]+argument[5]-1,_y*argument[5]+argument[5]-1, valueNew);
      24. draw_rectangle(_x*argument[5],_y*argument[5],_x*argument[5]+argument[5],_y*argument[5]+argument[5], false);
      25. // Nachbar position in den Stack packen
      26. ds_stack_push(stack, _x, _y - 1, 1);
      27. ds_stack_push(stack, _x, _y + 1, 2);
      28. ds_stack_push(stack, _x - 1, _y, 3);
      29. ds_stack_push(stack, _x + 1, _y, 4);
      30. }
      31. else
      32. {
      33. ds_stack_push(stack2, _x, _y, dir);
      34. }
      35. }
      36. if argument[6] {
      37. while (ds_stack_size(stack2) > 0)
      38. {
      39. dir = ds_stack_pop(stack2);
      40. _y = ds_stack_pop(stack2);
      41. _x = ds_stack_pop(stack2);
      42. if dir == 1 {
      43. for(var i=argument[5]; i>0; i-=1) {
      44. if ds_grid_get(grid, _x*argument[5]+i,_y*argument[5]+argument[5]-1) = valueOld {
      45. ds_grid_floodfill(grid, _x*argument[5]+i,_y*argument[5]+argument[5]-1, valueOld, valueNew, 1);
      46. break;
      47. }
      48. }
      49. }
      50. else if dir == 2 {
      51. for(var i=argument[5]; i>0; i-=1) {
      52. if ds_grid_get(grid, _x*argument[5]+i,_y*argument[5]) = valueOld {
      53. ds_grid_floodfill(grid, _x*argument[5]+i,_y*argument[5], valueOld, valueNew, 1);
      54. break;
      55. }
      56. }
      57. }
      58. else if dir == 3 {
      59. for(var i=argument[5]; i>0; i-=1) {
      60. if ds_grid_get(grid, _x*argument[5]+argument[5]-1,_y*argument[5]+i) = valueOld {
      61. ds_grid_floodfill(grid, _x*argument[5]+argument[5]-1,_y*argument[5]+i, valueOld, valueNew, 1);
      62. break;
      63. }
      64. }
      65. }
      66. else if dir == 4 {
      67. for(var i=argument[5]; i>0; i-=1) {
      68. if ds_grid_get(grid, _x*argument[5],_y*argument[5]+i) = valueOld {
      69. ds_grid_floodfill(grid, _x*argument[5],_y*argument[5]+i, valueOld, valueNew, 1);
      70. break;
      71. }
      72. }
      73. }
      74. }
      75. }
      76. ds_stack_destroy(stack);
      77. ds_stack_destroy(stack2);
      Alles anzeigen



      Hab die pixelgenaue Methode von Tice mit meiner erweiterten Methode zeitlich verglichen, mit current_time. Die erweiterte Methode ist um ein vielfaches schneller!
      Damit hab ich "Floodfill" revolutioniert!! xD
    • Ja da hast du dich nicht verguckt. Es wird nicht weniger :)
      Das könnte ich eigentlich noch einbauen, muss mal überlegen wie ich das mach.

      Es gibt bestimmt noch viele andere Anwendungsgebiete für floodfill. Mir fiel nur das mit dem Wasser ein. Viellecicht fallen euch ja noch paar ein. A* Pathfinding oder G* Pathfinding baut doch im Prinzip auf sowas auf oder nicht?