Avatar billede anadan Nybegynder
07. november 2002 - 08:23 Der er 22 kommentarer og
1 løsning

Messageloop og animering

Jeg har lavet en animering med DC
HDC hdc...
HBRUSH hbrush...
while(true){
paint(hdc, hbrush, ...);
Sleep(100);
}
Når jeg så trykker på en key, eller bevæger musen, stopper animationen, hvorfor?
Der er ikke defineret nogen Sleep i mit messageloop, og hvis jeg sætter ovenstående sleep til 0, virker det fint, men animeringen er for hurtig.
Avatar billede jpk Nybegynder
07. november 2002 - 08:47 #1
Hvor har du placeret ovenstående kode i dit program?
Avatar billede anadan Nybegynder
07. november 2002 - 08:52 #2
Den står i min winmain

mens håndtering af keyevents står i WndProc
switch(iMes){
case WM_KEYDOWN:{
    int keyCode = lParam;
    if(!gamePaused){
        if(keyCode==DOWN){
            bool done = false;
            while(!done){
                if(tryToMove(DOWN)){}
                else done = true;
            }
        }
        else if(keyCode==LEFT||keyCode==RIGHT){
            tryToMove(keyCode);
            }
Der er ingen Sleep i nogen af de metoder der bliver kaldt
Avatar billede jpk Nybegynder
07. november 2002 - 09:04 #3
Prøv at poste koden til din winmain, så jeg ved præcis hvad der foregår.

Det er generelt en forkert måde at håndtere animering på, Din kode bliver jo ved med at køre så længe den får CPU-tid, pga. din "while(true)"!
Der er flere problemer herved:
Din message procedure får ikke noget CPU-tid!
Sleep(100) gør at dit program afgiver den resterende tid af sin time-slice på CPU'en...
Avatar billede anadan Nybegynder
07. november 2002 - 09:08 #4
ok, her er winmain
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){
    static TCHAR szAppName[] = TEXT ("RandRect") ;
    HWND        hwnd ;
    MSG          msg ;
    WNDCLASS    wndclass ;

    wndclass.style        = CS_HREDRAW | CS_VREDRAW ;
    wndclass.lpfnWndProc  = WndProc ;
    wndclass.cbClsExtra    = 0 ;
    wndclass.cbWndExtra    = 0 ;
    wndclass.hInstance    = hInstance ;
    wndclass.hIcon        = LoadIcon (NULL, IDI_APPLICATION) ;
    wndclass.hCursor      = LoadCursor (NULL, IDC_ARROW) ;
    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
    wndclass.lpszMenuName  = NULL ;
    wndclass.lpszClassName = szAppName ;
   

    if (!RegisterClass (&wndclass)){
          MessageBox (NULL, TEXT("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;
          return 0 ;
    }
   
    hwnd = CreateWindow (szAppName,
                          TEXT ("Tetris In C++"), //title
                          WS_OVERLAPPEDWINDOW,
                          50, 50,                        //x, y
                          700, 500,                        //width, height
                          NULL, NULL, hInstance, NULL) ;
   
    ShowWindow (hwnd, iCmdShow) ;                        //set visible
    UpdateWindow (hwnd) ;
    HDC hdc = GetDC(hwnd);                                //get the handle to device context
    RECT clear;                                        //create rect for clearing
    SetRect(&clear, 0,0,700,500);

    HBRUSH black = CreateSolidBrush (RGB (0, 0, 0)) ;        //create black brush
    HBRUSH white = CreateSolidBrush (RGB (255, 255, 255)) ;//create white brush
    HBRUSH blue = CreateSolidBrush (RGB (0, 0, 255)) ;//create white brush
    HBRUSH green = CreateSolidBrush (RGB (0, 255, 0)) ;//create white brush

    hdcBuffer = CreateCompatibleDC(hdc);
    hbmBuffer = CreateCompatibleBitmap(hdc, 700, 500);
    gamePaused = false;

    block = ts.newBlock(random(7));
    cpy(block.block, nextBlockArray);
    createBlock();

    while (true)    //animation
    {
        if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)){
              if (msg.message == WM_QUIT)
                    break ;
              TranslateMessage (&msg) ;
              DispatchMessage (&msg) ;
          }   
        else{
            while(!gamePaused){
                if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)){
                      if (msg.message == WM_QUIT)
                            break ;
                      TranslateMessage (&msg) ;
                      DispatchMessage (&msg) ;
                  }
                  else{
                    move();                        
                    repaint(hdc, &clear, white, black, blue, green);                   
                  }
                Sleep(100);                //wait     
            }
            repaint(hdc, &clear, white, black, blue, green);
            Sleep(100);                //wait
        }
    }
    DeleteObject(black) ;            //delete black brush
    DeleteObject(white) ;            //delete white brush
    DeleteObject(blue);
    DeleteObject(green);
    ReleaseDC (hwnd, hdc) ;        //release the handle to the device context
    return msg.wParam ;
Avatar billede jpk Nybegynder
07. november 2002 - 09:17 #5
Hvorfor har du gentaget noget af koden..?
2 x PeekMessage
2 x TranslateMessage
2 x DispatchMessage
2 x repaint
2 x Sleep

osv...
Avatar billede anadan Nybegynder
07. november 2002 - 09:20 #6
Fordi man i spillet skal kunne sætte det på pause, men der skal stadig ske en håndtering af ovennævnte, og den vil ikke gå ind i det inderste while, hvis gamePaused
Avatar billede jpk Nybegynder
07. november 2002 - 09:34 #7
Jamen i din while(true) håndterer du først alle messages der ligger i køen, så går du videre (else) og så gør du nøjagtig det samme igen!
Det giver ingen mening!!!
Avatar billede anadan Nybegynder
07. november 2002 - 09:38 #8
ok, det var noget kode jeg fandt et eller andet sted, jeg må indrømme at jeg ikke helt forstår hvad det gør, men da der virker bruger jeg det bare (newbie alert!), måske kunne jeg fjerne den ene af dem på en eller anden måde vha en bool?
Avatar billede jpk Nybegynder
07. november 2002 - 09:47 #9
Hvad med noget lign.:

while(true)
{
    while(PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) // First, handle all messages
    {
          if(msg.message == WM_QUIT)
                break;
          TranslateMessage(&msg);
          DispatchMessage(&msg);
    }

    // No more messages

    if(!gamePaused) // If we are not paused handle the movement
        move();

    // Render the next frame
    repaint(hdc, &clear, white, black, blue, green);                   
}
Avatar billede anadan Nybegynder
07. november 2002 - 10:08 #10
ja det virker fint, nu er mit problem bare at der sker håndtering-tegning-håndtering-tegning-håndtering, ..., . Jeg gik ud fra af håndteringen sker i en Thread ligesom i Java, måske skulle jeg prøve det?
Avatar billede mjohansen Nybegynder
07. november 2002 - 10:17 #11
Hvordan er det et problem???
Den måde jpk beskriver, er en meget almindelig måde at styre animation i windows. Jeg har selv tidligere lavet et spil med DirectX der kørte på den måde, uden problemer.
Avatar billede anadan Nybegynder
07. november 2002 - 10:23 #12
jeg er ved at lave tetris, men problemet er at hvis jeg trykker venstre 5 gange, så rykker min brik til venstre 5 gange, men i løbet af at den falder 5. Man skulle gerne kunne rykke hurtigere hen, end ned. Men hvis det er den måde der sker på, ja så point til jpk
Avatar billede anadan Nybegynder
07. november 2002 - 10:24 #13
Hvis han da vil give et svar :-)
Avatar billede mjohansen Nybegynder
07. november 2002 - 10:26 #14
Jamen, hvis brikken skal falde ned hver gang den løber gennem move og repaint, så må den da falde super hurtigt ned?!?!?!? Du må vist arbejde lidt med timingen i din move.
Avatar billede anadan Nybegynder
07. november 2002 - 10:29 #15
Der er håndtering af Sleep, det kan du også se i min kode længere oppe, jeg tror bare jpk glemte den i løsningen, men det er ikke noget problem
Avatar billede mjohansen Nybegynder
07. november 2002 - 10:31 #16
Ja, glem alt om sleep! Du kan evt. bruge GetTickCount til at måle hvor tit du skal flytte en brik.
Avatar billede jpk Nybegynder
07. november 2002 - 11:19 #17
Jeg glemte IKKE Sleep!
Det er en dårlig måde at håndtere framerate på!
Der er 2 typiske måder at gøre det på:

1) Du ønsker en fast frame rate og tjekker altså tiden hver gang du er klar til at rendere næste frame. Hvis der er gået tilstrækkelig tid siden sidste frame, renderer du framen, ellers laver du ingenting.
Denne metode er dårlig hvis maskinen ikke kan levere den ønskede framerate, da spillet kommer til at køre langsommere...

2) Du renderer en frame så snart du kan, men bruger tiden siden sidste frame til at beregne hvor meget objekterne skal flyttes.
Denne metode giver en ensarted hastighed uanset hvor hurtig maskinen er, men der er også lidt mere arbejde i den...
Avatar billede jpk Nybegynder
07. november 2002 - 11:22 #18
Du kan godt vælge at indsætte en Sleep(0) for hver gennemgang af loopet, det vil få Windows til at starte eventuelle processer der har højere prioritet og er klar til at køre...

Anvender du Sleep med et andet tal, vil din process rent faktisk blive suspenderet i MINIMUM det antal millisekunder!
Med Sleep(100) bliver din maksimale frame rate jo 10fps, altså ret dårlig..!
Avatar billede anadan Nybegynder
07. november 2002 - 11:26 #19
Der talte jeg vist lidt over mig der, men tak for jeres svar
Avatar billede jpk Nybegynder
07. november 2002 - 11:53 #20
Velbekomme, skulle det være en anden gang...
Avatar billede soepro Nybegynder
11. november 2002 - 09:35 #21
humlen må vel være at der skal sættes et forhold op mellem hastigheden på at brikken falder og på at brugeren flytter rundt på dem. Det kunne f.eks. gøres sådan her:

const int userActionFactor = 10; // Brugeren skal kunne flytte 10 gange så hurtigt som maskinen.
int  actionCount = 0;

while (true)
{
  // Process messages = user interaction
  if (peek_message(....))
  {
  };

  // Anitmate only when user has had the chance to move.
  if (++actionCount >= userActionFactor)
  {
    move():
    redraw();
    actionCount = 0;
  };
}; // Indtil break.

jpk >> Hvordan kan din metoden 2) køre hurtigere end 1) - du måler da tiden i begge, så forskellen må da være den samme - ingen af metoderne leverer en bedre framerate end hvad maskinen kan !

Humlen er vel snarere at man skal sørge for at BEVÆGELSERNE ikke for hurtige til at brugeren kan nå at reagere på dem. Hvis et objekt f.eks. skal flyttes 500 pixels og det skal minimum tage 3 sekunder - ja så må den altid ikke flytte sig mere end 500/3000 = 1/6 pixels pr. millisekund. Dermed skal du bare beregne dit move() som antal ms siden sidste frame * 1/6. (Men det var måske også det du mente.) Det betyder også at såfremt din maskine yder godt nok, så kan du have 100 gennemløb inden for det samme millisekund - og dermed 600 gennemløb, hvor du ikke skal lave noget, fordi objektet står på samme sted !

Hermed vil brugeren have 3 sekunder til at flytte på plads - hvor mange "chancer" det svarer til, afhænger så at maskinens formåen.
Avatar billede jpk Nybegynder
11. november 2002 - 09:43 #22
soepro >> At ingen af metoderne leverer en bedre framerate end hvad maskinen kan, siger vist sig selv...

Prøv at læse de to metoder igen og vis du stadig ikke er med på hvorfor metode 2 kan give en højere frame rate end metode 1, så skal jeg gerne omformulere.
Avatar billede soepro Nybegynder
11. november 2002 - 11:41 #23
jpk >> Framerate er vel antal skift (dvs. ændringer) i skærmbilledet eller ? Er max så ikke under alle omstændigheder 6/1000 ms. i mit eksempel, dvs. 1 pixels flytning ?
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester