[C#] [SourceCode] Pixelgenaue Kollisionserkennung mit Laufzeit O(1) Version 1.1

    • C#

    Es gibt 32 Antworten in diesem Thema. Der letzte Beitrag () ist von nikeee13.

      [C#] [SourceCode] Pixelgenaue Kollisionserkennung mit Laufzeit O(1) Version 1.1

      @Mods
      Es gibt leider keinen SourceCodeAustausch für "weitere Programmiersprachen", deswegen hab ich das hier reingepackt.
      Wenns doch in den normalen SourceCodeAustausch gehört, kann das ja verschoben werden.

      Beschreibung:
      Mit diesem Code hier könnt ihr Pixelgenau bestimmen, ob ein bestimmter Punkt eines Bildes ein Kollision verursacht oder nicht. Er ist vollständig kommentiert.
      Da ich LockBits in Verbindung mit Unsafe verwendet habe, kann er leider nicht ohne weiteres nach VB konvertiert werden, dafür ist das ganze um einiges schneller.
      Die Laufzeit beträgt O(1), also sie ist für beliebig große Bilder gleich schnell. Nur die Initialisierung benötigt bei größeren Bildern länger.
      Spoiler anzeigen

      C#-Quellcode

      1. /// <summary>
      2. /// Führt pixelgenaue Kollisionserkennungen für Bitmaps aus.
      3. /// </summary>
      4. public class CollisionObject
      5. {
      6. byte[] bytes;
      7. int width, height;
      8. /// <summary>
      9. /// Erstellt ein neues CollisionObject aus der angegebenen Bitmap.
      10. /// </summary>
      11. /// <param name="bmp"></param>
      12. public CollisionObject(Bitmap bmp)
      13. {
      14. //Höhe und Breite des Bildes Speichern
      15. width = bmp.Width;
      16. height = bmp.Height;
      17. //LockBits für bessere Performance
      18. var data = bmp.LockBits(new Rectangle(0,0,bmp.Width,bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
      19. //Byte-Array erstellen
      20. bytes = new byte[(width * height + 7) / 8]; //Bytes anlegen
      21. int index = 0; //Position im Array
      22. int posInByte = 0; //Position des aktuellen Bits im Byte
      23. unsafe
      24. {
      25. for (byte* alpha = (byte*)data.Scan0 + 3, lastAlpha = alpha + width * height * 4; alpha < lastAlpha; alpha += 4 )
      26. {
      27. //Alphakanal des aktuellen Pixels prüfen
      28. if (*alpha > 0)
      29. bytes[index] = (byte)(bytes[index] | (1 << posInByte)); //wenn Pixel nicht transparen, Bit auf 1 setzen
      30. posInByte++; //Bitposition erhöhen
      31. //wenn Byte voll, nächstes auswählen
      32. if (posInByte == 8)
      33. {
      34. index++;
      35. posInByte = 0;
      36. }
      37. }
      38. }
      39. bmp.UnlockBits(data); //Bild freigeben
      40. }
      41. /// <summary>
      42. /// Bestimmt, ob das Bild an einer angegebenen Stelle transparent ist oder nicht.
      43. /// </summary>
      44. /// <param name="pos"></param>
      45. /// <returns></returns>
      46. public bool PixelVisible(Point pos)
      47. {
      48. if (pos.X < 0 || pos.Y < 0 || pos.X >= width || pos.Y >= height)
      49. return false; //Punkt liegt außerhalb der Bildes
      50. //Position des Bits berechnen
      51. var bitIndex = pos.Y * width + pos.X; //Gesammtindex berechnen
      52. var byteIndex = bitIndex / 8; //Index des Bytes im Array berechnen
      53. var posInByte = bitIndex % 8; //Index des Bits im Byte berechnen
      54. //Prüfen, ob das Bit 1 ist
      55. var val = (byte)(1 << posInByte);
      56. return (bytes[byteIndex] & val) == val;
      57. }
      58. }

      Verwendung ist denke ich mal klar.
      Erst eine neue Instanz mit dem gewünschten Bild erstellen, dann kann einfach mit PixelVisible abgefragt werden, ob ein bestimmter Punkt sichtbar ist oder nicht.

      Edit Version 1.1: dank ~blaze~ konnte ich den Code noch etwas optimieren, die Initialisierung sollte nun nur noch etwa ein Drittel der alten Zeit brauchen.

      Das ganze ist übrigens durch diesen Thread entstanden.
      Dateien

      Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „Artentus“ ()

      na, das ließe sich schon auch in vb schreiben, und wäre auch nicht langsamer.
      höchstens die Initialisierung, aber die auch nur paar %.

      Wenn du ein Testprojekt anhängst (inklusive Bildle), kannichdas mal probieren.


      Wassich dir empfehlen kann: guck dir mal die Klasse BitArray an.
      Ich bezog mich auch nur auf den unsafe-Teil. Dass der Rest in VB auch geht, ist mir schon klar.
      Allerdings dürften das doch mehr als ein paar Prozent sein, denn Marshalling ist im Vergleich zu Pointern enorm langsam.

      Testprojekt kann ich schnell schreiben, schien mir halt bei dem bisschen Code nicht nötig.

      Und ja, BitArray wurde mir schon empfohlen, aber ich werde das jetzt nicht mehr umschreiben, funktioniert ja tadellos und dürfte sogar etwas schneller sein.
      Warum machst du es so, dass man es "nicht ohne weiteres" nach VB konvertieren kann? Somit ist dein Code ziemlich unnütz, solange man ihn nicht in eine .dll packt, somit könntest du diese eigentlich auch gleich als .dll veröffentlichen.
      Hi
      Marshalling wäre halt dann einfach Marshal.Copy in ein Integer-Array und dann das gleiche auf dem Integer-Array. Ist effizienter, als jedes mal einen Aufruf von Marshal.ReadInt32 oder so zu machen.
      C# wurde btw. vmtl. verwendet, weil es für diese Art der Anwendung sinnvoller ist, als VB.

      Gruß
      ~blaze~
      Hi
      hier wär' mein Ansatz:

      C#-Quellcode

      1. using System;
      2. using System.Drawing.Imaging;
      3. using System.Drawing;
      4. namespace CollisionTest
      5. {
      6. public class CollisionObject
      7. {
      8. int[] _buffer;
      9. int _length, _width;
      10. public CollisionObject(Bitmap bmp)
      11. {
      12. int width = bmp.Width;
      13. int height = bmp.Height;
      14. BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
      15. int[] outp = new int[(width * height + 31) / 32];
      16. int index = 0;
      17. unsafe
      18. {
      19. //Alphakanal ist an Offset 3
      20. for (byte* cp = (byte*)data.Scan0 + 3, targetptr = cp + width * height * 4; cp < targetptr; cp += 4, index++)
      21. if (*cp == 0)
      22. outp[index / 32] |= 1 << (index % 32); //man koennte, wen mans uebertreiben will, noch die % 32 in eine extra loop auslagern
      23. }
      24. _length = width * height;
      25. _width = width;
      26. _buffer = outp;
      27. bmp.UnlockBits(data);
      28. }
      29. public bool IsVisible(Point pos)
      30. {
      31. return IsVisible(pos.X, pos.Y);
      32. }
      33. public bool IsVisible(int x, int y)
      34. {
      35. int index = x + y * _width;
      36. if (x < 0 || y < 0 || x >= _width || index > _length)
      37. return false;
      38. return (_buffer[index / 32] & (1 << (index % 32))) == 0;
      39. }
      40. public bool this[Point pos]
      41. {
      42. get { return IsVisible(pos); }
      43. }
      44. public bool this[int x, int y]
      45. {
      46. get { return IsVisible(x, y); }
      47. }
      48. }
      49. }

      bei Artentus Code waren noch einige unnötige Konvertierungen und über Zeigerarithmetik lässt sich das schöner lösen ;). Von der Geschwindigkeit her hab' ich's nicht getestet.

      Gruß
      ~blaze~

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „~blaze~“ ()

      ...und sollte wegen Zeile 38 beim Aufruf der Funktion in Zeile 36 nem Stackoverflow erzeugen;
      wohl den falschen Overload genommen. :P

      Edit: So, jetzt stimmt's. :D
      Von meinem iPhone gesendet

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „nikeee13“ ()

      @~blaze~
      Ich hab mir deine Version mal genauer angeschaut und einiges daraus in meinen Code eingearbeitet, sind jetzt nur noch 50 gegen 90 Ticks. :P
      Wo die restlichen 40 Ticks bei dir herkommen hab ich übrigens keine Ahnung.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Artentus“ ()