Dinge mit Maus im Prog verschieben (Fenster, Bilder, ...)

Übersicht BlitzBasic FAQ und Tutorials

Neue Antwort erstellen

DivineDominion

Betreff: Dinge mit Maus im Prog verschieben (Fenster, Bilder, ...)

BeitragSa, Aug 21, 2004 17:15
Antworten mit Zitat
Benutzer-Profile anzeigen
So, heute kam wieder 'ne lustige Frage im Chat, und zwar wieso ein Bild bei der Mausbewegung nicht schnell genug mitkommt.

Das Problem lag darin, das erst auf Kollision zwischen Mauszeiger und Bild geprüft wurde und dann erst der Status von MouseDown(). Wenn das zutraf wurde die Bildposition auf MouseX() bzw. MouseY() gesetzt.

Nicht sehr edel diese Methode - und sie funktionierte auch nicht richtig.

Dank Inarie, der mir mal diese Sache mit den Mausstadien verraten hat, konnte ich da auch eine entsprechende Lösung basteln. Hier jetzt Schritt für Schritt erklärt.


Teil 1 - Das Prinzip

Drückt man die Maus über einem Bild, soll es sich so lange mitbewegen bis man loslässt.
Klignt ganz einfach.
Jetzt gibt es aber einige Formfehler, wie oben beschrieben, und einige pfiffige Lösungswege. Einer zum Beispiel ist der, die Kollision zu prüfen, wenn die Maus grade erst betätigt wird, also so was wie MouseHit( ).
Dann setzt man den Bildstatus auf "schieben" und solange die Maus gedrückt wird bewegt es sich mit.
Damit umgeht man auch das Problem, dass man andere Elemente "mitschleifen" kann. Was ich mit dem Mitgeschleife meine, erkläre ich hier kurz:
Wir haben in usnerem Programm 2 Bilder. Eines oben-links und eines unten-rechts. Wir prüfen auf Mousedown() und dann auf Kollision für beide Bilder. Nun betätigen wir das Bild in der Ecke und ziehen es nach unten-rechts indem wir die Mitte passieren. So. Wir befinden uns, mit dem einen Bild im Gepäck, auf der Fläche vom mittigen Bild. MouseDown() ist noch immer true, weil wir ja weiterhin das Bild verschieben wollen.
Somit trifft die Bedingung der Kollision und des MouseDown() zu und wir ziehen das Bild gleich mit. Haben also zwei Bilder im Gepäck, obwohl das ja eigentlich nicht so üblich ist.

Eine ElseIf-Anweisung hilft da, leider Gottes, auch nicht. Denn dann wird die oberste IF für wichtiger genommen, es kommt also auf die Reihenfolge an. Ist das Bild in der Mitte also ganz oben im IF-Block, schieben wir ab Berührungsmoment nur noch das mittige Bild mit. Die ELSEIFs werden aj ignoriert, es trifft immerhin eines zu, das anfängliche Bild bleibt also auf der Strecke.

Ich denke ihr könnt euch das alle selber zusammenprogrammierern wenn ihr nicht ganz verstanden habt was ich meine. Jedenfalls geht es ohne weiteres nicht, da muss was anderes her.
Und zwar die anfänglich erwähnte Methode: Wir prüfen nur bei einem MouseHit() auf Kollision und shcieben beim MouseDown() lediglich weiter mit. berühren wir dann mit gedrückter Maustaste das mittlere Bild, wird es nicht geprüft. MouseDown() liefert true (schieben), MouseHit() nicht (klicken & Kollision).

Und dafür gibt es diesen "Trick" den ich von Inarie habe.

Teil 2 - Mausstadien

Das was Inarie mir gezeigt hat, hat mich anfangs sehr verwirrt. Darum benutze ich hier Konstanten für die einzelnen Stadien, 4 an der Zahl, damit man den Code in Worten lesen kann und keine Ratespielchen mit Zahlen machen muss. Das mag vielleicht ein wenig blöd klingen, hilft mir aber ungemein bei der Lesbarkeit.

Hier also die vier Stadien. Welche Zahlen ihr nehmt ist egal, hauptsache der normale Status, also "Nicht-gedrückt" ist 0, damit es von Anfang an zutrifft, und das keine Zahl doppelt vorkommt. Ich habe einfach von 0 an aufwärts gezählt.

Code: [AUSKLAPPEN]
Const NORMAL = 0
Const HIT = 1
Const DOWN = 2
Const UP = 3


Normal - Nicht gedrückt, also anfänglicher Status.
Hit - MouseHit() äquivalent. Man drückt zum ersten mal die Maus. Danach, in dem nächsten Durchlauf wenn man weiterhin gedrückt hält, wechselt es zu...
Down - Das ist das MouseDown() Äquivalent. Man hält die Taste eben gedrückt.
Up - Das ist der Punkt zwischen "drücken" und "normal". Wenn man loslässt also. Sowas wie "MouseUp()" meinetwegen, gibts aber nicht Smile

Nun wechseln wir zwischen den Stadien. Die Reihenfolge ist die selbe wie in der Kosntantenreihenfolge.
Man hat die Maus normal, also ungedrückt, als Ausgangssituation. Drückt man dann, ist erst HIT aktiv. Hält man weiterhin die Taste unten setzt man es auf DOWN. Der Status bleibt solange, bis man loslässt.
Dan kommt nämlich, egal ob von HIT oder DOWN aus, der Status UP. Die Taste wurde soeben losgelassen. Das brauchen wir hier eigentlich nicht, aber der Vollständigkeit halber(und eventuellen Effekten für später zugunste)...

So, hier der Code für den Stadienwechsel.
Code: [AUSKLAPPEN]
If MausStatus = NORMAL
         
   If MouseDown( 1 ) Then MausStatus = HIT
   
ElseIf MausStatus = HIT
      
   If MouseDown( 1 ) Then MausStatus = DOWN Else MausStatus = UP
   
ElseIf MausStatus = DOWN

   If Not MouseDown( 1 ) Then MausStatus = UP
   
ElseIf MausStatus = UP
   
   MausStatus = NORMAL
      
EndIf


Nun speichern wir auch gleich die Mausposition, zum arbeiten für später:
Code: [AUSKLAPPEN]
MausX = MouseX(  )
MausY = MouseY(  )


So. Damit können wir die Aktionen jetzt abfragen!


Teil 3 - Auswirkungen auf das Bild

In diesem Beispiel haben wir einfach nur ein Bild, weil ich dann nur einfache Variablen wie BildX nehmen muss und keine Implementation für Types oder Arrays machen brauch. Faulheit eben Smile

So, zum eigentlichen Kernpunkt der Sache.
Wir haben nun die Mausstadien in der Hauptschleife oder, noch besser, in einer Funktion stehen, in der alle BEnutzerreingaben abgefangen werden. Das man den Code für z.b. eine GUI schön gliedert ist sehr wichtig, Funktionen sind ein MUSS, aber das, denke und hoffe ich, muss ich gar nicht erst sagen Smile

Die Sache ist denkbar einfach. Wenn HIT gegeben ist, prüfen wir auf Kollision und setzen, wenn die auch zutrifft, einen Status auf true. Meinetwegen BildSchieben = true. Lässt man los, muss man den Status natürlich auch zurücksetzen.

Code: [AUSKLAPPEN]
If MausStatus = HIT

   If MausX >= BildX And MausX <= BildX + BildBreite
      
      If MausY >= BildY And MausY <= BildY + BildHoehe

         BildSchieben = True
      
      EndIf
         
   EndIf

ElseIf MausStatus = UP

   BildSchieben = False
   
EndIf


Dann prüfen wir, ob BildSchieben denn true ist. Wir führen die Verschiebung NICHT direkt aus. Ich denke das so eine Trennung durchaus sinnvoll ist, damit man später bei überlappenden Fenstern noch einmal durchsortieren kann und sowas alles, wenn man es nicht gleich sinnvoll einbaut. Ich würde diese Kollisionsabfrage mit in die Funktion der Usereingaben packen und die eigentliche Verschiebung in eine Funktion, die sich um die Bilder (bzw. Fenster, wenn ihr eine GUI bastelt) dreht und deren Sachen verarbeitet und ausführt.
Sauberer Code, das ist es doch, was wir alle wollen, oder nicht? Wink

Code: [AUSKLAPPEN]
If BildSchieben = True

   BildX = BildX + ( MausX - MausXAlt )
   BildY = BildY + ( MausY - MausYAlt )
      
EndIf


MausXAlt und MausYAlt setzen wir am Ende der Hauptschleife auf die aktuelle MausX bzw. MausY. Damit können wir, wie eben gezeigt, die Differenz der Positionen vom "jetzt" zum "letzten Mal" errechnen und so anständig schieben:
Code: [AUSKLAPPEN]
MausXAlt = MausX
MausYAlt = MausY


BildX und BildY sind die Koordianten des Bildes, das wir verschieben wollen. Ihr könnt sie vorher ggf. global machen und meinetwegen auch gleich eine Statposition festlegen, das ist gleich.


Abschluss - Kompletter Beispielcode

So, das wär's dann auch wieder. Ihr müsst lediglich die Bilder malen und, wie gesagt, diesen Kram mit MausXAlt und MausYAlt ans Ende der Hauptschleife packen. Dann funktioniert mdas auch schon.
Hier ist das ganze Tutorium veranschaulicht in einem Beispiel mit top Grafik aufgeführt.

Ich hoffe ihr konntet was daraus lernen Smile

Kommentare erwünscht und verlangt Wink

Code: [AUSKLAPPEN]
Graphics 640, 480, 0, 2

;Mausgrafik erstellen und malen
Maus = CreateImage( 16, 16 )
SetBuffer ImageBuffer( Maus )
Color 160, 160, 160
Rect 0, 0, 16, 2
Rect 0, 0, 2, 16
Rect 2, 2, 12, 2
Rect 2, 2, 2, 12

;Bild erstellen & malen
Bild = CreateImage( 100, 100 )
SetBuffer ImageBuffer( Bild )
Color 60, 23, 160
Rect 0, 0, 21, 34, 1
Rect 56, 12, 44, 23, 1
Color 255, 255, 255
Rect 0, 0, 100, 100, 0

;Bildkoordinaten
BildX = 100
BildY = 100

;Bildmaße
BildBreite = ImageWidth( Bild )
BildHoehe = ImageHeight( Bild )

;Mausstadien
Const NORMAL = 0   ;Nicht gedrückt
Const HIT = 1      ;wie MouseHit(), also grade gedrückt
Const DOWN = 2      ;wie MouseDown(), also gedrückt gehalten
Const UP = 3      ;Soeben losgelassen

HidePointer

SetBuffer BackBuffer(  )

While Not KeyHit( 1 )
   
   Cls
   
   ;Aktuellen Mauskoordinaten
   MausX = MouseX(  )
   MausY = MouseY(  )
   
   ;Mausstadien wechseln   
   If MausStatus = NORMAL
      ;Von NORMAL zu HIT wechseln, wenn ma anfängt zu drücken
         
      If MouseDown( 1 ) Then MausStatus = HIT
   
   ElseIf MausStatus = HIT
      ;Von HIT zu DOWN wechseln, wenn man gedrückt hält.
      ;Von HIZ zu UP wechseln, wenn man loslässt/gelassen hat.
      
      If MouseDown( 1 ) Then MausStatus = DOWN Else MausStatus = UP
   
   ElseIf MausStatus = DOWN
      ;Von DOWN zu UP wechseln, wenn man loslässt. Sonst belassen.
      
      If Not MouseDown( 1 ) Then MausStatus = UP
   
   ElseIf MausStatus = UP
      ;Von UP zu NORMAL wechseln, dauerhaft losgelassen
      
      MausStatus = NORMAL
      
   EndIf
      
   
   ;Mausaktionen abfragen
   If MausStatus = HIT
      ;Angefangen zu drücken.
      
      ;Nur hier muss man auf "Kollision" prüfen.
      ;Wenn man hier das Bild berührt, wird man es auch weiterhin tun,
      ;weil es ja mitkommen soll.
      
      If MausX >= BildX And MausX <= BildX + BildBreite
         
         If MausY >= BildY And MausY <= BildY + BildHoehe
            
            ;=> Bild wird geschoben
            BildSchieben = True
         
         EndIf
            
      EndIf
   
   ElseIf MausStatus = UP
      ;Aufhören zu drücken
      
      ;Jetzt kann man das Bild wieder freilassen
      BildSchieben = False
      
   EndIf
   
   
   ;"Schiebung" prüfen
   If BildSchieben = True
      
      ;Differenz von der akt. Position zur letzten Position addieren
      BildX = BildX + ( MausX - MausXAlt )
      BildY = BildY + ( MausY - MausYAlt )
      
   EndIf
   
   ;Bild malen
   DrawImage Bild, BildX, BildY
   
   ;Maus malen
   DrawImage Maus, MausX, MausY
   
   Flip
   
   ;Position zum errechnen der Differenz speichern
   MausXAlt = MausX
   MausYAlt = MausY
   
Wend

End
christian.tietze@gmail.com - https://christiantietze.de
macOS

Xenon

BeitragSa, Aug 21, 2004 22:18
Antworten mit Zitat
Benutzer-Profile anzeigen
UI da hat sich ein kleiner Fehler eingeschlichen Wink

Falsch:

Code: [AUSKLAPPEN]
If MausX >= BildX And MausX <= BildX + BildBreite

If MausY >= BildY And MausY <= BildY + BildHoehe


Richtig:

Code: [AUSKLAPPEN]
If MausX >= BildX And MausX < BildX + BildBreite

If MausY >= BildY And MausY < BildY + BildHoehe

DivineDominion

BeitragSo, Aug 22, 2004 1:49
Antworten mit Zitat
Benutzer-Profile anzeigen
Ne ne, das ist schon gut so Smile Ich kann ja links und oben auch auf den Rand klicken, wieso sollte es dann rechts nicht auch gehen? Ich glaube nicht das es überhaupt jemand versucht, aber dennoch. Sonst müsste ich auch ein +1 in dne ersten Teil machen. Wozu, wenn ich das Bild nicht vergrößern kann? Dann wäre es bestimmt praktisch Smile
christian.tietze@gmail.com - https://christiantietze.de
macOS

Neue Antwort erstellen


Übersicht BlitzBasic FAQ und Tutorials

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group