Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

MCP

Alter Hase

  • »MCP« ist der Autor dieses Themas

Beiträge: 513

Wohnort: Paderborn

Beruf: Software-Entwickler

  • Private Nachricht senden

1

17.06.2009, 12:33

Relative Mauskoordinaten mit WinAPI

Hi, derzeit habe ich ein paar kleine Probleme mit einer relativen Maussteuerung für ein Spiel über die WinAPI.

Das ganze soll folgendermaßen funktionieren:
- Die Mausposition abfragen
- Die relativen Koordinaten zur Fenstermitte berechnen
- Den Mauscursor wieder in die Fenstermitte setzen

Eigentlich sollte das nicht allzu schwer sein, aber im Fenstermodus habe ich ein paar Stolpersteine.
So kann ich über WM_MOUSEMOVE die Position des Cursors innerhalb des Fensters abfragen, jedoch fangen die Schwierigkeiten beim berechnen des Offsets an: Wo liegt der Mittelpunkt des Fensters?
Diese Problem habe ich versucht zu lösen indem ich die Fensterposition bestimmt habe, die Bildschirmgröße und die Fenstergröße (mit GetWindowRect und GetClientRect). Damit lässt sich die Fenstermitte einigermaßen gut bestimmen, jedoch Fehlen da irgendwie knapp 20 Pixel die ich nicht zuordnen kann. Die Pixel für Fensterrahmen sowie den farbigen Balken kann man ausrechnen, da bin ich für rechts und links auf 8 Pixel gekommen und für oben und unten auf 47 Pixel. Der obere Rahmen ist bei mir nur 27 Pixel groß, habe ich da etwas übersehen oder warum werden 47 Pixel berechnet? Ich würde fast sagen die 20 Pixel zuviel gehören zu einem Menü, aber ich habe in dem Fenster keines.

Mit etwas Spielen mit den Werten kann ich dann zwar die Fenstermitte ziemlich präzise bestimmen, jedoch gibt es dann wieder Probleme beim setzen des Cursor in die Fenstermitte. Den Cursor setze ich mit SetCursorPos(). Diese Funktion nutzt aber Bildschirmkoordinaten. Irgendwie liege ich damit auf der Y-Achse immer knapp 10 Pixel daneben.
Gibt es eine Funktion um den Cursor auf eine Position innerhalb des Fensters zu setzen? Das würde mir unheimlich weiterhelfen...
Wenn jemand einen anderen Ansatz hat oder einen Denkfehler entdeckt bin ich gerne für Ideen offen.

Viele Grüße,
MCP

P.S.: Mit Hardgecodeten Werten funktioniert es, aber da nicht jeder den selben Skin hat (ich nutze den Classic Look unter WinXP) kommt das nicht infrage, da die Werte dann abweichend sein können.

2

17.06.2009, 12:57

Muss denn der Cursor genau in der Fenstermitte sein?
Wenns nur um die Steuerung eines Ego-Shooters o.ä. geht, reicht es doch auch aus, wenn der Cursor ungefähr in der Mitte ist.

MCP

Alter Hase

  • »MCP« ist der Autor dieses Themas

Beiträge: 513

Wohnort: Paderborn

Beruf: Software-Entwickler

  • Private Nachricht senden

3

17.06.2009, 13:20

Zentral wäre mir am liebsten, aber es ist nicht unbedingt ein muss.
Vielleicht gibt es aber auch eine Möglichkeit die Mausbewegung auf eine andere Art abzufragen. Vergleichbar mit getAsyncKeyState().

4

17.06.2009, 14:07

Mausposition abfragen:

WM_MOUSEMOVE -> lParam

GET_X_LPARAM(lParam);
GET_Y_LPARAM(lParam);

Oder via GetCursorPos und Umrechnung via ScreenToClient.


Maus im Clientbereich zentrieren:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    RECT wr;
    RECT cr;

    GetWindowRect(hwnd, &wr);
    GetClientRect(hwnd, &cr);

    int fx = GetSystemMetrics(SM_CXSIZEFRAME);
    int fy = GetSystemMetrics(SM_CYSIZEFRAME);

    int sc = GetSystemMetrics(SM_CYCAPTION);
    
    int cx = (cr.right / 2) + wr.left + fx;     // Center X

    int cy = (cr.bottom / 2) + wr.top + fy;     // Center Y


    SetCursorPos(cx, cy);
fka tm

MCP

Alter Hase

  • »MCP« ist der Autor dieses Themas

Beiträge: 513

Wohnort: Paderborn

Beruf: Software-Entwickler

  • Private Nachricht senden

5

17.06.2009, 14:20

Ah, das sieht sehr gut aus. Die Funktion ScreenToClient() kannte ich noch nicht. Werde mir das alles mal anschauen. Danke!

MCP

Alter Hase

  • »MCP« ist der Autor dieses Themas

Beiträge: 513

Wohnort: Paderborn

Beruf: Software-Entwickler

  • Private Nachricht senden

6

17.06.2009, 15:09

Mit dem Code funktioniert es bestens. Änderungen musste ich nur an Der Berechnung der Y-Achse vornehmen.
Ausserdem musste ich zu TrikkieMikkies Code noch die Rahmenhöhe mit in die Berechnung der Fenstermitte aufnehmen.
Für alle die ein ähnliches Problem haben hier kurz mein funktionierender Code:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
RECT WindowRect;
        RECT ClientRect;

        GetWindowRect(hWnd, &WindowRect);
        GetClientRect(hWnd, &ClientRect);
        //Fenstermitte ausrechnen

        int FrameX = GetSystemMetrics(SM_CXSIZEFRAME);
        int FrameY = GetSystemMetrics(SM_CYSIZEFRAME);
        
        int TitleBar = GetSystemMetrics(SM_CYCAPTION);
        
        int iWindowCenterX = (ClientRect.right / 2) + WindowRect.left + FrameX;
        int iWindowCenterY = (ClientRect.bottom / 2) + WindowRect.top + FrameY + TitleBar;

while(PeekMessage(&Message, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&Message);
            DispatchMessage(&Message);

            switch(Message.message)
            {
            case WM_MOUSEMOVE:
                int iMousePosY = HIWORD(Message.lParam);
                int iMousePosX = LOWORD(Message.lParam);
                
                //Abbruchbedingung testen

                if(Mouse->GetAlwaysCentered() && (ClientRect.bottom / 2) == iMousePosY && (ClientRect.right / 2) == iMousePosX)
                    break;              
                bMouseMovement = true;
                Mouse->SetMouseMovement(Message.lParam);
                SetCursorPos(iWindowCenterX, iWindowCenterY);
                break;
            }
        }


Danke vor allem für den Hinweis auf die Funktion GetSystemMetrics(). Die hab ich nicht mehr in Errinnerung gehabt und hätte vieles leichter gemacht... ^^"

Danke für die schnelle Hilfe.

7

17.06.2009, 15:26

Schön, dass es funktioniert! ;)

Die TitleBar-Höhe habe ich rausgelassen, weil das bei mir (Vista) nicht ganz hingehauen hat.
fka tm

MCP

Alter Hase

  • »MCP« ist der Autor dieses Themas

Beiträge: 513

Wohnort: Paderborn

Beruf: Software-Entwickler

  • Private Nachricht senden

8

17.06.2009, 15:49

Gut zu wissen. Was liefert er dir denn bei der Titelhöhe zurück? Kann es leider nicht testen da ich kein Vista habe. Werde es mir auf jedenfall schonmal notieren und bei Gelegenheit mit Win7 testen.
Immerhin nehme ich ja die Variablen damit es möglichst auf allen Systemen läuft.

9

17.06.2009, 16:09

SM_CYCAPTION = 20
Und genau soweit rutscht der Cursur zu weit nach unten.

Kann aber auch sein, dass ich irgendeinen Denkfehler drin hab.
War ein harter Tag heute... :?

Aber mit dem Code solltest du ab- und aufwärtskompatibel sein.

Was proggst du grade?
Und was ist, wenn das Fenster bewegt wird?
fka tm

MCP

Alter Hase

  • »MCP« ist der Autor dieses Themas

Beiträge: 513

Wohnort: Paderborn

Beruf: Software-Entwickler

  • Private Nachricht senden

10

17.06.2009, 16:48

Das ist ein Teil für eine 3D Engine.
Tastatursteuerung und normale Mauskoordinaten funktionieren einwandfrei, nur die Möglichkeit auch relative Mauskoordinaten zu behandeln (Ego-Shooter Steuerung) hatte bis eben noch Probleme gemacht.
Irgendwo musste ich auch einen Denk- oder Rundungsfehler drinne gehabt haben bevor ich hier nachgefragt hatte. Bis dahin hatte ich die Rahmengröße noch ausgerechnet indem ich die Fensterbreite von der Bildschirmbreite etc. abgezogen hatte. Das Ergebnis war dann die Breite der Rahmen. So lies sich aber nicht genau die Höhe für die Titelleiste etc. bestimmen da ich nur die Summe der Werte hatte. Die GetSystemMetrics funktioniert da viel präziser.

Nun ja, auf jeden Fall wollte ich mit der ganzen Mauseingabe flexibel bleiben da es eben nicht für ein bestimmtes Produkt, sondern eine Engine ist die in verschiedenen Programmen eingesetzt werden soll. DirectInput werde ich auch noch einbauen, aber nur der Vollständigkeit halber. Wenn selbst in der MSDN davon abgeraten wird für einfache Maus und Tastatursteuerung DirectInput zu verwenden wird das seinen Grund haben.
Erstmal bin ich jedenfalls mit diese Methode zufrieden. Später werde ich noch entscheiden ob ich GetCursorPos nehme oder bei der Eventverarbeitung bleibe oder beides parallel anbiete.

Bei dem Testprogramm läuft es derzeit sehr flüssig und präzise. Und das ganze funktioniert auch wenn man das Fenster verschiebt oder dessen Größe ändert. :)

Und zu guter letzt: Bei WinXP mit dem Klassik Layout ist die Titelleiste 19 Pixel groß. Wenn er Client und Window Rect auch gleich berechnet sollte das damit auch funktionieren, oder?

Werbeanzeige