Figuren Bewegen



  • Hallo,
    ich habe nun vor einiger Zeit angefangen etwas Grafik in meine Programme zu bringen.
    Ein Damespiel war relativ schnell geschrieben. Nun hänge ich aber seit einigen Tagen an einer Stelle fest, auf die ich einfach keine Antwort finde (oder einfach nur übersehe;-)

    Wie kann ich eine Figur (z.B. ein Rechteck) mit einem Tastendruck sich bewegen lassen?
    Z.B man drücke die rechte Pfeiltaste (nur einmal, also nicht gedrückt halten) und das Rechteck bewege sich 300 Pixel nach rechts, aber nicht das es einfach 300 Pixel weiter rechts erscheint (das ist ja eine leichte Sache), sondern dass es nach rechts "läuft".
    Quasi eine Schleife die ständig die Position neu berechnet und das Fenster neu zeichnet. - Nur wie setze ich das um?

    Schon mal Vielen Dank für eure Antworten.
    MFG John. 🙂



  • Du speicherst dir in einer Variable die aktuelle Richtung, in die sich das Rechteck bewegen soll. Bei einem Tastendruck setzt du sie entsprechend. In irgendeiner Hauptschleife wird nun geprüft ob die Richtung gesetzt ist. Ist sie gesetzt wird die Figur um - sagen wir mal - 1 Pixel bewegt. Ist sie genug bewegt, wird die Richtung wieder zurückgesetzt.



  • Hallo,
    erstmal Danke für deine Antwort. Genauso hatte ich es auch gemacht. 🙂
    Nur bleibt immer noch die Frage offen, wo/wie ich das Fenster aktualisieren lasse (quasi immer wenn die Figur um einen Pixel verschoben wurde)?
    Anbei mal ein Minibeispiel, InvalidateRect in die Schleife einzubauen funktioniert schon mal nicht...
    LG John.

    #include <windows.h>
    
    int left=20, top=20, right=200, bottom=200;
    HWND hwnd;
    
    void move(){
    	for(int a; a<300; a++){
    		left++; right++;
    		InvalidateRect(hwnd, NULL, TRUE);
    		//???
    	}
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    	switch(Message) {
    
    		case WM_KEYDOWN:{
    			switch(wParam){
    				case VK_RIGHT: move(); 
    					break;
    			}
    		}
    
    		case WM_PAINT:{
    
    			PAINTSTRUCT   ps;
             	HDC           hDC;
    			hDC = BeginPaint(hwnd, &ps);
    			HBRUSH hBrush;
    			hBrush = CreateSolidBrush (RGB(0,255,0));
    			SelectObject (hDC, hBrush);
    			Rectangle (hDC, left, top, right, bottom);
    			EndPaint(hwnd, &ps);
    			break;
    		}
    
    		case WM_CLOSE: {
    			DestroyWindow(hwnd);
    			break;
    		}
    
    		case WM_DESTROY: {
    			PostQuitMessage(0);
    			break;
    		}
    
    		default:
    			return DefWindowProc(hwnd, Message, wParam, lParam);
    	}
    	return 0;
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    	WNDCLASSEX wc; 
    	HWND hwnd; 
    	MSG Msg; 
    
    	memset(&wc,0,sizeof(wc));
    	wc.cbSize		 = sizeof(WNDCLASSEX);
    	wc.lpfnWndProc	 = WndProc; 
    	wc.hInstance	 = hInstance;
    	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);
    
    	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    	wc.lpszClassName = "WindowClass";
    	wc.hIcon		 = LoadIcon(NULL, IDI_APPLICATION); 
    	wc.hIconSm		 = LoadIcon(NULL, IDI_APPLICATION);
    
    	if(!RegisterClassEx(&wc)) {
    		MessageBox(NULL, "Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
    		return 0;
    	}
    
    	hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"WindowClass","Caption",WS_VISIBLE|WS_OVERLAPPEDWINDOW,
    		CW_USEDEFAULT, 
    		CW_USEDEFAULT, 
    		640,
    		480, 
    		NULL,NULL,hInstance,NULL);
    
    	if(hwnd == NULL) {
    		MessageBox(NULL, "Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
    		return 0;
    	}
    
    	while(GetMessage(&Msg, NULL, 0, 0) > 0) { 
    		TranslateMessage(&Msg); 
    		DispatchMessage(&Msg); 
    	}
    	return Msg.wParam;
    }
    


  • John1 schrieb:

    Anbei mal ein Minibeispiel, InvalidateRect in die Schleife einzubauen funktioniert schon mal nicht.

    Obwohl "funktioniert schon mal nicht" weder eine sinnvolle Fehlerbeschreibung ist und die Aussage so auch schicht falsch ist,
    sind die eigentlichen Fehler offensichtlich.

    1. HWND hwnd doppelt deklariert; lokal und global.

    2. Break für case WM_KEYDOWN fehlt.

    3. Wenn man für das Bewegen keinen Thread oder eine Timerfunktion verwendet steht man sich natürlich selbt
    auf dem Schlips.

    Und zum Schluss a ist nicht initialisiert - wieso findet Dein Compiler sowas nicht ?

    John1 schrieb:

    void move(){
    	for(int a; a<300; a++){
    		left++; right++;
    		InvalidateRect(hwnd, NULL, TRUE);
    		//???
    	}
    }
    


  • merano schrieb:

    Und zum Schluss a ist nicht initialisiert - wieso findet Dein Compiler sowas nicht ?

    Moin, weil der schlau ist und scheinbar dann mit 0 belegt^^ (Wusste ich auch noch nicht;-)

    "Funktioniert nicht" ist etwas unglücklich ausgedrückt, eher funktioniert nicht wie es soll...

    Auch wenn es nicht um Flüchtigkeitsfehler ging, ist Timerfunktion der entscheidende Denkansatz der mir fehlte.
    Von daher trotzdem Vielen Dank.
    MFG John.



  • John1 schrieb:

    "Funktioniert nicht" ist etwas unglücklich ausgedrückt, eher funktioniert nicht wie es soll...

    Das ist auch nicht besser. Du hättest ruhig schreiben können, daß keine Bewegung dargestellt wird, sondern einfach Dein Rechteck um 300 Pixel nach rechts versetzt wird.

    John1 schrieb:

    Auch wenn es nicht um Flüchtigkeitsfehler ging, ist Timerfunktion der entscheidende Denkansatz der mir fehlte.
    Von daher trotzdem Vielen Dank.
    MFG John.

    Abgesehen von der nicht initialiserten Variablen, waren Deine "Fehler" auch nicht wirklich ursächlich für Dein Problem und nicht besonders tragisch.

    Ursache für Dein Problem ist, daß InvalidateWindow nicht sofort die Nachricht WM_PAINT an Dein Fenster schickt. Erst wenn die Fensterprozedur mit der Behandlung von WM_KEYDOWN fertig ist, dann wird WM_PAINT bearbeitet. Wegen dem fehlenden break wird dieser Teil halt zweimal ausgeführt.

    Die Lösung dieses Problems könnte natürlich ein Timer sein. Ist aber meines Erachtens zu umständlich, denn es geht viel einfacher:

    void idleLoop( void )
    {
    	MSG		msg;
    
    	while( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
    	{
    		TranslateMessage( &msg );
    		DispatchMessage( &msg );
    	}
    }
    
    void move()
    {
    	for( int a=0; a<300; a++){
    		left++; right++;
    		InvalidateRect(hwnd, NULL, TRUE);
    		idleLoop();
        }
    }
    

    Mit der Funktion idleLoop gibst Du Windows die Chance WM_PAINT zu verschicken und Deine Fensterprozedur kann WM_PAINT bearbeiten bevor move() fertig ist.

    Versuche aber vielleicht InvalidateRect ohne den dritten Parameter mit TRUE aufzurufen und die WM_PAINT-Behandlung so zu schreiben, daß sie NICHT davon ausgeht, daß das Fenster schon leer ist. Dann flackert das Fenster nicht so beim Zeichnen.

    mfg Martin



  • 1 Pixel pro Frame hat das Problem, dass es von der Framerate abhängt. Und diese wird massiv schwanken und du kannst nie die Geschwindigkeit ordentlich einstellen.

    Ich wäre für sowas (ungetestet):

    struct RectAnimation{
        int startx, endx, starty, endy, starttime, endtime, rectwidth, rectheight;
    };
    
    vector<RectAnimation> animations;
    
    //Animation hinzufügen
    int now = GetTickCount();
    animations.push_back({100, 100, 200, 300, now, now + 500 /*animation dauert 500 ms*/, 150, 200});
    
    //Animation animieren
    int now = GetTickCount();
    for (const auto &a : animations){
        RECT rect;
        if (now < a.starttime)
            rect = RECT{a.startx, a.starty, a.rectwidth, a.rectheight};
        else if (now >= a.endtime)
            rect = RECT{a.endx, a.endy, a.rectwidth, a.rectheight}-,
        else{
            //man könnte auch eine andere als lineare Interpolation nehmen
    #define F(START, END) START + (now - a.starttime) * (END - START) / (a.endtime - a.starttime)
            rect = RECT{F(a.startx, a.endx), F(a.starty, a.endy), a.rectwidth, a.rectheight)};
    #undef F
        }
        FillRect(hdc, &rect, brush);
    }
    
    //TODO: alle Animationen löschen wo now > endtime
    

    Jetzt bewegt sich das Rechteck immer gleich schnell.



  • Hallo, Danke für eure Hilfe!
    PeekMasseage kannte ich noch nicht, läuft einwandfrei mit der Funktion. 👍
    @nwp3 Deine Variante schaue ich mir nochmal an, wenn ich etwas mehr Ahnung habe;-)

    mgaeckler schrieb:

    Versuche aber vielleicht InvalidateRect ohne den dritten Parameter mit TRUE aufzurufen und die WM_PAINT-Behandlung so zu schreiben, daß sie NICHT davon ausgeht, daß das Fenster schon leer ist.

    Sorry, wie genau meinst du das? (Link reicht natürlich auch)

    Die Timer-Variante habe ich auch noch einmal geschrieben, läuft auch wie sie soll, trotzdem möchte ich die einfach zur Fehleranalyse noch einmal einstellen. Vielleicht hat ja jemand die Zeit/Geduld mir zu sagen was verbesserungswürdig ist. 🙂

    #include <windows.h>
    
    bool Sprung=FALSE;
    int top=50, bottom=200, left=50, right=200, Pos=0;
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
      switch (message){
       	case WM_KEYDOWN:{
             switch (wParam)
             	{
             	case VK_RIGHT:
                	{
                   		SetTimer(hWnd, 1, 20, NULL);
                   		Sprung=TRUE;
                   		break;
                	}
             	break;
             }
          	break;
          }
       	case WM_TIMER:{
          		if(Pos>100){Pos=0; Sprung=FALSE; KillTimer(hWnd, 1);} 
    			  	else{Pos+=5; left+=5; right+=5;} 
             	InvalidateRect(hWnd, NULL, TRUE);
             	break;
          }
    
       	case WM_PAINT:{
            PAINTSTRUCT   ps;
            HDC           hDC;
    
            hDC = BeginPaint(hWnd, &ps);
    
    		HBRUSH hBrush;
    		hBrush = CreateSolidBrush (RGB(0,255,0));
    		SelectObject (hDC, hBrush);
    
    		Rectangle (hDC, left, top, right, bottom);
    
            DeleteObject(hBrush);	
            EndPaint(hWnd, &ps); 
            break;
          }
    
       	case WM_CLOSE:{
                DestroyWindow(hwnd);
                break;
          }
    
            case WM_DESTROY:{
            PostQuitMessage(0);
            break;
          }
        break;
       }
       	return DefWindowProc(hWnd, message, wParam, lParam);
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){
       MSG         msg;
       HWND        hWnd;
       WNDCLASS    wc;
    
       wc.cbClsExtra          = 0;
       wc.cbWndExtra          = 0;
       wc.hbrBackground       = (HBRUSH) GetStockObject(WHITE_BRUSH);
       wc.hCursor             = LoadCursor(NULL, IDC_ARROW);
       wc.hIcon               = LoadIcon(NULL, IDI_APPLICATION);
       wc.hInstance           = hInstance;
       wc.lpfnWndProc         = WndProc;
       wc.lpszClassName       = "WindowClass";
       wc.lpszMenuName        = NULL;
       wc.style               = CS_HREDRAW | CS_VREDRAW;
    
       RegisterClass(&wc);
    
       hWnd = CreateWindow(  "WindowClass","Caption", WS_OVERLAPPED | WS_SYSMENU, 
       							20, 20, 640, 480, NULL, NULL, hInstance, NULL);
    
       ShowWindow(hWnd, iCmdShow);
       UpdateWindow(hWnd);
    
       while (GetMessage(&msg, NULL, 0, 0)>0){
          TranslateMessage(&msg);
          DispatchMessage(&msg);
       }
       return msg.wParam;
    }
    


  • John1 schrieb:

    Hallo, Danke für eure Hilfe!
    PeekMasseage kannte ich noch nicht, läuft einwandfrei mit der Funktion. 👍
    @nwp3 Deine Variante schaue ich mir nochmal an, wenn ich etwas mehr Ahnung habe;-)

    mgaeckler schrieb:

    Versuche aber vielleicht InvalidateRect ohne den dritten Parameter mit TRUE aufzurufen und die WM_PAINT-Behandlung so zu schreiben, daß sie NICHT davon ausgeht, daß das Fenster schon leer ist.

    Sorry, wie genau meinst du das? (Link reicht natürlich auch)

    z.B. so:

    http://stackoverflow.com/questions/7502291/drawing-without-flickering

    mal schaun, ob ioch ein Beispiel bauen kann.

    mfg Martin



  • mgaeckler schrieb:

    mal schaun, ob ioch ein Beispiel bauen kann.

    mfg Martin

    So:

    case WM_PAINT:{
    
                PAINTSTRUCT   ps;
                HDC           hDC;
                hDC = BeginPaint(hwnd, &ps);
    
    			HDC memDC = CreateCompatibleDC ( hDC );
    			HBITMAP memBM = CreateCompatibleBitmap ( hDC, 640, 480 );
    			SelectObject ( memDC, memBM );
    
    			HBRUSH hBrush;
    			hBrush = CreateSolidBrush (RGB(255,255,255));
    			SelectObject (memDC, hBrush);
    			Rectangle (memDC, 0, 0, 640, 480);
    
    			hBrush = CreateSolidBrush (RGB(0,255,0));
    			SelectObject (memDC, hBrush);
    			Rectangle (memDC, left, top, right, bottom);
    			BitBlt( hDC, 0, 0, 640, 480, memDC, 0, 0, SRCCOPY );
    
    			EndPaint(hwnd, &ps);
    			break;
            }
    

    Beim Registrieren der Klasse mußt Du allerdings noch den Hintergrund weglassen:

    memset(&wc,0,sizeof(wc));
        wc.cbSize        = sizeof(WNDCLASSEX);
        wc.lpfnWndProc   = WndProc;
        wc.hInstance     = hInstance;
        wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    
    //	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    	wc.lpszClassName = "WindowClass";
    	wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    	wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
    
        if(!RegisterClassEx(&wc)) {
            MessageBox(NULL, "Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
            return 0;
        }
    

    Da ist natürlich noch genügend Platz für Optimierungen. Ist jetzt nur auf die schnelle hingerotzt, um das Prinzip zu demonstrieren.

    mfg Martin



  • mgaeckler schrieb:

    John1 schrieb:

    Auch wenn es nicht um Flüchtigkeitsfehler ging, ist Timerfunktion der entscheidende Denkansatz der mir fehlte.
    Von daher trotzdem Vielen Dank.
    MFG John.

    Abgesehen von der nicht initialiserten Variablen, waren Deine "Fehler" auch nicht wirklich ursächlich für Dein Problem und nicht besonders tragisch.

    Wenn ein Nullpointer (hwnd in move()) nicht tragisch ist, was dann ?

    @John1: Wieso trotzdem ?
    Ich habe alle Fehler, vollständig dokumentiert und einen wichtigen Hinweis für die vollständige Lösung gegeben 😕



  • Nee, Martin - setzen Sechs !!

    mgaeckler schrieb:

    case WM_PAINT:{
    	PAINTSTRUCT   ps;
    	HDC           hDC;
    	hDC = BeginPaint(hwnd, &ps);
    
    	HDC memDC = CreateCompatibleDC ( hDC );
    	HBITMAP memBM = CreateCompatibleBitmap ( hDC, 640, 480 );
    	SelectObject ( memDC, memBM );
    
    	HBRUSH hBrush;
    	hBrush = CreateSolidBrush (RGB(255,255,255));
    	SelectObject (memDC, hBrush);
    	Rectangle (memDC, 0, 0, 640, 480);
    
    	hBrush = CreateSolidBrush (RGB(0,255,0));
    	SelectObject (memDC, hBrush);
    	Rectangle (memDC, left, top, right, bottom);
    	BitBlt( hDC, 0, 0, 640, 480, memDC, 0, 0, SRCCOPY );
    
    	EndPaint(hwnd, &ps);
    	break;
            }
    

    Was soll das denn sein ?

    Bei jedem Aufruf von WM_PAINT neue HBRUSH-Objekte, Memory-DCs und Bitmaps erzeugen ist wohl
    weder intelligent noch performant.

    Dazu kommt noch das bei jedem Durchlauf groesser werdende memleak!
    http://de.wikipedia.org/wiki/Speicherleck

    When you no longer need the HBRUSH object, call the DeleteObject function to delete it.

    http://msdn.microsoft.com/en-us/library/windows/desktop/dd183518.aspx

    When you no longer need the memory DC, call the DeleteDC function.

    http://msdn.microsoft.com/en-us/library/windows/desktop/dd183489.aspx

    When you no longer need the bitmap, call the DeleteObject function to delete it.

    http://msdn.microsoft.com/en-us/library/windows/desktop/dd183488.aspx

    Edit: und was soll 640, 480 ??



  • John1 schrieb:

    Die Timer-Variante habe ich auch noch einmal geschrieben, läuft auch wie sie soll

    👍

    John1 schrieb:

    Vielleicht hat ja jemand die Zeit/Geduld mir zu sagen was verbesserungswürdig ist. 🙂

    int top=50, bottom=200, left=50, right=200, Pos=0;
    

    Einfacher wäre hier direkt ein RECT zu verwenden. Positions und Grössenänderungen wären
    dann auch einfacher.

    SetTimer(hWnd, 1, 20, NULL);
    

    Statt die 1 hart zu kodieren und danach zu ignorieren wäre es sinnvoll Namen zu vergeben und genau auf
    diesen Timer zu reagieren. Wenn es weitere geben sollte ..

    case WM_TIMER: 
        switch (wParam) { 
            case IDT_TIMER1: 
                // process the timer1
    

    Und irgendwo sollte auch

    KillTimer(hwnd, IDT_TIMER1);
    

    mal vorkommen.

    du bist ja schon drauf hingewiesen worden, das nicht immer der ganze Clientbereich gelöscht werden muss;

    InvalidateRect(hWnd, NULL, TRUE);
    

    macht aber genau das. Anschliessend muss alles neu gezeichnet werden.



  • N´abend.

    mgaeckler schrieb:

    Da ist natürlich noch genügend Platz für Optimierungen.

    Von daher werden in meinem Programm natürlich auch alle Objekte nach Benutzung gelöscht, von daher gibt es von mir eine Eins für Martins Beitrag 👍
    Funktioniert einwandfrei und das Thema Hintergrundbild was ich als nächstes angehen wollte, muss ich nicht mehr angehen, war ja gleich mit dabei...

    @merano: gekillt wird der Timer ja auch wieder direkt nachdem die Figur fertig mit der Bewegung ist (wäre ja auch ungünstig "Speicher-Leichen" zu hinterlassen):

    if(Pos>100){Pos=0; Sprung=FALSE; KillTimer(hWnd, 1);}

    Ist die Stelle falsch?

    Der neuzuzeichnende Bereich wird natürlich ausgerechnet, als RECT gespeichert und anstatt der NULL in InvalidateRect mitgegeben, war ich nur zu faul für im Beispielprogramm. 🙂

    Timer benennen und eine RECT fürs Rechteck zu verwenden sind gute Hinweise, Danke.
    Schönen Abend, John.

    Edit: Die 640, 480 entspricht meiner Fenstergröße 😉



  • merano schrieb:

    mgaeckler schrieb:

    John1 schrieb:

    Auch wenn es nicht um Flüchtigkeitsfehler ging, ist Timerfunktion der entscheidende Denkansatz der mir fehlte.
    Von daher trotzdem Vielen Dank.
    MFG John.

    Abgesehen von der nicht initialiserten Variablen, waren Deine "Fehler" auch nicht wirklich ursächlich für Dein Problem und nicht besonders tragisch.

    Wenn ein Nullpointer (hwnd in move()) nicht tragisch ist, was dann ?

    Guter Hinweis: Ich habe tatsächlich nicht gesehen, das hwnd auch noch lokal in WinMain definiert wurde. Ich dachte Du meinst die lokale Variable in WndProc. Das wäre aber kein Problem, da WM_KEYDOWN erst gesendet wird, wenn CreateWindow fertig ist. So wird InvalidateRect aber tatsächlich ein ungültiges Handle übergeben. Das Funktioniert aber trotzdem, wenn die gloabale hwnd NULL ist:

    A handle to the window whose update region has changed. If this parameter is NULL, the system invalidates and redraws all windows, not just the windows for this application, and sends the WM_ERASEBKGND and WM_NCPAINT messages before the function returns. Setting this parameter to NULL is not recommended.

    http://msdn.microsoft.com/en-us/library/windows/desktop/dd145002%28v=vs.85%29.aspx

    mfg Martin



  • merano schrieb:

    Nee, Martin - setzen Sechs !!
    ...

    Wer lesen kann, ist klar im Vorteil:

    mgaeckler schrieb:

    Da ist natürlich noch genügend Platz für Optimierungen. Ist jetzt nur auf die schnelle hingerotzt, um das Prinzip zu demonstrieren.

    mfg Martin



  • Noch ein Hinweis:

    Man sollte sich genau überlegen, wo es sich lohnt, Optimierungen zu implementieren. Wichtig beim UI Design ist, daß der Anwender hinreichend schnelles Feedback auf eine Aktion erhält. Was hinreichen schnell ist, hängt natürlich von der Anwendung ab.

    In einem Fall wie diesen, wo es darum geht, ein Damebrett zu zeichnen sollte jeder halbwegs aktuelle Rechner in der Lage sein, dies innerhalb von 0,1 Sekunden zu erledigen. Flakernde Fenster sind da viel störender, daher habe ich darauf hingewiesen.

    mfg Martin



  • mgaeckler schrieb:

    In einem Fall wie diesen, wo es darum geht, ein Damebrett zu zeichnen sollte jeder halbwegs aktuelle Rechner in der Lage sein, dies innerhalb von 0,1 Sekunden zu erledigen. Flakernde Fenster sind da viel störender, daher habe ich darauf hingewiesen.

    Wenn man das Spielfeld animiert mit 1 Pixel-Schritten ausprogrammiert kann da schon ne Menge auf
    der GUI los sein.
    Von einem konkreten Rechner auszugehen ist ungesund; letztlich weiß man nicht, welche Rechner der
    Kunde verwendet. Das Programm muß ja auch nicht unnötig langsam sein ...

    Ich würde auch nie von festen Fenstergrössen ausgehen, sondern diese immer abfragen (z.B. GetClientRect()).

    Der Nullpointer bei hwnd führt an dieser Stelle nicht zum Absturz, aber das alle Fenster neugezeichnet
    werden, war sich auch nicht gewollt. Da könnte schon mal was flackern, oder ?



  • John1 schrieb:

    @merano: gekillt wird der Timer ja auch wieder direkt nachdem die Figur fertig mit der Bewegung ist (wäre ja auch ungünstig "Speicher-Leichen" zu hinterlassen):

    if(Pos>100){Pos=0; Sprung=FALSE; KillTimer(hWnd, 1);}

    Ist die Stelle falsch?

    Die Stelle hatte ich übersehen; woran das wohl liegen könnte ? 😉



  • merano schrieb:

    mgaeckler schrieb:

    In einem Fall wie diesen, wo es darum geht, ein Damebrett zu zeichnen sollte jeder halbwegs aktuelle Rechner in der Lage sein, dies innerhalb von 0,1 Sekunden zu erledigen. Flakernde Fenster sind da viel störender, daher habe ich darauf hingewiesen.

    Wenn man das Spielfeld animiert mit 1 Pixel-Schritten ausprogrammiert kann da schon ne Menge auf
    der GUI los sein.
    Von einem konkreten Rechner auszugehen ist ungesund; letztlich weiß man nicht, welche Rechner der
    Kunde verwendet. Das Programm muß ja auch nicht unnötig langsam sein ...

    Bei der Animation magst Du durchaus recht haben. Ich würde das wahrscheinlich auch nicht über eine Redrawmessage realisieren, sondern die Animation beim move() zeichnen. Wie ich schon schrieb, das kommt halt auch einfach auf die Art der Zeichnung an.

    merano schrieb:

    Ich würde auch nie von festen Fenstergrössen ausgehen, sondern diese immer abfragen (z.B. GetClientRect()).

    Das war ja auch nur eine Demo. In einer realen Anwendung hast Du vollkommen recht.

    merano schrieb:

    Der Nullpointer bei hwnd führt an dieser Stelle nicht zum Absturz, aber das alle Fenster neugezeichnet
    werden, war sich auch nicht gewollt. Da könnte schon mal was flackern, oder ?

    Korrekt, wenn viele Fenster offen sind, dürfte das sogar heftigst flackern. Aber auch mit einem korrekten Handle war es sichtbar. Nocheinmal: das NULL-Handle haben wir ja alle bis auf Du 👍 übersehen, das muß natürlich korrigiert werden.

    mfg Martin


Anmelden zum Antworten