Laden...

Kollisionsabfrage bei scrolling mit der Maus (Mouse_Move)

Erstellt von Peter Bucher vor 17 Jahren Letzter Beitrag vor 17 Jahren 2.604 Views
Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 17 Jahren
Kollisionsabfrage bei scrolling mit der Maus (Mouse_Move)

Guten Abend

Ich habe eine kleine Tile Engine die auf GDI+ und in einem WindowsForms läuft.
Dort ist scrolling mit der Tastatur eingebaut, das funktioniert auch wunderbar.

Meine Tiles sind 32x32px und mit der Tastatur scrolle ich jeweils 8 Pixel,
so funktioniert meine "simple" Kollisionsabfrage bzw. Randkollisionsabfrage auch.

Nachträglich habe ich ein "KKND2 like" scrolling mit der Maus implementiert.
Und zwar über den Eventhandler Mouse_Move.

Ich scrolle dort genau so viel, wie mit der Maus gefahren wurde, also von einem Mouse_Move Aufruf bis zum nächsten.

Also schön flüssig mit der Maus.
Jetzt habe ich aber das Problem, das die Anzahl Pixel (Mausbewegungsdifferenz), nicht auf 8 bzw. 32 aufgeht.
Daher funktioniert meine Randkollisionsabfrage nicht mehr, ich kann weiter scrollen als die Map gross ist und irgendwan darüber hängt es, wenn man allerdings schnell mit der Maus fährt, kann man wieder weiterscrollen.

Ich frage mich wie häufig der Mouse_Move Event beim bewegen gefeuert wird?

Probiert habe ich, erst zu scrollen, wenn beide (X/Y) Koordinaten, mit 8 aufgeben... nur dann kann ich dann sozusagen nicht mehr scrollen.. denn es geht ja nie so richtig auf.

Wie würdet ihr ein solches Problem handhaben?

Hier noch ein bisschen Code, um sich das ganze besser vorzustellen.

Die Mouse Event Handlers:


        private void doubleBufferedPanel1_MouseDown(object sender, MouseEventArgs e) {
            if (e.Button == MouseButtons.Right) {
                this._mouseStartPosition = e.Location;

                this._firstXScroll = true;
                this._firstYScroll = true;

                this._mousePosition = new Point();
            }
        }

        private void doubleBufferedPanel1_MouseUp(object sender, MouseEventArgs e) {
            this._firstXScroll = false;
            this._firstYScroll = false;
        }

        private void doubleBufferedPanel1_MouseMove(object sender, MouseEventArgs e) {
            if (e.Button == MouseButtons.Right) {
                if (this._firstXScroll) {
                    this._firstXScroll = false;
                    this._map.Scroll((e.X - this._mousePosition.X) - this._mouseStartPosition.X, ScrollDirection.Right);
                } else {
                    this._map.Scroll(e.X - this._mousePosition.X, ScrollDirection.Right);
                }

                if (this._mousePosition.X > e.X) {
                    if (this._firstXScroll) {
                        this._firstXScroll = false;
                        this._map.Scroll((this._mousePosition.X - e.X) - this._mouseStartPosition.X, ScrollDirection.Left);
                    } else {
                        this._map.Scroll(this._mousePosition.X - e.X, ScrollDirection.Left);
                    }
                }
            
                if (this._mousePosition.Y < e.Y) {
                    if (this._firstYScroll) {
                        this._firstYScroll = false;
                        this._map.Scroll((e.Y - this._mousePosition.Y) - this._mouseStartPosition.Y, ScrollDirection.Top);
                    } else {
                        this._map.Scroll(e.Y - this._mousePosition.Y, ScrollDirection.Top);
                    }
                }

                if (this._mousePosition.Y > e.Y) {
                    if (this._firstYScroll) {
                        this._map.Scroll(this._mousePosition.Y - e.Y, ScrollDirection.Bottom);
                    } else {
                        this._map.Scroll(this._mousePosition.Y - e.Y, ScrollDirection.Bottom);
                    }
                }

                this.doubleBufferedPanel1.Invalidate();
                this._mousePosition = e.Location;
            }

Die Scroll Methode der Klasse "Map":
(Die "Kollisionsabfrage" ist hier realisiert, es wird einfach auf den Rand geprüft.)


        // TODO: wg. dem Mausscrolling Genauigkeit der Kollisionsabfrage der Ränder verbessern!
        public void Scroll(int scrollAmount, ScrollDirection sd) {
            switch (sd) {
                case ScrollDirection.Top:
                    if (!((this._viewPort.Y * -1) >= 512)) {
                        this._viewPort.Y -= scrollAmount; //this._scrollingCount; // 32 Pixel nach unten verschieben
                    }
                    break;
                case ScrollDirection.Bottom:
                    //if (!(this._viewPort.Y == this._viewPortSize.Height)) {
                    //if (!((this._viewPort.Y) == -((this._viewPortSize.Height * this._screenCount) * -1))) {
                    if (!((this._viewPort.Y * -1) == 0)) {
                        this._viewPort.Y +=  scrollAmount; //this._scrollingCount; // "        32 Pixel nach oben verschieben
                    }
                    break;
                case ScrollDirection.Left:
                    if (!(this._viewPort.X == 0)) {
                        this._viewPort.X += scrollAmount; //this._scrollingCount; // "        32 Pixel nach rechts verschieben
                    }
                    break;
                case ScrollDirection.Right:
                    if(!((this._viewPort.X * -1) == 560)) {
                        this._viewPort.X -= scrollAmount; //this._scrollingCount; // "        32 Pixel nach links verschieben
                    }
                    break;
            }
        }

--

Hier noch eine zusätzliche Frage.
Erläuterung: this.map.Scroll(int AnzahlPixel, Scrollrichtung);
Wenn ich eine andere Startposition (also Position beim Rechtsklick und halten der Taste) habe, muss ich diese, beim ersten Scrollvorgang, von der Anzahl Pixel die gescrollt werden, abzählen.
Dies, weil sonst die Map herumspring, sobald ich mehr als einmal scrollen möchte.

So wie der der Code hier jetzt ersichtlich ist, funktioniert es ohne Fehler.
Ich verstehe aber nicht ganz, wieso der Code bzw. ein Teil davon funktioniert 🤔

Ich muss überall die Startposition abzählen, ausser bei der letzten Abfrage nicht, weiss jemand vielleicht wieso?


                if (this._mousePosition.Y > e.Y) {
                    if (this._firstYScroll) {
                        this._map.Scroll(this._mousePosition.Y - e.Y, ScrollDirection.Bottom);
                    } else {
                        this._map.Scroll(this._mousePosition.Y - e.Y, ScrollDirection.Bottom);
                    }
                }

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

N
177 Beiträge seit 2006
vor 17 Jahren

Ich habe deine Sachen nicht so genau durchgelesen und mir vor mener Antwort auch nicht viel Bedenkzeit gelassen. Also alles unter Vorbehalt: Setze deine Map-Position einfach auf gültige Werte NACHDEM du gescrollt hast (aber bevor sie angezeigt wird). Das zweite Problem sollte es eigentlich nicht geben. Scrollst du relativ oder absolut. Wenn absolut, dann könnte das dein Problem erklären.

3.170 Beiträge seit 2006
vor 17 Jahren

Hallo,
habe jetzt nicht den ganzen Quelltext gelesen aber:

Probiert habe ich, erst zu scrollen, wenn beide (X/Y) Koordinaten, mit 8 aufgeben... nur dann kann ich dann sozusagen nicht mehr scrollen.. denn es geht ja nie so richtig auf.

Vielleicht hilft es Dir, dann zu scrollen, wenn die Differenz zwischen der Startposition des Move und der aktuellen Mausposition größer als die (bzw. kleiner als das Negative der) Tile-Größe ist.

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 17 Jahren

Hallo ihr zwei

Original von nop
Ich habe deine Sachen nicht so genau durchgelesen und mir vor mener Antwort auch nicht viel Bedenkzeit gelassen. Also alles unter Vorbehalt: Setze deine Map-Position einfach auf gültige Werte NACHDEM du gescrollt hast (aber bevor sie angezeigt wird). Das zweite Problem sollte es eigentlich nicht geben. Scrollst du relativ oder absolut. Wenn absolut, dann könnte das dein Problem erklären.

relativ, absolut, wie meinst du das?

Wenn ich es so mache, klappt es wunderbar, wenn man langsam mit der Maus fährt, wird man schneller, dann kann man aus der Map fahren, klappt die Kollisionsabfrage nicht mehr.


        private int getValidMoveAmount(int actualAmount) {
            do {
                actualAmount++;
            } while (actualAmount % 8 != 0);
            System.Diagnostics.Debug.WriteLine(actualAmount.ToString());
            return actualAmount;
        }

        private void doubleBufferedPanel1_MouseMove(object sender, MouseEventArgs e) {
            if (e.Button == MouseButtons.Right) {
                if (this._firstXScroll) {
                    this._firstXScroll = false;
                    this._map.Scroll(getValidMoveAmount((e.X - this._mousePosition.X) - this._mouseStartPosition.X), ScrollDirection.Right);
                } else {
                    this._map.Scroll(getValidMoveAmount(e.X - this._mousePosition.X), ScrollDirection.Right);
                }
// ...

Original von MarsStein
Vielleicht hilft es Dir, dann zu scrollen, wenn die Differenz zwischen der Startposition des Move und der aktuellen Mausposition größer als die (bzw. kleiner als das Negative der) Tile-Größe ist.

Hmm, ich verstehe nicht ganz, was das nützen soll.
Die Differenz der Mauspositionen ergeben für mich Anzahl Pixel zum scrollen - und diese Anzahl muss immer auf 8 aufgehen.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 17 Jahren

Ich glaube ich habs.
Man muss einfach schauen ob die jetzige viewPort Position + die Anzahl zum scrollen, gleich der Grenze oder diese überschreiten, wenn ja,
zur Grenze (zurück-) scrollen.


                    if (!((this._viewPort.Y * -1) + scrollAmount >= 512)) {
                        // Normal scrollen
                        this._viewPort.Y -= scrollAmount;
                    } else {
                        // Zum Rand (zurück-) scrollen
                        this._viewPort.Y = -512;
                    }

<edit>
Naja, doch nicht ganz.

Unten und rechts funktioniert es.
Oben auch, obwohl es manchmal ein bisschen darüber geht.
Und links hat die gleiche Formel wie oben - aber reagiert überhaupt nicht.
Wo liegt mein Denkfehler?

Die ganze Abfrage:


        public void Scroll(int scrollAmount, ScrollDirection sd) {
            switch (sd) {
                case ScrollDirection.Top:
                    if (!((this._viewPort.Y * -1) + scrollAmount >= 512)) {
                        // Normal scrollen
                        this._viewPort.Y -= scrollAmount;
                    }
                    break;
                case ScrollDirection.Bottom:
                    if (this._viewPort.Y + scrollAmount <= 0) {
                        this._viewPort.Y += scrollAmount;
                    }
                    break;
                case ScrollDirection.Left:
                    if (this._viewPort.X + scrollAmount <= 0) {
                        this._viewPort.X += scrollAmount; //this._scrollingCount; // "        32 Pixel nach rechts verschieben
                    }
                    break;
                case ScrollDirection.Right:
                    if(!((this._viewPort.X * -1) +scrollAmount >= 560)) {
                        this._viewPort.X -= scrollAmount; //this._scrollingCount; // "        32 Pixel nach links verschieben
                    }
                    break;
            }
        }

</edit>

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

3.170 Beiträge seit 2006
vor 17 Jahren

Hallo,
meine letzter Beitrag war in gewisser Weise nur ein Schuß ins Blaue, da ich KKND2 nicht kenne. Habe mir jetzt einige Screenshots angesehen, ist wohl eine Art RTS-Game?
Nun weiß ich aber leider immer noch nicht, wie genau da gescrollt wird, kannst du mich mal aufklären? Ich kenne das bei solchen Spielen so, daß das Scrolling losgeht, wenn die Maus einen bestimmten Bereich nahe dem Rand trifft. Scheint bei Dir aber anders zu sein -> was genau soll eigentlich passieren?

EDIT:
Was ich im letzten Beitrag meinte ist, wenn du die Mausbewegungsdifferenz nicht vom letzten MouseMove aus rechnest, sondern dir die Startposition des gesamten Scrollvorgangs pufferst, kannst Du diese Differenz benutzen um dann relativ zu dieser Startposition zu scrollen. Dann scrollst du um so viele Tiles wie in der Differenz enthalten sind.

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 17 Jahren

Original von MarsStein
meine letzter Beitrag war in gewisser Weise nur ein Schuß ins Blaue, da ich KKND2 nicht kenne. Habe mir jetzt einige Screenshots angesehen, ist wohl eine Art RTS-Game?
Nun weiß ich aber leider immer noch nicht, wie genau da gescrollt wird, kannst du mich mal aufklären? Ich kenne das bei solchen Spielen so, daß das Scrolling losgeht, wenn die Maus einen bestimmten Bereich nahe dem Rand trifft. Scheint bei Dir aber anders zu sein -> was genau soll eigentlich passieren?

Ja, ein Real Time Strategy Game.

  1. Ein Scrolling mit der Tastatur (Läuft ohne Probleme)

  2. Ein Scrolling mit der rechten Maustaste (gedrückt halten und mit der Maus fahren), dann solll es genau soviel scrollen wie der die Differenz vom letzten Mouse_Move ist.
    Nur weiss ich nicht, ob die Häufigkeit von Mouse_Move vielleicht eine Rolle spielen könnte.

  3. Das Scrolling bei der Berührung der Maus an den Rändern kommt zuletzt auch noch dazu. Aber zuerst muss das andere mal funktionieren.

Original von MarsStein
Was ich im letzten Beitrag meinte ist, wenn du die Mausbewegungsdifferenz nicht vom letzten MouseMove aus rechnest, sondern dir die Startposition des gesamten Scrollvorgangs pufferst, kannst Du diese Differenz benutzen um dann relativ zu dieser Startposition zu scrollen. Dann scrollst du um so viele Tiles wie in der Differenz enthalten sind.

Hmm, hört sich irgendwie logisch an.
Doch ich habe ein ViewPort, dessen Position verschoben wird, beim Scrolling,
nach einer Änderung wird neu gezeichnet.
Dann muss ich ja immer vom letzten Mouse_Move ausgehen, oder sehe ich das falsch?

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

3.170 Beiträge seit 2006
vor 17 Jahren

Hallo,

Doch ich habe ein ViewPort, dessen Position verschoben wird, beim Scrolling,
nach einer Änderung wird neu gezeichnet.
Dann muss ich ja immer vom letzten Mouse_Move ausgehen, oder sehe ich das falsch?

Ja, das siehst du falsch. Wenn du dir im Mouse_Click in einer Member-Variable Deiner Klasse die Position merkst, an der die rechte Taste gedrückt wurde, kannst Du so vorgehen, wie ich vorgeschlagen habe. Zusätzlich müsstest Du Dir dann auch die Position Deines Viewports zu Beginn des Scrollvorgangs. Du benutzt dann zur Berechnung der zu scrollenden Differenz immer den gepufferten Mouse_Click-Referenzpunkt und setzt dann denViewport relativ zu der gemerkten Viewport-Position neu.
Müßte funktionieren.

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

Peter Bucher Themenstarter:in
5.941 Beiträge seit 2005
vor 17 Jahren

Hallo

Musste eine Pause einlegen.
Leider verstehe ich wirklich nicht genau, wie du das meinst.
Kannst du das vielleicht ein bisschen ausführlicher erlären.

Ich raff das wirklich nicht X(

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

3.170 Beiträge seit 2006
vor 17 Jahren

Hallo,
hier ein Beispiel wie ichs gemeint habe, ist aber nur runtergeschrieben, nicht getestet!

Point dragStartPoint = Point.Empty;
Rectangle dragStartPort = Rectangle.Empty;

void OnMouseDown(MouseEventArgs e)
{
  // puffern wo das Draggen losging
  dragStartPoint = e.Location;
  dragStartPort = _viewPort;
}

void OnMouseUp(MouseEventArgs e)
{
  // gepufferte Punkte leeren -> dann weiß auch OnMouseMove, daß nix zu tun ist.
  dragStartPoint = Point.Empty;
  dragStartPort = Rectangle.Empty;
}

void OnMouseMove(MouseEventArgs e)
{
  if(!(dragStartPoint.IsEmpty || dragStartPort.IsEmpty))
  {
    // wie weit ist die aktuelle Mausposition von der gepufferten entfernt?
   int scrollDistanceX = dragStartPoint.X - e.Location.X;
   int scrollDistanceY = dragStartPoint.Y - e.Location.Y;
	  
   // der oben gewonnene Wert wird hier für ein 8x8 Pixel Tile normalisiert.
   int tileDistanceX = 8*(int)(scrollDistanceX / 8);
   int tileDistanceY = 8*(int)(scrollDistanceY / 8);
	  
    // _viewPort den gepufferten Wert zuweisen ...
    _viewPort = dragStartPort;
    
    // ... und um die normalisierten Werte verschieben.
    _viewPort.Offset(tileDistanceX, tileDistanceY);
    
    Invalidate();
  }
}

Der Trick dabei ist eigentlich nur, sich immer auf die ursprünglichen Werte zu Beginn der Scroll-Aktion zu beziehen.

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca