GDI+ Problem mit Kollision für bestimmten Bereich (Collision)

  • Hallo Community,

    ich habe bereits eine Kollisionserkennung für ein GDI+ Spiel, an dem ich noch eine ganze Weile dran sein werde, erstellt. Jedoch möchte ich meine eigene Erkennung (leider viel zu komplex und nicht gerade dynamisch) durch die Möglichkeiten mit _GDIPlus_PathIsVisiblePoint() ersetzen. Auf der Suche nach passenden Beispielen bin ich über einen Thread im englischen Forum von UEZ gestoßen, den ich nun (den Code daraus) für meine Bedürfnisse angepasst habe.

    Leider tritt auf einer bestimmten Höhe (Y Pos) für die links --> rechts Bewegung oder auch für die rechts <-- links Bewegung keine Kollision auf. Warum?

    Die Hintergründe der beiden Bilder sind nur zur Darstellung wo die Grenzen der Vierecke sind. Die eigentliche Kollision bezieht sich auf das rote Viereck. Der Player soll nicht in diesem Bereich eintreten können (Bewegung stop).

    Danke für Vorschläge, Tipps und Hilfe - ich bin dankbar!

    Sven

    Das animierte GIF sollte hoffentlich mein Problem weiter erklären:

    [Solved] Finale Lösung:
    Danke der Community.

    Code:
  • Hier mal eine zusammenfassende Stellungnahme zu dem Skript: (hoffentlich konstruktive Kritik)

    - Memoryleak bei Zeile 84 und 85. Die Grafiken müssen beim "Start" einmal geladen und beim "Ende" wieder freigegeben werden. Sie bei jedem Zeichnen neu von der Platte zu lesen und nicht wieder freizugeben verursacht ein Speicherleck.

    - Das "exit" am Ende der SetOnEvent Funktion ist "unschön". Besser ist es einen Main Loop zu haben der z.B. via "While Not $bExit" läuft. Via Event lässt sich dann das $bExit auf True umstellen, anschließend durchläuft das Programm die aktuell laufende Schleife aber noch vollständig und kann anschließend in der niedrigsten Ebene (also nicht in einer Funktion, einem Event oder sonstigem) terminieren. Zum freigeben der Ressourcen kann man entweder eine Freigabefunktion nach dem Main Loop aufrufen, oder via OnAutoItExitRegister automatisch ausführen lassen sobald das Programm beendet wird. Hier aber auch unbedingt KEIN "exit" reinschreiben.

    - Kollisionsdetektion kann über mehrere Verfahren die sehr einfach zu bewerkstelligen sind erledigt werden. Erstens wäre der Klassiker RectRectCollision oder PointRectCollision (leider wurden die schon millionenfach implementiert und haben jedes Mal einen anderen Namen), oder man benutzt eine 2te Bitmap und weist den Hindernissen explizite Farbwerte zu, diese kann man mit GDIPlusBitmapGetPixel abfragen. Mit Path Funktionen kenne ich mich leider nicht aus, da muss jemand anderes helfen...

    - Unterhalb deiner Linie wo "declaration ------------" steht wird überall Local als Keyword genutzt. Verwende an dieser Stelle lieber Global. Genauso wie bei den GDI+ Objekten die später auch alle mit Local deklariert werden. AutoIt ist zwar gegen solche "Fehler" abgesichert und interpretiert das "Local" dennoch als "Global" (da man in der niedrigsten Ebene ist), das gehört aber zum guten Stil.

    Code
    Func _Kollision_Viereck_Viereck($x1, $y1, $b1, $h1, $x2, $y2, $b2, $h2) ;Selbsterklärend
    Return ($x1 + $b1 > $x2 And $y1 + $h1 > $y2 And $x1 < $x2 + $b2 And $y1 < $y2 + $h2)
    EndFunc   ;==>_Kollision_Viereck_Viereck
    
    Func _Kollision_Viereck_Punkt($x1, $y1, $x2, $y2, $b2, $h2) ;Selbsterklärend
    Return ($x1 > $x2 And $y1 > $y2 And $x1 < $x2 + $b2 And $y1 < $y2 + $h2)
    EndFunc   ;==>_Kollision_Viereck_Punkt

    Edit1: Allgemein gibt es 2 Möglichkeiten zu verhindern, dass der Spieler über ein verbotenes Gebiet geht.

    - Die Erste ist das Prognostizieren, dass die gewünschte Bewegung nicht geht und diese verhindern bevor sie ausgeführt wird. (Man berechnet also vorher, dass eine Bewegung nicht möglich ist und führt diese dann nicht aus)

    - Als Zweites existiert die Möglichkeit jede Bewegung per sé auszuführen, egal ob sie möglich ist oder nicht. Anschließend wird festgestellt, dass sich der Spieler in einem Verbotenen Gebiet befindet und "sanft" zurückgedrängt bis er wieder wo steht wo er auch stehen darf.

    lg

    M

  • Deine 4 Punkte bleiben außerhalb des roten Rechteckes, somit kann deine Figur durchlaufen. Du musst zwei weitere Punkte prüfen, ob sie das rote Rechteck berühren.

    Beispiel Code im engl. Forum.

    Abgesehen davon würde ich nicht _GDIPlus_PathIsVisiblePoint für solche simple Objekte (Rechtecke) benutzen. Da ist eher der Check, wie es Mars gepostet hat, schneller. Aber hier musst du noch zwei weitere Punkte hinzufügen, da ansonsten die Eckpunkte außerhalb liegen.

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Hallo ihr beiden,

    vielen Dank dir Mars für deine konstruktive Kritik :) und die Hilfe an sich. Habe deine vier angesprochenen Empfehlungen berücksichtigt und meinen post #1 Code seitig aktualisiert. Ich gehe davon aus, dass dies nun so passt, wie du erwarten würdest (hoffentlich). Ehrlich gesagt muss ich zugeben, dass die Umstellung von Local zu Global für mich wenig Sinn macht, da ich bisher dachte, dass außerhalb von functions bspw. sowieso ein globaler Kontext (auch mit Local) gegeben ist - aber gut, wieder was gelernt!

    Dir natürlich ebenfalls vielen Dank UEZ . Die Variante mit _GDIPlus_PathIsVisiblePoint habe ich mir von dir im engl. Forum angeschaut, aber wie du bereits selbst erwähntest, habe ich den Gedanken dies so zu machen verworfen und die "einfacherer" Variante laut Vorschlag Mars umgesetzt. Danke aber für die Ergänzung wie es über paths funktionieren würde.

    Übringens werde ich zukünftig keine memory leaks mehr erzeugen, so wie ihr beide direkt ermahnend darauf hin gewiesen habt :D .

    Dankeschön für die Unterstützung!

    Sven

  • Die Sache mit Local und Global ist und bleibt ein Mysterium.

    Prinzipiell wird jede Variable außerhalb von Funktionen Global deklariert, egal was man davorschreibt. Und Innerhalb von Funktionen sollte man normalerweise nicht "Global" als Statement verwenden, da hier das Programm keinen zu jedem Zeitpunkt fest definierten Pool an globalen Variablen mehr hat und dem Programmierer das Leben nur unnötig schwer macht. (z.B. könntest du eine Initialisierungsfunktion schreiben die alle globalen Deklarationen enthält und diese gleich zu Beginn aufrufen. Das wird dir aber an vielen Stellen Warnungen a la "Variable possibly not declared yet" entgegenwerfen, dem muss man dann mit forcedef usw entgegenwirken). Im Regelfall löst man soetwas gekapselt über eigene Dateien. z.B. enthält die GDI+ UDF alle notwendigen globalen Variablen und wenn man ein großes Programm schreibt erstellt man auch einzelne "Module" die jeweils gewisse Programmteile beinhalten die jeweils eigene globale Variablen besitzen.

    Potential hätten diese Scopes, wenn es noch weitere Kategorien wie "Subglobal" = "Lokal außerhalb von Funktionen" oder "Superlocal" = "Global innerhalb einer Funktion" geben würde. Soetwas wurde in AutoIt aber nicht implementiert, sodass man prinzipiell auch überall "Dim" benutzen kann, dieses ist in Fuktionen automatisch lokal und außerhalb automatisch global. Für "sauberen Code" benutzt man überlicherweise aber Local in- und Global außerhalb.

    Edit1: Und noch ein kleiner Tip zur Bewegung:

    Mach dir ein Array mit [_IsPressed, ..., ..., ..] für die 4 möglichen Richtungen. Kommt ein verschachteltes Switch oder If Else um unmögliche bzw simultane Bewegungen zu berücksichtigen. z.B. ist es unsinnig nach rechts UND links zu gehen und diagonale Bewegungen brauchen einen Faktor (Wurzel 2). Weiterhin ist es sinnvoll alle Spielerrelevanten Daten in ein Array zu packen und die Indizes via Enum zu deklarieren. Anbei ist auch ein Beispiel was ich meine (OBACHT: Ich bin mir ziemlich sicher, dass man diese unendlich verschachtelten If Abfragen irgendwie effizienter anordnen kann, aber es funktioniert schonmal :) )

  • Hallo Mars,

    vielen Dank für die Erläuterungen :) . Was den Scope angeht verhält sich AutoIt wirklich etwas seltsam, im Vergleich mit anderen Sprachen, aber ich kann damit leben. Sehr gut war auch der Tipp mit den diagonalen Bewegungen mittels Sqrt( 2 ). Habe ich in meinem Game gleich untergebracht. Die Fragen die ich hier zu GDI+ usw. stelle, sind zumeist kleine Bausteine auf dem Weg ein Spiel sinnvoll, refaktorisiert und erweiterbar zu erstellen. Wird mich sicherlich in Zukunft auch noch eine ganze Weile beschäftigen.

    Merci auch für den Tipp mit dem Enums. Mir war nicht bewusst, dass ich solche in AutoIt verwenden kann bzw. das es sie gibt - super! Was die Verschachtelung angeht (auf dein Beispiel bezogen) kraust es micht schon ein wenig ;) , jedoch sehe ich mir es mal nach und nach durch und refaktorisiere so gut ich kann. Die größte Hürde ist mein Anspruch die meisten Objekte dynamisch aufzubauen, zu laden und sie zu nutzen ... Assign(), Eval(), Execute() etc.

    Ich komme sicherlich demnächst wieder mit einer Frage um die Ecke - aber falls dieses Projekt irgendwann mal zeigbar sein wird, steht Mars, UEZ und noch ein paar weitere Helfende in den Credits!

    Danke für die Unterstützung und die Erläuterungen.
    Sven