【问题标题】:C++ Win32 FPS and DeltaTime ImplementationC++ Win32 FPS 和 DeltaTime 实现
【发布时间】:2015-09-26 12:38:28
【问题描述】:

我正在尝试为我的程序 (C++ Win32) 实现 FPS 和 DeltaTime。以下是我拥有的当前代码。 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;
}

【问题讨论】:

    标签: c++ windows game-loop


    【解决方案1】:

    我之前已经回答过这个问题,但你忽略了我的 cmets。你不应该从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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-22
      • 2012-02-28
      • 2010-12-12
      • 1970-01-01
      • 1970-01-01
      • 2011-02-08
      • 1970-01-01
      相关资源
      最近更新 更多