PixelCollision

    • VB.NET

    Es gibt 20 Antworten in diesem Thema. Der letzte Beitrag () ist von nafets3646.

      PixelCollision

      Hallo liebes Forum :),

      Ich habe ein kleine PixelCollisions System gebaut womit man auch andere Tolles sachen machen kann :)
      Sie liest Bilder ein und sucht nach nicht transparenten Farben diese werden dann zu Objecten und bilden eine Hülle :)

      Hier ist erstmal die Grundklasse :
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Public Class Collision_Object
      2. Public tcollisionlist As New List(Of Point)
      3. Public bmpsize As Size
      4. Public Location As Point
      5. Public bmp As Bitmap
      6. Public Sub New(ByVal file As String, ByVal location As Point)
      7. Me.Location = location
      8. Dim list As New List(Of Point)
      9. bmp = New Bitmap(file)
      10. bmpsize = bmp.Size
      11. For y = 0 To bmp.Height - 1
      12. For x = 0 To bmp.Width - 1
      13. If bmp.GetPixel(x, y).A >= 10 Then
      14. list.Add(New Point(x, y)) ' Wen der Pixel einen Höheren Transparents wert hat als 10 wird er der Liste Hinzugefügt.
      15. End If
      16. Next
      17. Next
      18. For i = 0 To list.Count - 1 ' ob eine leerstelle über,unter,links,rechts von im ist (es bildet eine Hülle).
      19. If list.Contains(New Point(list.Item(i).X + 1, list.Item(i).Y)) = False Then
      20. tcollisionlist.Add(list.Item(i))
      21. ElseIf list.Contains(New Point(list.Item(i).X - 1, list.Item(i).Y)) = False Then
      22. tcollisionlist.Add(list.Item(i))
      23. ElseIf list.Contains(New Point(list.Item(i).X, list.Item(i).Y + 1)) = False Then
      24. tcollisionlist.Add(list.Item(i))
      25. ElseIf list.Contains(New Point(list.Item(i).X, list.Item(i).Y - 1)) = False Then
      26. tcollisionlist.Add(list.Item(i))
      27. End If
      28. Next
      29. End Sub
      30. Public Function Collision(ByVal obj As Point) As Boolean
      31. obj.X -= Location.X 'Sie da obj auf der Form ist und die Locations unterschiedlich sind muss man die Länge/Höhe minus nehmen.
      32. obj.Y -= Location.Y
      33. Return Contains(obj)
      34. End Function
      35. Function Contains(ByVal p As Point) As Boolean
      36. If bmpsize.Width > p.X And bmpsize.Height > p.Y Then ' ob der Pixel ins Bitmap feld reinpasst
      37. For i = 0 To tcollisionlist.Count - 1
      38. If tcollisionlist.Item(i).Y > p.Y Then Return False
      39. If tcollisionlist.Item(i).Y = p.Y Then ' ob einer der Punke auf y passt.
      40. Dim last As Boolean = False
      41. Dim von As Integer = tcollisionlist.Item(i).X 'der anfang der Line.x
      42. Dim bis As Integer = 0
      43. For i2 = i To tcollisionlist.Count - 1
      44. If Not tcollisionlist.Item(i).Y = tcollisionlist.Item(i2).Y Then
      45. bis = tcollisionlist.Item(i2 - 1).X ' das ende der Linie
      46. Exit For
      47. End If
      48. Next
      49. If p.X >= von And p.X <= bis Then
      50. Dim de As Boolean = False
      51. For i2 = von To bis
      52. If tcollisionlist.Contains(New Point(i2, p.Y)) = True Then
      53. If tcollisionlist.Contains(New Point(i2 + 1, p.Y)) = False Then
      54. If de = False Then ' leerräume beachten
      55. de = True
      56. Else
      57. de = False
      58. End If
      59. End If
      60. End If
      61. If p.X = i2 Then
      62. Return de ' wen der Punkt gefunden wurde das ereigniss liefern
      63. End If
      64. Next
      65. Else
      66. Return False
      67. End If
      68. End If
      69. Next
      70. End If
      71. Return False
      72. End Function
      73. End Class


      In dieser Klasse wird die Hülle gebaut siehe hier :

      Hier sind die verwendetet Bilder :


      Und hier ist die Form :)
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Public Class Form1
      2. Dim Button666 As Collision_Object
      3. Dim Hover As Boolean = False
      4. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      5. Button666 = New Collision_Object("...Pfad zum Bild...", New Point(100, 100))
      6. Me.DoubleBuffered = True
      7. End Sub
      8. Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
      9. If Button666.Collision(e.Location) = True Then
      10. Hover = True
      11. Else
      12. Hover = False
      13. End If
      14. Me.Invalidate()
      15. End Sub
      16. Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
      17. e.Graphics.DrawImage(Button666.bmp, Button666.Location.X, Button666.Location.Y)
      18. If Hover = True Then
      19. e.Graphics.DrawRectangle(Pens.Black, New Rectangle(Button666.Location, Button666.bmpsize))
      20. End If
      21. End Sub
      22. End Class

      Diese Klasse bietet Pixelgenaue Collision die wie oben im beispiel Quellcode gezeigt eignet sie sich auch für Buttons und co :D
      würde mich über Kritik und co Freuen :)

      PS das mit dem Getpixel tut mir echt leid aber ich habe das mit dem Lockbits nicht verstanden :)

      Hier gibt es mittlerweile schon was besseres :) : [C#] [SourceCode] Pixelgenaue Kollisionserkennung mit Laufzeit O(1)
      MFG 0x426c61636b4e6574776f726b426974
      InOffical VB-Paradise IRC-Server
      webchat.freenode.net/
      Channel : ##vbparadise

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

      Also erstmal herzlichen Glückwunsch, du hast es geschafft einen Code zu produzieren, den ich nicht mal annähernd verstehe. :D

      Wenn ich aber so die ganzen Schleifen ansehe bin ich mir sicher, dass das um einiges performanter geht (nicht auf GetPixel bezogen).
      Alles, was du eigentlich bloß machen müsstes, ist dir einen Kollisionschannel für das Bild anzulegen. Da könntest du dann je 8 Pixel in einem Byte speichern (1 Bit pro Pixel) und von da an wäre es nur noch wenig Arbeit, das jeweilige Bit abzufragen. Die Vorgehensweise, die du hier verwendest, ist da eher unvorteilhaft weil kompliziert, und bei solchen Sachen kommt es auf die Geschwindigkeit an.
      hmm momentan ist der Code schon relativ schnell :)
      aber ich kucke mir das mit dem Bytes mal an :)
      MFG 0x426c61636b4e6574776f726b426974
      InOffical VB-Paradise IRC-Server
      webchat.freenode.net/
      Channel : ##vbparadise
      Edit: habs gerade ausprobiert. Dieser Code hier ist laut Stopwatch etwa um den Faktor 50 schneller als deiner (2 Ticks gegen 100 Ticks):
      Spoiler anzeigen

      C-Quellcode

      1. public class CollisionObject
      2. {
      3. byte[] bytes;
      4. int width, height;
      5. public CollisionObject(Bitmap bmp)
      6. {
      7. width = bmp.Width;
      8. height = bmp.Height;
      9. var byteList = new List<byte>();
      10. byte currentByte = 0;
      11. int posInByte = 0;
      12. for (int y = 0; y < bmp.Height; y++)
      13. for (int x = 0; x < bmp.Width; x++)
      14. {
      15. if (bmp.GetPixel(x, y).A > 0)
      16. currentByte = (byte)(currentByte | (1 << posInByte));
      17. posInByte++;
      18. if (posInByte == 8)
      19. {
      20. byteList.Add(currentByte);
      21. currentByte = 0;
      22. posInByte = 0;
      23. }
      24. }
      25. if (posInByte != 0)
      26. byteList.Add(currentByte);
      27. bytes = byteList.ToArray();
      28. }
      29. public bool PixelVisible(Point pos)
      30. {
      31. if (pos.X < 0 || pos.Y < 0 || pos.X >= width || pos.Y >= height)
      32. return false;
      33. var bitIndex = pos.Y * width + pos.X;
      34. var byteIndex = bitIndex / 8;
      35. var posInByte = bitIndex % 8;
      36. var val = (byte)(1 << posInByte);
      37. return (bytes[byteIndex] & val) == val;
      38. }
      39. }

      Ich hab es mit deinem Beispielbild von oben ausprobiert. Bei größeren Bildern sollte der Unterschied noch größer werden, da mein Arrayzugriff genauso lange dauert, deine Schleifen aber länger brauchen.

      Ist aber beides so schnell, dass man keinen Unterschied merkt. Braucht man also nur, wenns um Nanosekunden geht.

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

      Hi
      hab' den Code nur schnell überflogen, aber die Laufzeiten der Codes sind von Vornherein schon komplett anders. Artentus Code liegt in O(1) (d.h. die Emittlung der Kollision erfolgt unabhängig von der Zahl der Pixel immer gleich schnell), während der von BlackNetworkBit weit darüber sein dürfte. D.h. die Effizienz ist halt sehr viel geringer. Bei großen Bitmaps führt das dann zu einem sehr viel höheren Faktor, da eine Abhängigkeit von der Bitmapgröße eben durch eine höhere Zahl an Pixeln hervorgerufen wird.
      Man könnte übrigens bei Artentus Code direkt die BitArray-Klasse verwenden.
      @BlackNetworkBit:
      Noch eine kleine Anmerkung zur Architektur: Achte darauf, dass du eine möglichst breite Auswahl an Eingangsdaten zulässt, die die nötigen Operationen bereitstellt. Z.B. hat Artentus im Konstruktor eine Bitmap zugelassen, während du einen String erwartest, aber letztendlich mit der Bitmap weiterarbeitest. Verwende doch lieber eine statische Funktion, die dir eine solche Instanz über eine neu instanzierte Bitmap erzeugt (einfach in der statischen Funktion die Bitmap laden und an den Konstruktor der Klasse übergeben und die erzeugte Instanz zurückgeben).
      Achte außerdem darauf, dass nur Properties nach außen hin sichtbar sind und insbesondere, dass diese nicht von außen gesetzt werden können, sofern das nicht gewünscht wird.

      Gruß
      ~blaze~
      @Artentus
      Vielleicht, vielleicht :D. Ich finde es allerdings etwas unfair, dass du es mir durch die Verwendung von Features, welche in C# aber nicht in VB enthalten sind, schwieriger machst - ich muss wohl woanders an der Performance schrauben :rolleyes:.
      @Artentus
      Das kann ich halt nicht und ich habe momentan keine Lust es mit allen speziellen Mechaniken etc. zu lernen.

      Btw: Kann man eigentlich direkt IL-Code schreiben? Damit ließe sich der Vorgang sicher richtig stark verschnellern.
      @nafets3646
      Verstehen ist denke ich mal nicht das Problem.
      Ich meinte eher, darin produktiv Programme zu schreiben wird wohl doch etwas aufwändig, da du 1. keine so gute Entwicklungsumgebung hast und 2. die Sprache auf einem weit niedrigeren Level funktioniert, als VB oder C# (IL ist so ein Mischmasch aus ASM und C++ auf .Net-Basis).
      @Artentus
      Ich dachte halt, ich schreibe den Code in VB und schaue nach, ob ich im IL-Code noch Verbesserungsmöglichkeiten finde.

      PS: Kann es sein, dass dein neuer Code sogar noch langsamer als dein alter Code ist? Ich habe mal ein kleines Benchmarkprogramm geschrieben, da schneidet dein alter Code eindeutig schneller ab (Faktor ~0,89). Ich habe es mal angehängt (deine alte Klasse heißt CollisionObjectStefan, da ich sie eigentlich umschreiben wollte, aber davor einfach mal nen kleinen Testbenchmark durchführen wollte und ich dann zu faul war, es nochmal umzubenennen, da ich es ja eh bearbeiten werde).

      //EDIT: Ich Dummy habe vergessen, dass die Datei lokal geladen wird :pinch:, habe sie noch hinzugefügt.
      Bilder
      • flat_medal1.png

        1,31 kB, 41×74, 645 mal angesehen
      Dateien