2015-09-26 101 views
0

我正在嘗試爲我的程序(C++ Win32)實現FPS和DeltaTime。以下是我現有的代碼。 FPS和DeltaTime應該以正確的方式實施。如果沒有,請告訴我如何解決我的問題。C++ Win32 FPS和DeltaTime實現

我面臨的當前問題是我應該如何使用DeltaTime。這是更新和渲染。是的,我確實使用了一個包裝類。 在我實現這個之前,我使用WindowProcedure來處理我的消息,我沒有問題。但是現在,試圖實現這一點正在讓我傾斜。所以在我使用後臺緩衝區和WM_PAINT進行繪製之前,我必須在hwnd中繪製。並且通過來自WindowProcedure的輸入進行更新,該輸入必須接受像LPARAM和WPARAM這樣的參數。但在閱讀了關於此主題的文章和論壇之後。更新和渲染是必要的,但他們並不需要在渲染方法中使用hwnd。至於更新,他們並沒有採取。

所以基本上我只想知道如何編寫更新和渲染方法?

bool BaseWindow::HandleMessages() { 

    // Counts Per Second 
    INT64 counts_per_sec = 0; 
    QueryPerformanceFrequency((LARGE_INTEGER*) &counts_per_sec); 
    // Seconds Per Count 
    float sec_per_count = 1.0f/(float) counts_per_sec; 
    // Pervious Time 
    INT64 prev_time = 0; 
    QueryPerformanceCounter((LARGE_INTEGER*) &prev_time); 

    MSG message = { 0 }; 

    if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) { 
     TranslateMessage(&message); 
     DispatchMessage(&message); 

     if (message.message == WM_QUIT) { 
      OnDestroy(); 
      return false; 
     } 
    } 
    else { 
     // Get current count 
     INT64 current_time = 0; 
     QueryPerformanceCounter((LARGE_INTEGER*) &current_time); 
     // DeltaTime 
     float delta_time = (current_time - prev_time) * sec_per_count; 

     // Update 

     // Render 
    } 
    return true; 
} 

更新 Main.cpp的

#include "BaseWindow.h" 
#include "ChildWindow.h" 

int APIENTRY WinMain(HINSTANCE h_instance, HINSTANCE h_prev_instance, LPSTR lp_cmd_line, int n_cmd_show) { 

    ChildWindow child_window(h_instance, TEXT("Child Window")); 
    BaseWindow base_window(TEXT("Base Window"), child_window.ClassName()); 

    while (base_window.HandleMessages()); 
    return 0; 
} 

AbstractWindow.h

#ifndef __ABSTRACTWINDOW_H__ 
#define __ABSTRACTWINDOW_H__ 
#pragma once 

#include <Windows.h> 

class AbstractWindow { 

    #pragma region Methods 
     public: 
      AbstractWindow(); 
      ~AbstractWindow(); 

      virtual bool Create(); 
       static LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM); 
      protected: 
       virtual LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM) = 0; 
    #pragma endregion 

    #pragma region Variables 
     protected: 
      HWND hwnd_; 
      DWORD style_ex_; 
      LPCTSTR class_name_; 
      LPCTSTR window_name_; 
      DWORD style_; 
      int x_; 
      int y_; 
      int width_; 
      int height_; 
      HWND parent_; 
      HMENU menu_; 
      HINSTANCE instance_; 

    #pragma endregion 

}; 
#endif // !__ABSTRACTWINDOW_H__ 

AbstractWindow.cpp

#include "AbstractWindow.h" 

AbstractWindow::AbstractWindow() {} 

AbstractWindow::~AbstractWindow() {} 

bool AbstractWindow::Create() { 
    // Default Create Method 

    hwnd_ = CreateWindowEx(
     style_ex_, 
     class_name_, 
     window_name_, 
     style_, 
     x_, 
     y_, 
     width_, 
     height_, 
     parent_, 
     menu_, 
     instance_, 
     this 
     ); 

    return (hwnd_ ? true : false); 

} 

LRESULT CALLBACK AbstractWindow::MessageHandler(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param) { 

    AbstractWindow* abstract_window = 0; 

    if (message == WM_NCCREATE) { 
     abstract_window = (AbstractWindow*) ((LPCREATESTRUCT(l_param))->lpCreateParams); 
     SetWindowLong(hwnd, GWL_USERDATA, long(abstract_window)); 
    } 

    abstract_window = (AbstractWindow *) (GetWindowLong(hwnd, GWL_USERDATA)); 

    if (abstract_window) { 
     return abstract_window->WindowProcedure(hwnd, message, w_param, l_param); 
    } 
    else { 
     return DefWindowProc(hwnd, message, w_param, l_param); 
    } 

} 

BaseWindow.h

#ifndef __BASEWINDOW_H__ 
#define __BASEWINDOW_H__ 
#pragma once 

#include "AbstractWindow.h" 
#include "ChildWindow.h" 

class BaseWindow : public AbstractWindow { 

    #pragma region Test Methods 
     private: 
      void Update(); 
      void Render(); 
    #pragma endregion 

    #pragma region Methods 
     public: 
      BaseWindow(); 
      ~BaseWindow(); 

      bool HandleMessages(); 
      BaseWindow(const TCHAR*, const TCHAR*); 
      void Show(); 
      virtual LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM); 
      #pragma region Handles 
       private: 
        bool CreateBackBuffer(HWND); 
        bool OnPaint(HWND); 
        bool PaintManager(); 
        bool OnDestroy(); 
      #pragma endregion 
    #pragma endregion 

    #pragma region Variables 
     private: 
      RECT window_rect_;    // Structure for window width and height 
      int client_width_; 
      int client_height_; 
      POINT mouse_pos_; 
      #pragma region Back Buffer 
        HDC hdc_;     // Handle to Device Context 
        HDC back_buffer_;   // Back Buffer 
        HBITMAP bitmap_;    // Current bitmap 
      #pragma endregion 
    #pragma endregion 

}; 
#endif // !__BASEWINDOW_H__ 

BaseWindow.cpp

#include "BaseWindow.h" 

BaseWindow::BaseWindow() {} 

BaseWindow::~BaseWindow() {} 

BaseWindow::BaseWindow(const TCHAR* window_name, const TCHAR* class_name) { 

    style_ex_ = NULL; 
    class_name_ = class_name; 
    window_name_ = window_name; 
    style_ = WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; 
    x_ = CW_USEDEFAULT; 
    y_ = CW_USEDEFAULT; CW_USEDEFAULT; 
    width_ = CW_USEDEFAULT; 
    height_ = CW_USEDEFAULT; 
    parent_ = NULL; 
    menu_ = NULL; 
    instance_ = GetModuleHandle(NULL); 

    Create(); 
    Show(); 

} 

void BaseWindow::Show() { 
    ShowWindow(hwnd_, SW_SHOW); 
    UpdateWindow(hwnd_); 
} 

LRESULT CALLBACK BaseWindow::WindowProcedure(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param) { 

    switch (message) { 
     case WM_CREATE: 
      CreateBackBuffer(hwnd); 
      return true; 
     case WM_ERASEBKGND: 
      return true; 
     case WM_DESTROY: 
      return OnDestroy(); 
     default: 
      return DefWindowProc(hwnd, message, w_param, l_param); 
    } 
} 

bool BaseWindow::HandleMessages() { 

    // Counts Per Second 
    INT64 counts_per_sec = 0; 
    QueryPerformanceFrequency((LARGE_INTEGER*) &counts_per_sec); 
    // Seconds Per Count 
    float sec_per_count = 1.0f/(float) counts_per_sec; 
    // Pervious Time 
    INT64 prev_time = 0; 
    QueryPerformanceCounter((LARGE_INTEGER*) &prev_time); 

    MSG message = { 0 }; 

    if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) { 
     TranslateMessage(&message); 
     DispatchMessage(&message); 

     if (message.message == WM_QUIT) { 
      OnDestroy(); 
      return false; 
     } 
    } 
    else { 
     // Get current count 
     INT64 current_time = 0; 
     QueryPerformanceCounter((LARGE_INTEGER*) &current_time); 
     // DeltaTime 
     float delta_time = (current_time - prev_time) * sec_per_count; 

     // Update 

     // Render 
     // I need to call for OnPaint() 
     // I need to take in hwnd 
     // But how ? 
     // Or do i not take in hwnd ? 
     // But hoe ? 

    } 
    return true; 
} 

#pragma region Handles 

bool BaseWindow::CreateBackBuffer(HWND hwnd) { 

    GetClientRect(hwnd, &window_rect_); 
    client_width_ = window_rect_.right; 
    client_height_ = window_rect_.bottom; 

    back_buffer_ = CreateCompatibleDC(NULL); // Create Back Buffer 
    hdc_ = GetDC(hwnd); // Get the Device Context 
    bitmap_ = CreateCompatibleBitmap(hdc_, client_width_, client_height_); // Create Bitmap 
    SelectObject(back_buffer_, bitmap_); // Select Bitmap 

    ReleaseDC(hwnd, hdc_); // Release 

    return true; 
} 

bool BaseWindow::OnPaint(HWND hwnd) { 

    PAINTSTRUCT paint_struct; 

    hdc_ = BeginPaint(hwnd_, &paint_struct);  // Get the Device Context 

    BitBlt(back_buffer_, 0, 0, client_width_, client_height_, NULL, NULL, NULL, WHITENESS); 

    // Paint 
    PaintManager(); 

    BitBlt(hdc_, 0, 0, client_width_, client_height_, back_buffer_, 0, 0, SRCCOPY);  // Display the back buff 
    InvalidateRect(hwnd, NULL, true);  // Repaint the screen 

    EndPaint(hwnd, &paint_struct); 

    return true; 
} 

bool BaseWindow::PaintManager() { 

    HBRUSH brush = (HBRUSH) (GetStockObject(WHITE_BRUSH)); 
    SelectObject(back_buffer_, brush);  // Select Brush 

    Rectangle(back_buffer_, 200, 200, 500, 500); 

    DeleteObject(brush); 

    return true; 

} 

bool BaseWindow::OnDestroy() { 
    PostQuitMessage(0); 
    return true; 
} 

#pragma endregion 

ChildWindow.h

#ifndef __CHILDWINDOW_H__ 
#define __CHILDWINDOW_H__ 
#pragma once 

#include "AbstractWindow.h" 
#include "BaseWindow.h" 

class ChildWindow : protected WNDCLASSEX { 

    #pragma region Methods 
     public: 
      ChildWindow(); 
      ~ChildWindow(); 

      ChildWindow(HINSTANCE, const TCHAR*); 
      bool Register(); 
      const TCHAR* ClassName() const; 
    #pragma endregion 

}; 
#endif // !__CHILDWINDOW_H__ 

ChildWindow.cpp

#include "ChildWindow.h" 

ChildWindow::ChildWindow() {} 

ChildWindow::~ChildWindow() {} 

ChildWindow::ChildWindow(HINSTANCE h_instance, const TCHAR* class_name) { 

    cbSize = sizeof(WNDCLASSEX); 
    style = NULL; 
    lpfnWndProc = AbstractWindow::MessageHandler; 
    cbClsExtra = NULL; 
    cbWndExtra = NULL; 
    hInstance = h_instance; 
    hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    hCursor = LoadCursor(NULL, IDC_ARROW); 
    //hbrBackground = (HBRUSH) (GetStockObject(DKGRAY_BRUSH)); 
    hbrBackground = (HBRUSH) NULL; 
    lpszMenuName = NULL; 
    lpszClassName = class_name; 
    hIconSm = LoadIcon(NULL, IDI_APPLICATION); 

    Register(); 

} 

bool ChildWindow::Register() { 
    return ((RegisterClassEx(this)) ? true : false); 
} 

const TCHAR* ChildWindow::ClassName() const { 
    return lpszClassName; 
} 

回答

0

我已經回答過這一點,但你忽略了我的意見。您不應該從WM_PAINT撥打InvalidateRect。您必須從OnPaint刪除InvalidateRect

您的OnPaint函數甚至沒有被調用。您必須添加以下行:

case WM_PAINT: 
    OnPaint(); 
    return 0; 

您在主消息循環中做了奇怪的事情。這非常危險。只需刪除HandleMessages。用簡單的消息循環替換它:

int APIENTRY WinMain(HINSTANCE h_instance, HINSTANCE, LPSTR, int) 
{ 
    ChildWindow child_window(h_instance, TEXT("Child Window")); 
    BaseWindow base_window(TEXT("Base Window"), child_window.ClassName()); 

    //main message loop 
    MSG msg; 
    while (GetMessage(&msg, NULL, 0, 0)) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 

    return 0; 
} 

單獨留下主消息循環。您可以稍後處理消息循環內的加速器,但這就是它。

現在讓我們說你想添加動畫。首先,讓我們從一些簡單的動畫開始。聲明一個全局變量int g_counter = 0;然後改變PaintManager像這樣:

bool BaseWindow::PaintManager() 
{ 
    HBRUSH brush = (HBRUSH)(GetStockObject(WHITE_BRUSH)); 
    SelectObject(back_buffer_, brush);  // Select Brush 
    Rectangle(back_buffer_, 200, 200, 500, 500); 
    DeleteObject(brush); 

    TCHAR buf[100]; 
    wsprintf(buf, TEXT("%d"), g_counter); 
    TextOut(back_buffer_, 0, 0, buf, strlen(buf)); 

    return true; 
} 

添加一些信號動畫:

case WM_LBUTTONDOWN: 
    animate(); 
    return 0; 

讓動畫功能:

void BaseWindow::animate() 
{ 
    for (int i = 0; i < 10000; i++) 
    { 
     g_counter++; 
     InvalidateRect(hwnd_, 0, FALSE); 

     //this code allows window to refresh itself, use it as is 
     //don't mess around with it 
     MSG msg; 
     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
     { 
      if (msg.message == WM_QUIT) 
      { 
       PostQuitMessage(0); 
       return; 
      } 
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
     } 
    } 
} 

你可以把一個計時器內部的for循環以減慢速度......如果您不需要高精度計時器,您還可以查看WM_TIMER