0

I'm trying to implement FPS and DeltaTime for my program (C++ Win32). Below is the current code that I have. FPS and DeltaTime should have been implemented in the right manner. If not, please tell me how I may fix my problem.

The current problem I am facing is how I should use DeltaTime. That is to Update and Render. And yes I did use a wrapper class. Before I implemented this, I was using WindowProcedure to handle my messages and I no problem with that. But now, trying to implement this is tilting me. So before I was using a back buffer and WM_PAINT to draw and I had to take in hwnd in order to draw. And updated is via input from WindowProcedure which had to take in arguments like LPARAM and WPARAM But after reading articles and forums on this topic. Update and Render is needed but they didnt have to take in hwnd for the Render Method. As for update they didnt have to take that in.

So basically I just want to know how do I write the Update and Render Method?

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;
}

Update 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;
}
Wolf
  • 9,679
  • 7
  • 62
  • 108
JekasG
  • 161
  • 1
  • 5
  • 14

1 Answers1

0

I already answered this before but you ignored my comments. You should not call InvalidateRect from WM_PAINT. You must remove InvalidateRect from OnPaint.

Your OnPaint function is not even called. You must add this line:

case WM_PAINT:
    OnPaint();
    return 0;

You are doing weird things inside the main message loop. This is very dangerous. Just remove HandleMessages altogether. Replace it with a simple message loop:

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;
}

Leave the main message loop alone. You can later handle accelerators inside the message loop, but that's about it.

Now lets say you want to add animation. First, lets start with some simple animation. Declare a global variable int g_counter = 0; Then change PaintManager like so:

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;
}

Add something to signal animation:

case WM_LBUTTONDOWN:
    animate();
    return 0;

Make animation function:

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);
        }
    }
}

You can put a timer inside that for loop to slow it down... You can also look at WM_TIMER if you don't need high precision timers.

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77