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* ) ¤t_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* ) ¤t_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;
}