I am using this website to learn DirectX: http://www.rastertek.com/tutdx10.html I am trying to use their DirectInput tutorial to add more keybindings besides Escape. However, I cannot get my keys to fire. Here is the relevant code:
////////////////////////////////////////////////////////////////////////////////
// Filename: inputclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "inputclass.h"
InputClass::InputClass()
{
m_directInput = 0;
m_keyboard = 0;
m_mouse = 0;
}
InputClass::InputClass(const InputClass& other)
{
}
InputClass::~InputClass()
{
}
bool InputClass::Initialize(HINSTANCE hinstance, HWND hwnd, int screenWidth, int screenHeight)
{
HRESULT result;
//Store the screen sizew hcih will be used for positioning the mouse cursor.
m_screenWidth = screenWidth;
m_screenHeight = screenHeight;
//Initialize the location of the mouse on the screen.
m_mouseX = 0;
m_mouseY = 0;
//Initialize the main direct input interface.
result = DirectInput8Create(hinstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_directInput, NULL);
if(FAILED(result))
{
return false;
}
//Initialize the direct input interface for the keyboard.
result = m_directInput->CreateDevice(GUID_SysKeyboard, &m_keyboard, NULL);
if(FAILED(result))
{
return false;
}
//Set the data format. In this case since it is a keyboard we can use the predefined data format.
result = m_keyboard->SetDataFormat(&c_dfDIKeyboard);
if(FAILED(result))
{
return false;
}
//Set the cooperative level of the keyboard to not share with other programs.
result = m_keyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE);
if(FAILED(result))
{
return false;
}
//Now aquire the keyboard.
result = m_keyboard->Acquire();
if(FAILED(result))
{
return false;
}
//Initialize the direct input interface for the mouse.
result = m_directInput->CreateDevice(GUID_SysMouse, &m_mouse, NULL);
if(FAILED(result))
{
return false;
}
//Set the data format for the mouse using the pre-defined mouse data format.
result = m_mouse->SetDataFormat(&c_dfDIMouse);
if(FAILED(result))
{
return false;
}
//Set the cooperative level of the mouse to share with other programs.
result = m_mouse->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
if(FAILED(result))
{
return false;
}
//Aquire the mouse.
result = m_mouse->Acquire();
if(FAILED(result))
{
return false;
}
return true;
}
void InputClass::Shutdown()
{
//Release the mouse.
if(m_mouse)
{
m_mouse->Unacquire();
m_mouse->Release();
m_mouse = 0;
}
//Release the keyboard.
if(m_keyboard)
{
m_keyboard->Unacquire();
m_keyboard->Release();
m_keyboard = 0;
}
//Release the main interface to direct input.
if(m_directInput)
{
m_directInput->Release();
m_directInput = 0;
}
return;
}
bool InputClass::Frame()
{
bool result;
//Read the current state of the keyboard.
result = ReadKeyboard();
if(!result)
{
return false;
}
//Read the current state of the mouse.
result = ReadMouse();
if(!result)
{
return false;
}
//Process the changes in the mouse and keyboard.
ProcessInput();
return true;
}
bool InputClass::ReadKeyboard()
{
HRESULT result;
//Read the keyboard device.
result = m_keyboard->GetDeviceState(sizeof(m_keyboardState), (LPVOID)&m_keyboardState);
if(FAILED(result))
{
//If the keyboard lost focuse or was not aquired then try to get control back.
if((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED))
{
m_keyboard->Acquire();
}
else
{
return false;
}
}
return true;
}
bool InputClass::ReadMouse()
{
HRESULT result;
//Read the mouse device.
result = m_mouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&m_mouseState);
if(FAILED(result))
{
//If the mouse lost focus or was not aqcuired then try to get control back.
if((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED))
{
m_mouse->Acquire();
}
else
{
return false;
}
}
return true;
}
void InputClass::ProcessInput()
{
//Update the location of the mouse cursor based on the change of the mouse location during the frame.
m_mouseX += m_mouseState.lX;
m_mouseY += m_mouseState.lY;
//Ensure the mouse location doesn't exceed the screen width or height.
if(m_mouseX < 0) {m_mouseX = 0;}
if(m_mouseY < 0) {m_mouseY = 0;}
if(m_mouseX > m_screenWidth) {m_mouseX = m_screenWidth;}
if(m_mouseY > m_screenHeight) {m_mouseY = m_screenHeight;}
return;
}
bool InputClass::IsEscapePressed()
{
//Do a bitwise and on the keyboard state to check if the escape key is currently being pressed.
std::cout << "checking ESCAPE" << std::endl;
if(m_keyboardState[DIK_ESCAPE] & 0x80)
{
std::cout << "ESCAPE" << std::endl;
return true;
}
return false;
}
bool InputClass::IsWPressed()
{
if(m_keyboardState[DIK_W] & 0x80)
{
std::cout << "W" << std::endl;
return true;
}
return false;
}
bool InputClass::IsAPressed()
{
if(m_keyboardState[DIK_A] & 0x80)
{
return true;
}
return false;
}
bool InputClass::IsSPressed()
{
if(m_keyboardState[DIK_S] & 0x80)
{
return true;
}
return false;
}
bool InputClass::IsDPressed()
{
if(m_keyboardState[DIK_D] & 0x80)
{
return true;
}
return false;
}
void InputClass::GetMouseLocation(int& mouseX, int& mouseY)
{
mouseX = m_mouseX;
mouseY = m_mouseY;
return;
}
Other file:
////////////////////////////////////////////////////////////////////////////////
// Filename: systemclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "systemclass.h"
SystemClass::SystemClass()
{
m_Input = 0;
m_Graphics = 0;
}
SystemClass::SystemClass(const SystemClass& other)
{
}
SystemClass::~SystemClass()
{
}
bool SystemClass::Initialize()
{
int screenWidth, screenHeight;
bool result;
// Initialize the width and height of the screen to zero before sending the variables into the function.
screenWidth = 0;
screenHeight = 0;
// Initialize the windows api.
InitializeWindows(screenWidth, screenHeight);
// Create the input object. This object will be used to handle reading the keyboard input from the user.
m_Input = new InputClass;
if(!m_Input)
{
return false;
}
// Initialize the input object.
result = m_Input->Initialize(m_hinstance, m_hwnd, screenWidth, screenHeight);
if(!result)
{
MessageBox(m_hwnd, L"Could not initialize the input object.", L"Error", MB_OK);
return false;
}
// Create the graphics object. This object will handle rendering all the graphics for this application.
m_Graphics = new GraphicsClass;
if(!m_Graphics)
{
return false;
}
// Initialize the graphics object.
result = m_Graphics->Initialize(screenWidth, screenHeight, m_hwnd);
if(!result)
{
return false;
}
return true;
}
void SystemClass::Shutdown()
{
// Release the graphics object.
if(m_Graphics)
{
m_Graphics->Shutdown();
delete m_Graphics;
m_Graphics = 0;
}
// Release the input object.
if(m_Input)
{
m_Input->Shutdown();
delete m_Input;
m_Input = 0;
}
// Shutdown the window.
ShutdownWindows();
return;
}
void SystemClass::Run()
{
MSG msg;
bool done, result;
// Initialize the message structure.
ZeroMemory(&msg, sizeof(MSG));
// Loop until there is a quit message from the window or the user.
done = false;
while(!done)
{
// Handle the windows messages.
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// If windows signals to end the application then exit out.
if(msg.message == WM_QUIT)
{
done = true;
}
else
{
// Otherwise do the frame processing.
result = Frame();
if(!result)
{
done = true;
}
}
//Check if the user pressed escape and wants to quit.
if(m_Input->IsEscapePressed() == true)
{
std::cout << "ESCAPE" << std::endl;
done = true;
}
if(m_Input->IsWPressed() == true)
{
m_Graphics->SetCameraPos(0, 1, 0);
std::cout << "W" << std::endl;
}
if(m_Input->IsAPressed() == true)
{
m_Graphics->SetCameraPos(1, 0, 0);
std::cout << "A" << std::endl;
}
if(m_Input->IsSPressed() == true)
{
m_Graphics->SetCameraPos(0, -1, 0);
std::cout << "S" << std::endl;
}
if(m_Input->IsDPressed() == true)
{
m_Graphics->SetCameraPos(-1, 0, 0);
std::cout << "D" << std::endl;
}
}
return;
}
bool SystemClass::Frame()
{
bool result;
int mouseX, mouseY;
//Do the frame processing.
result = m_Input->Frame();
if(!result)
{
return false;
}
//Get the location of the mouse from the input object.
m_Input->GetMouseLocation(mouseX, mouseY);
// Do the frame processing for the graphics object.
result = m_Graphics->Frame(mouseX, mouseY);
if(!result)
{
return false;
}
result = m_Graphics->Render();
if(!result)
{
return false;
}
return true;
}
LRESULT CALLBACK SystemClass::MessageHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
void SystemClass::InitializeWindows(int& screenWidth, int& screenHeight)
{
WNDCLASSEX wc;
DEVMODE dmScreenSettings;
int posX, posY;
// Get an external pointer to this object.
ApplicationHandle = this;
// Get the instance of this application.
m_hinstance = GetModuleHandle(NULL);
// Give the application a name.
m_applicationName = L"Engine";
// Setup the windows class with default settings.
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = m_hinstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hIconSm = wc.hIcon;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = m_applicationName;
wc.cbSize = sizeof(WNDCLASSEX);
// Register the window class.
RegisterClassEx(&wc);
// Determine the resolution of the clients desktop screen.
screenWidth = GetSystemMetrics(SM_CXSCREEN);
screenHeight = GetSystemMetrics(SM_CYSCREEN);
// Setup the screen settings depending on whether it is running in full screen or in windowed mode.
if(FULL_SCREEN)
{
// If full screen set the screen to maximum size of the users desktop and 32bit.
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = (unsigned long)screenWidth;
dmScreenSettings.dmPelsHeight = (unsigned long)screenHeight;
dmScreenSettings.dmBitsPerPel = 32;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
// Change the display settings to full screen.
ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
// Set the position of the window to the top left corner.
posX = posY = 0;
}
else
{
// If windowed then set it to 800x600 resolution.
screenWidth = 800;
screenHeight = 600;
// Place the window in the middle of the screen.
posX = (GetSystemMetrics(SM_CXSCREEN) - screenWidth) / 2;
posY = (GetSystemMetrics(SM_CYSCREEN) - screenHeight) / 2;
}
// Create the window with the screen settings and get the handle to it.
m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, m_applicationName, m_applicationName,
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
posX, posY, screenWidth, screenHeight, NULL, NULL, m_hinstance, NULL);
// Bring the window up on the screen and set it as main focus.
ShowWindow(m_hwnd, SW_SHOW);
SetForegroundWindow(m_hwnd);
SetFocus(m_hwnd);
// Hide the mouse cursor.
ShowCursor(false);
return;
}
void SystemClass::ShutdownWindows()
{
// Show the mouse cursor.
ShowCursor(true);
// Fix the display settings if leaving full screen mode.
if(FULL_SCREEN)
{
ChangeDisplaySettings(NULL, 0);
}
// Remove the window.
DestroyWindow(m_hwnd);
m_hwnd = NULL;
// Remove the application instance.
UnregisterClass(m_applicationName, m_hinstance);
m_hinstance = NULL;
// Release the pointer to this class.
ApplicationHandle = NULL;
return;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
{
switch(umessage)
{
// Check if the window is being destroyed.
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
// Check if the window is being closed.
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
// All other messages pass to the message handler in the system class.
default:
{
return ApplicationHandle->MessageHandler(hwnd, umessage, wparam, lparam);
}
}
}
Why don't try with GetAsyncKeyState function?
you can put that in your update function and check for the desired key code.
Something like this:
//check if num1 is pressed, if yes switch off the lights.
if(GetAsyncKeyState('1') & 0x8000)
{
mLightCount = 0;
}
As it turned out, the keybindings were working. I tried switching the close key to w and pressing W closed the window. What was wrong was how I was moving the camera. Thanks for helping!
Related
I have a C++ Win32 app that needs to be able to play an external .wav file every time a certain event is triggered. I currently have code that looks like this:
void CALLBACK timerCall(HWND hwnd, UINT msg, UINT timer, DWORD time)
{
if(/*some condition is met*/)
{
std::cout << "Detected event" << std::endl;
PlaySound("file.wav", NULL, SND_FILENAME | SND_ASYNC);
}
}
The problem is, the .wav file is several seconds long and I want each invocation of the event to play a new instance of that sound. Without SND_ASYNC there it wouldn't trigger the event until the sound had finished playing; this was resolved by adding SND_ASYNC. However, now if the event is triggered again while the sound is already playing, it interrupts the playing and simply starts over instead of overlapping the sounds.
How do I prevent the new call to PlaySound from interrupting the previous and force the sounds to overlap?
"waveaudio" device (which is used by PlaySound) does not support playing several files simultaneously. Try to use an .mp3 file. Example below.
#include <Windows.h>
#include <stdio.h>
#include <stdexcept>
#ifdef _UNICODE
#define stprintf_s swprintf_s
#else
#define stprintf_s sprintf_s
#endif
class Player {
public:
Player(LPCTSTR lpFileName) {
MCI_OPEN_PARMS openp;
MCI_SET_PARMS setp;
openp.dwCallback = NULL;
openp.lpstrDeviceType = reinterpret_cast<LPCTSTR>(MCI_ALL_DEVICE_ID);
openp.lpstrElementName = lpFileName;
TCHAR name[32];
static int alias = 0;
stprintf_s(name, TEXT("alias%08d"), alias++);
openp.lpstrAlias = name;
checkerror(mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE | MCI_OPEN_ALIAS, reinterpret_cast<DWORD_PTR>(&openp)));
_device = openp.wDeviceID;
setp.dwCallback = NULL;
setp.dwTimeFormat = 0;
if (mciSendCommand(openp.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, reinterpret_cast<DWORD_PTR>(&setp)) == DWORD(-1)) {
close();
throw std::runtime_error("Can't open MCI device");
}
}
Player(Player const&) = delete;
Player(Player&& other) {
_device = other._device;
other._device = 0;
}
~Player() {
if (_device != 0) {
close();
}
}
void play(HWND hWndNotify) {
MCI_PLAY_PARMS params;
params.dwCallback = reinterpret_cast<DWORD_PTR>(hWndNotify);
params.dwFrom = NULL;
params.dwTo = NULL;
checkerror(mciSendCommand(_device, MCI_PLAY, (hWndNotify != 0) ? MCI_NOTIFY : 0, reinterpret_cast<DWORD_PTR>(¶ms)));
}
void rewind() {
MCI_SEEK_PARMS params;
checkerror(mciSendCommand(_device, MCI_SEEK, MCI_WAIT | MCI_SEEK_TO_START, reinterpret_cast<DWORD_PTR>(¶ms)));
}
void pause() {
MCI_GENERIC_PARMS params;
params.dwCallback = NULL;
checkerror(mciSendCommand(_device, MCI_PAUSE, MCI_WAIT, reinterpret_cast<DWORD_PTR>(¶ms)));
}
void stop() {
MCI_GENERIC_PARMS params;
params.dwCallback = NULL;
checkerror(mciSendCommand(_device, MCI_STOP, MCI_WAIT, reinterpret_cast<DWORD_PTR>(¶ms)));
}
MCIDEVICEID device() const { return _device; }
private:
MCIDEVICEID _device;
static void checkerror(MCIERROR code) {
if (code != 0) {
char buffer[260];
mciGetErrorStringA(code, buffer, sizeof(buffer) - 1);
throw std::runtime_error(buffer);
}
}
void close() {
MCI_GENERIC_PARMS params;
params.dwCallback = NULL;
checkerror(mciSendCommand(_device, MCI_CLOSE, MCI_WAIT, reinterpret_cast<DWORD_PTR>(¶ms)));
}
};
#include <map>
#include <string>
#include <mutex>
#ifdef _UNICODE
typedef std::wstring tstring;
#else
typedef std::string tstring;
#endif
class Repeater {
public:
Repeater(LPCTSTR fn) : fn(fn) {
hWnd = CreateWindowEx(0, TEXT("STATIC"), NULL, 0, 0, 0, 0, 0, HWND_DESKTOP, NULL, GetModuleHandle(NULL), 0);
if (hWnd == NULL)
throw std::runtime_error("Can't create window");
oldproc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG>(this));
SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG>(&myproc));
}
~Repeater() {
DestroyWindow(hWnd);
}
void play() {
Player player(fn.c_str());
std::lock_guard<std::recursive_mutex> lock(devmap_mutex);
player.play(hWnd);
devmap.insert(decltype(devmap)::value_type(player.device(), std::move(player)));
}
HWND wnd() const { return hWnd; }
void stop() {
std::lock_guard<std::recursive_mutex> lock(devmap_mutex);
devmap.clear();
}
private:
HWND hWnd;
tstring fn;
std::recursive_mutex devmap_mutex;
std::map<MCIDEVICEID, Player> devmap;
WNDPROC oldproc;
static LRESULT CALLBACK myproc(_In_ HWND hWnd, _In_ UINT Msg, _In_ WPARAM wParam, _In_ LPARAM lParam) {
auto self = reinterpret_cast<Repeater*>(GetWindowLong(hWnd, GWLP_USERDATA));
switch (Msg) {
case MM_MCINOTIFY: {
// see https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd757358(v=vs.85).aspx
std::lock_guard<std::recursive_mutex> lock(self->devmap_mutex);
self->devmap.erase(static_cast<MCIDEVICEID>(lParam));
return 0;
}
case WM_DESTROY: {
SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG>(self->oldproc));
}
default:
break;
}
return CallWindowProc(self->oldproc, hWnd, Msg, wParam, lParam);
}
};
int main() {
// USE MP3. Forget about WAV.
LPCTSTR filename = TEXT("c:\\Users\\Vyacheslav\\Music\\Ori\\soundtrack\\Racing the Lava.mp3");
#if 0
// without notifications
Player dev1(filename), dev2(filename);
dev1.play();
Sleep(1000);
dev2.play();
Sleep(10000);
#else
// with notifications
{
Repeater rep(filename);
std::thread thread([&rep] {
for (int i = 0; i < 5; ++i) {
rep.play();
Sleep(1000);
}
Sleep(1000);
rep.stop(); // .stop() MUST be called from the same thread as ALL .play() !!!
PostMessage(rep.wnd(), WM_QUIT, 0, 0); // interrupt message processing queue
});
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
thread.join();
}
Sleep(10000); // silence
#endif
return 0;
}
So if I use the code below when the program exits I get an exception in std::list at this location. This is regardless of whether I add items to it or not.
void clear() _NOEXCEPT
{ // erase all
#if _ITERATOR_DEBUG_LEVEL == 2
this->_Orphan_ptr(*this, nullptr);
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */
_Nodeptr _Pnode = this->_Nextnode(this->_Myhead);
this->_Nextnode(this->_Myhead) = this->_Myhead;
this->_Prevnode(this->_Myhead) = this->_Myhead;
this->_Mysize = 0;
for (_Nodeptr _Pnext; _Pnode != this->_Myhead; _Pnode = _Pnext)
{ // delete an element
_Pnext = this->_Nextnode(_Pnode); //Unhandled exception, access violation
this->_Freenode(_Pnode);
}
}
Here's the only container I'm using std::list in currently and I only have 1 of them ...
if I switch std::list to std::vector then it works fine.
#pragma once
#ifndef EVENT_H
#define EVENT_H
#include <delegate.h>
#include <list>
#include <vector>
using fd::delegate;
template<typename... Args>
class Event;
template<typename...Args>
class Event<void(Args...)>
{
private:
std::list<delegate<void(Args...)>> m_delegates;
public:
void addListener(const delegate<void(Args...)>& d)
{
if (d)
m_delegates.push_back(d);
}
void removeListener(const delegate<void(Args...)>& d)
{
if (d)
m_delegates.remove(d);
}
void removeDeadListeners()
{
for (auto i = m_delegates.begin(); i != m_delegates.end(); i++)
{
if (i->empty())
m_delegates.remove(i);
}
}
void operator()(Args... args)
{
for (auto& d : m_delegates)
{
d(args...);
}
}
};
#endif //EVENT_H
Any way to use std::list?
I believe your problem is that you're removing items from the list while iterating over it, but I'm surprised the code you've shown compiles at all.
for (auto i = m_delegates.begin(); i != m_delegates.end(); i++)
{
if (i->empty())
m_delegates.remove(i); // <-- This shouldn't compile
}
list::remove takes a list::value_type as the argument, and not list::iterator, so the above should fail to compile. The solution is to use list::erase
for (auto i = m_delegates.begin(); i != m_delegates.end(); )
{
if (i->empty())
i = m_delegates.erase(i); // get iterator to next element
else
++i;
}
You could also eliminate the loop completely and use list::remove_if
m_delegates.remove_if([](decltype(m_delegates)::value_type const& elem) {
return elem.empty();
});
I've determined it's just something to do with the class it is declared in as I can use Event fine in other classes ..... I'm just confused because I NEVER call a single method. I'm just modifying a tutorial code to make it a bit easier to work with for a test project of mine. The default constructor works fine for other classes and I can add / call methods fine it's just this one ... I'm stumped.
The .h
#pragma once
#ifndef D3DAPP_H
#define D3DAPP_H
#pragma warning(push)
#pragma warning( disable : 4005) //Ignore Identical Macro declarations that exist in Windows and DirectX headers
#include <D3D11.h>
#include <D3DX11.h>
#include <Windows.h>
#pragma warning(pop)
#include "GameTimer.h"
#include <string>
#include "Event.h"
class D3DApp
{
public:
D3DApp(HINSTANCE hInstance);
virtual ~D3DApp();
HINSTANCE AppInst() const;
HWND MainWnd() const;
float AspectRatio() const;
int Run();
/*
Framework methods. Derived client class overrides these methods
to implement specific application requirements
*/
virtual bool Init();
virtual void Release();
Event<void(int,int)> OnResize;
virtual void UpdateScene(float dt) = 0;
virtual void DrawScene() = 0;
virtual LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
//Convenience overrides for handling mouse input
virtual void OnMouseDown(WPARAM btnState, int x, int y);
virtual void OnMouseUp(WPARAM btnState, int x, int y);
virtual void OnMouseMove(WPARAM btnState, int x, int y);
protected:
virtual void ApplicationResize(int w, int h);
bool InitMainWindow();
bool InitDirect3D();
void CalculateFrameStats();
protected:
HINSTANCE mAppInstance;
HWND mMainWnd;
bool mAppPaused;
bool mMinimized;
bool mMaximized;
bool mResizing;
bool mInitialized;
UINT m4xMsaaQuality;
GameTimer mTimer;
ID3D11Device* md3dDevice;
ID3D11DeviceContext* md3dImmediateContext;
IDXGISwapChain* mSwapChain;
ID3D11Texture2D* mDepthStencilBuffer;
ID3D11RenderTargetView* mRenderTargetView;
ID3D11DepthStencilView* mDepthStencilView;
D3D11_VIEWPORT mScreenViewport;
std::wstring mMainWndCaption;
D3D_DRIVER_TYPE md3dDriverType;
int mClientWidth;
int mClientHeight;
bool mEnable4xMsaa;
};
#endif //D3DAPP_H
The .cpp
#include "d3dApp.h"
#include "Utility.h"
#include <assert.h>
#include <sstream>
D3DApp* g_d3dApp = 0;
LRESULT CALLBACK
MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
return g_d3dApp->MsgProc(hwnd, msg, wParam,lParam);
}
D3DApp::D3DApp(HINSTANCE hInstance)
: mAppInstance(hInstance),
mMainWndCaption(L"D3D11 Application"),
md3dDriverType(D3D_DRIVER_TYPE_HARDWARE),
mClientWidth(800),
mClientHeight(600),
mEnable4xMsaa(false),
mMainWnd(0),
mAppPaused(false),
mMinimized(false),
mMaximized(false),
mResizing(false),
mInitialized(false),
m4xMsaaQuality(0),
md3dDevice(0),
md3dImmediateContext(0),
mSwapChain(0),
mDepthStencilBuffer(0),
mRenderTargetView(0),
mDepthStencilView(0)
{
ZeroMemory(&mScreenViewport, sizeof(D3D11_VIEWPORT));
g_d3dApp = this;
}
void D3DApp::Release()
{
ReleaseCOM(mRenderTargetView);
ReleaseCOM(mDepthStencilView);
ReleaseCOM(mSwapChain);
ReleaseCOM(mDepthStencilBuffer);
if (md3dImmediateContext)
md3dImmediateContext->ClearState();
ReleaseCOM(md3dImmediateContext);
ReleaseCOM(md3dDevice);
if (g_d3dApp == this)
g_d3dApp = nullptr;
mInitialized = false;
}
D3DApp::~D3DApp()
{
if (mInitialized) Release();
}
float D3DApp::AspectRatio() const
{
return static_cast<float>(mClientWidth) / mClientHeight;
}
int D3DApp::Run()
{
MSG msg = { 0 };
mTimer.Reset();
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
mTimer.Tick();
CalculateFrameStats();
UpdateScene(mTimer.DeltaTime());
DrawScene();
}
}
return (int)msg.wParam;
}
bool D3DApp::Init()
{
if (!InitMainWindow())
return false;
if (!InitDirect3D())
return false;
mInitialized = true;
return true;
}
void D3DApp::ApplicationResize(int w,int h)
{
assert(md3dImmediateContext);
assert(md3dDevice);
assert(mSwapChain);
ReleaseCOM(mRenderTargetView);
ReleaseCOM(mDepthStencilView);
ReleaseCOM(mDepthStencilBuffer);
mClientWidth = w;
mClientHeight = h;
HR(mSwapChain->ResizeBuffers(1, mClientWidth, mClientHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
ID3D11Texture2D* backBuffer;
HR(mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&backBuffer)));
HR(md3dDevice->CreateRenderTargetView(backBuffer, 0, &mRenderTargetView));
ReleaseCOM(backBuffer);
D3D11_TEXTURE2D_DESC depthStencilDesc;
ZeroMemory(&depthStencilDesc, sizeof(D3D11_TEXTURE2D_DESC));
depthStencilDesc.Width = mClientWidth;
depthStencilDesc.Height = mClientHeight;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
if (mEnable4xMsaa)
{
depthStencilDesc.SampleDesc.Count = 4;
depthStencilDesc.SampleDesc.Quality = m4xMsaaQuality - 1;
} //No MSAA
else
{
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = 0;
}
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MipLevels = 0;
HR(md3dDevice->CreateTexture2D(&depthStencilDesc, 0, &mDepthStencilBuffer));
HR(md3dDevice->CreateDepthStencilView(mDepthStencilBuffer, 0, &mDepthStencilView));
md3dImmediateContext->OMSetRenderTargets(1, &mRenderTargetView,mDepthStencilView);
mScreenViewport.TopLeftX = 0;
mScreenViewport.TopLeftY = 0;
mScreenViewport.Width = static_cast<float>(mClientWidth);
mScreenViewport.Height = static_cast<float>(mClientWidth);
mScreenViewport.MinDepth = 0.0f;
mScreenViewport.MaxDepth = 1.0f;
md3dImmediateContext->RSSetViewports(1, &mScreenViewport);
//OnResize(w, h);
}
LRESULT D3DApp::MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
//Ignore messages until we have intialized everything
if (!mInitialized)
return DefWindowProc(hWnd, msg, wParam, lParam);
switch (msg)
{
case WM_ACTIVATE:
if (LOWORD(wParam) == WA_INACTIVE)
{
mAppPaused = true;
mTimer.Stop();
}
else
{
mAppPaused = false;
mTimer.Start();
}
return 0;
case WM_SIZE:
if (wParam == SIZE_MINIMIZED)
{
mAppPaused = true;
mMinimized = true;
mMaximized = false;
mTimer.Stop();
}
else if (wParam == SIZE_MAXIMIZED)
{
mAppPaused = SIZE_MAXIMIZED;
mMinimized = false;
mMaximized = true;
ApplicationResize(LOWORD(lParam), HIWORD(lParam));
}
else if (wParam == SIZE_RESTORED)
{
if (mMinimized)
{
mAppPaused = false;
mMinimized = false;
mTimer.Start();
ApplicationResize(LOWORD(lParam), HIWORD(lParam));
}
else if (mMaximized)
{
mAppPaused = false;
mMaximized = false;
ApplicationResize(LOWORD(lParam), HIWORD(lParam));
}
else if (mResizing)
{
//We don't want to do stuff while this is going on
}
else
{
ApplicationResize(LOWORD(lParam), HIWORD(lParam));
}
}
return 0;
case WM_ENTERSIZEMOVE:
mAppPaused = true;
mResizing = true;
mTimer.Stop();
return 0;
case WM_EXITSIZEMOVE:
mAppPaused = false;
mResizing = false;
mTimer.Start();
if (lParam != 0)
ApplicationResize(LOWORD(lParam), HIWORD(lParam));
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_MENUCHAR:
//Don't beep when we alt-enter
return MAKELRESULT(0, MNC_CLOSE);
case WM_GETMINMAXINFO:
((MINMAXINFO*)lParam)->ptMinTrackSize.x = 200;
((MINMAXINFO*)lParam)->ptMinTrackSize.y = 200;
return 0;
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
OnMouseDown(wParam, LOWORD(lParam), HIWORD(lParam));
return 0;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
OnMouseUp(wParam, LOWORD(lParam), HIWORD(lParam));
return 0;
case WM_MOUSEMOVE:
OnMouseMove(wParam, LOWORD(lParam), HIWORD(lParam));
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
bool D3DApp::InitMainWindow()
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = mAppInstance;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = L"D3DWndClassName";
if (!RegisterClass(&wc))
{
MessageBox(0, L"RegisterClass Failed!", 0, 0);
return false;
}
RECT R = { 0, 0, mClientWidth, mClientHeight };
AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
int width = R.right - R.left;
int height = R.bottom - R.top;
mMainWnd = CreateWindow(L"D3DWndClassName", mMainWndCaption.c_str(),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, mAppInstance, 0);
if (mMainWnd);
{
MessageBox(0, L"CreateWindow Failed", 0, 0);
return false;
}
ShowWindow(mMainWnd, SW_SHOW);
UpdateWindow(mMainWnd);
return true;
}
bool D3DApp::InitDirect3D()
{
//Create the device and device context
UINT createDeviceFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
D3D_FEATURE_LEVEL featureLevel;
HRESULT hr = D3D11CreateDevice(
0,
md3dDriverType,
0,
createDeviceFlags,
0, 0,
D3D11_SDK_VERSION,
&md3dDevice,
&featureLevel,
&md3dImmediateContext);
if (FAILED(hr))
{
MessageBox(0, L"D3D11CreateDevice Failed.", 0, 0);
return false;
}
if (featureLevel != D3D_FEATURE_LEVEL_11_0)
{
MessageBox(0, L"Direct3D Feature Level 11 unsupported", 0, 0);
return false;
}
HR(md3dDevice->CheckMultisampleQualityLevels(
DXGI_FORMAT_R8G8B8A8_UNORM, 4, &m4xMsaaQuality));
assert(m4xMsaaQuality > 0);
DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width = mClientWidth;
sd.BufferDesc.Height = mClientHeight;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
if (mEnable4xMsaa)
{
sd.SampleDesc.Count = 4;
sd.SampleDesc.Quality = m4xMsaaQuality - 1;
}
else
{
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
}
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 1;
sd.OutputWindow = mMainWnd;
sd.Windowed = true;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
sd.Flags = 0;
IDXGIDevice* dxgiDevice = 0;
HR(md3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice));
IDXGIAdapter* dxgiAdapter = 0;
HR(dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&dxgiAdapter));
IDXGIFactory* dxgiFactory = 0;
HR(dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&dxgiFactory));
HR(dxgiFactory->CreateSwapChain(md3dDevice, &sd, &mSwapChain));
ReleaseCOM(dxgiDevice);
ReleaseCOM(dxgiAdapter);
ReleaseCOM(dxgiFactory);
ApplicationResize(mClientWidth, mClientHeight);
return true;
}
void D3DApp::CalculateFrameStats()
{
//Code computes the average frames per second also the average
//time it takes to render one frame. These stats
//are appended to the window caption bar.
static int frameCnt = 0;
static float timeElapsed = 0.0f;
frameCnt++;
float dt = mTimer.TotalTime() - timeElapsed;
if (dt >= 1.0f)
{
float fps = (float)frameCnt/dt;
float mspf = 1000.0f / fps;
std::wstringstream outs;
outs.precision(6);
outs << mMainWndCaption << L" "
<< L"FPS: " << fps << L" "
<< L"Frame Time: " << mspf << L" (ms)";
SetWindowText(mMainWnd, outs.str().c_str());
frameCnt = 0;
timeElapsed += dt;
}
}
void D3DApp::OnMouseDown(WPARAM btnState, int x, int y)
{
}
void D3DApp::OnMouseUp(WPARAM btnState, int x, int y)
{
}
void D3DApp::OnMouseMove(WPARAM btnState, int x, int y)
{
}
I am attempting to make a simple scrolling shooter game with SDL2. I have a moving player on a screen, and I am trying to make the player shoot a bullet using an array (so they can shoot multiple bullets) however, when I press the space bar, nothing happens, and instead the bullet image sort of flashes in the top left corner.
Heres the same code in codepad: http://codepad.org/rOhE1AqY
#include <SDL.h>
#include <stdio.h> //use for things like printf, same as cout
#include <iostream>
#include <string>
#include <time.h>
using namespace std;
//screend dimensions& sprtie dimensions
const int SCREEN_HEIGHT = 600;
const int SCREEN_WIDTH = 400;
const int SPRITE_WIDTH = 60;
const int SPRITE_HEIGHT = 80;
const int MAX_BULLETS = 50;
SDL_Window* Window = NULL;//the window rendering to
SDL_Surface* ScreenSurface = NULL;//surface contained by window
SDL_Surface* Background = NULL;
SDL_Surface* Player = NULL;
SDL_Surface* Enemy = NULL;
SDL_Surface* Bullet = NULL;
SDL_Surface* newBullet = NULL;
SDL_Rect posPlayer, posEnemy, posBullet, posnewBullet;
const Uint8* keystate = SDL_GetKeyboardState(NULL);
SDL_Event event;
class thePlayer
{
public:
thePlayer();
void player_movement();
void show_player();
private:
};
class theBullet
{
public:
theBullet();
bool isActive;
int x_position;
int y_position;
void bullet_movement();
void add_new_bullet();
void show_bullet();
private:
};
theBullet arrayofBullets[MAX_BULLETS];
class theEnemy
{
public:
theEnemy();
void enemy_movement();
void show_enemy();
private:
};
thePlayer::thePlayer()
{
posPlayer.x = 170;
posPlayer.y = SCREEN_HEIGHT;
posPlayer.w = 20;
posPlayer.h = 30;
}
void thePlayer::player_movement()
{
if(keystate[SDL_SCANCODE_LEFT])
{
posPlayer.x -= 2;
}
if(keystate[SDL_SCANCODE_RIGHT])
{
posPlayer.x += 2;
}
if(keystate[SDL_SCANCODE_UP])
{
posPlayer.y -= 2;
}
if(keystate[SDL_SCANCODE_DOWN])
{
posPlayer.y += 2;
}
if ((posPlayer.x + SPRITE_WIDTH) > SCREEN_WIDTH)
{
posPlayer.x = (SCREEN_WIDTH - SPRITE_WIDTH);
}
if ((posPlayer.y + SPRITE_HEIGHT) > SCREEN_HEIGHT)
{
posPlayer.y = (SCREEN_HEIGHT - SPRITE_HEIGHT);
}
}
void thePlayer::show_player()
{
SDL_BlitSurface(Player, NULL, ScreenSurface, &posPlayer);
SDL_SetColorKey(Player, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));
}
theBullet::theBullet()
{
/*posBullet.x;
posBullet.y;
posBullet.w = 10;
posBullet.h = 15;*/
}
void theBullet::bullet_movement()
{
/*if(keystate[SDL_SCANCODE_SPACE])
{
posBullet.x = posPlayer.x + 25;
posBullet.y = posPlayer.y + 10;
}
posBullet.y -= 2;
if(posBullet.y < 0)
{
posBullet.y = -50;
}*/
}
void theBullet::show_bullet()
{
//SDL_BlitSurface(Bullet, NULL, ScreenSurface, &posBullet);
//SDL_SetColorKey(Bullet, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));//removes white background
}
theEnemy::theEnemy()
{
srand (time(NULL));
posEnemy.x = rand() % 300 + 50;
posEnemy.y =0;
posEnemy.w = 35;
posEnemy.h = 60;
}
void theEnemy::enemy_movement()
{
posEnemy.y += 1;
if(posEnemy.y > SCREEN_HEIGHT)
{
posEnemy.y = SCREEN_HEIGHT +50;
}
}
void theEnemy::show_enemy()
{
SDL_BlitSurface(Enemy, NULL, ScreenSurface, &posEnemy);
SDL_SetColorKey(Enemy, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));
}
bool initialise()
{
bool success = true;
if (SDL_Init(SDL_INIT_EVERYTHING) !=0)
{
cout<<"SDL_Init Error."<<SDL_GetError()<<endl;
success = false;
}
else
{
//create the window for game
Window = SDL_CreateWindow("Scrolling Shooter Game", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (Window == NULL)
{
cout<<"Window Error"<<SDL_GetError()<<endl;
success = false;
}
else
{
//get window surface
ScreenSurface = SDL_GetWindowSurface(Window);
}
}
return success;
}
bool LoadingMedia()
{
bool success = true;
Background = SDL_LoadBMP("background.bmp");
if (Background == NULL)
{
cout<<"Error in loading background."<<SDL_GetError()<<endl;
success = false;
}
Player = SDL_LoadBMP("spaceship.bmp");
if (Player == NULL)
{
cout<<"Error in loading player."<<SDL_GetError()<<endl;
success = false;
}
Enemy = SDL_LoadBMP("enemy.bmp");
if (Enemy == NULL)
{
cout<<"Error in loading enemy."<<SDL_GetError()<<endl;
success = false;
}
Bullet = SDL_LoadBMP("bullet.bmp");
if (Bullet == NULL)
{
cout<<"Error in loading bullet."<<SDL_GetError()<<endl;
success = false;
}
return success;
}
void closedown()
{
SDL_FreeSurface(Background);
Background = NULL;
SDL_FreeSurface(Player);
Player = NULL;
SDL_FreeSurface(Enemy);
Enemy = NULL;
SDL_DestroyWindow(Window);
Window = NULL;
SDL_Quit();
}
int main(int argc, char** argv)
{
bool quit = false;
thePlayer myPlayer;
theEnemy myEnemy;
theBullet myBullet;
if (!initialise())
{
cout<<"Failed to initialise"<<SDL_GetError()<<endl;
}
else
{
if (!LoadingMedia())
{
cout<<"Error loading media"<<SDL_GetError()<<endl;
}
}
//makes all bullets false
for (int i=0; i<MAX_BULLETS; i++)
{
arrayofBullets[i].isActive = false;
}
//GAME LOOP
while (quit == false)
{
SDL_BlitSurface(Background, NULL, ScreenSurface, NULL);
myPlayer.show_player();
myPlayer.player_movement();
while (SDL_PollEvent(&event))
{
if( event.type == SDL_QUIT )
{
quit = true;
break;
}
if(keystate[SDL_SCANCODE_SPACE])
{
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == false)
{
arrayofBullets[i].x_position = posPlayer.x + 25;
arrayofBullets[i].y_position = posPlayer.y + 10;
arrayofBullets[i].isActive = true;
break;
}
}
}
//update game objects
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == true)
{
arrayofBullets[i].y_position -= 2;
if (arrayofBullets[i].y_position < 0)
{
arrayofBullets[i].isActive = false;
}
}
}
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == true)
{
SDL_BlitSurface(Bullet, NULL, ScreenSurface, NULL);
}
}
}
//myPlayer.show_player();
//myBullet.show_bullet();
//myEnemy.show_enemy();
//myPlayer.player_movement();
//myBullet.bullet_movement();
//myEnemy.enemy_movement();
SDL_UpdateWindowSurface(Window); //updates screen
}
closedown();
return 0;
}
SDL_BlitSurface(Bullet, NULL, ScreenSurface, NULL);
You haven't specified destination rect, so it will blit on left-top corner.
It should be
SDL_Rect dstrect;
dstrect.x = arrayofBullets[i].x_position;
dstrect.y = arrayofBullets[i].y_position;
dstrect.w = Bullet->w;
dstrect.h = Bullet->h;
SDL_BlitSurface(Bullet, NULL, ScreenSurface, &dstrect);
I have this class:
WNDCLASSEX ActionButton::m_wndClass = CreateWndClass();
ActionButton::ActionButton() :
m_function(NULL), m_parameters(NULL), m_window()
{}
ActionButton::~ActionButton()
{
DestroyWindow(m_window);
}
bool ActionButton::DestroyButton()
{
return DestroyWindow(m_window);
}
bool ActionButton::Create(HWND parent, int x, int y, int heigth, int width)
{
HWND m_window = CreateWindowEx(0, L"Action button", NULL, WS_CHILD | WS_VISIBLE,
x, y, width, heigth, parent, NULL, NULL, NULL);
if (m_window == NULL)
return false;
SetWindowLongPtr(m_window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
return true;
}
void ActionButton::SetFuncionLeftButtonDown(CallbackFunction f)
{
m_function = f;
}
void ActionButton::SetParametersLeftButtonDown(void* param)
{
m_parameters = param;
}
WNDCLASSEX ActionButton::CreateWndClass()
{
WNDCLASSEX m_wndClass = {0};
if (m_wndClass.cbSize == 0)
{
m_wndClass.cbSize = sizeof(WNDCLASSEX);
m_wndClass.style = CS_NOCLOSE;
m_wndClass.lpfnWndProc = WndProc;
m_wndClass.cbClsExtra = 0;
m_wndClass.cbWndExtra = 0;
m_wndClass.hInstance = GetModuleHandle(NULL);
m_wndClass.hIcon = NULL;
m_wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
m_wndClass.hbrBackground = HBRUSH(COLOR_BACKGROUND);
m_wndClass.lpszMenuName = NULL;
m_wndClass.lpszClassName = L"Action button";
m_wndClass.hIconSm = NULL;
}
RegisterClassEx(&m_wndClass);
return m_wndClass;
}
LRESULT __stdcall ActionButton::WndProc (HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
ActionButton* classInfo = reinterpret_cast<ActionButton *>(GetWindowLongPtr(window, GWLP_USERDATA));
switch(msg)
{
case WM_LBUTTONDOWN:
{
(classInfo->m_function)(classInfo->m_parameters, classInfo);
classInfo->DestroyButton();
break;
}
case WM_DESTROY:
{
break;
}
default:
return DefWindowProc(window, msg, wp, lp);
}
return 0;
}
I have found problem, that it do not destroy window, what is more I check with debugger that m_window in destructor and Destroy() methods is NULL.
My code of using this class:
void Function(void* input, ActionButton*)
{
std::cout << "Works :)\n";
}
//....
ActionButton button;
button.Create(Form.Get(), 150,150, 50,50);
button.SetFuncionLeftButtonDown(Function);
HWND m_window
That declares a local variable which hides the class member variable. Your compiler should be warning you about this. Pay attention to the warnings.
I'm sort of new to rendering graphics with GDI...
I made a paint program, and it works fine, it's just that it causes a lot of annoying screen flickering. I will admit my paint code is not really optimized (lack of time), but it shouldn't be super inefficient either, so I'm puzzled.
What I'm basically doing is creating a compatible DC on init, then create a compatible bitmap. Then I select it into the compatible DC, and paint to the compatible DC. Then I use BitBlit() to copy it to the window hDC...
Could anyone tell me the possible causes for this screen tearing?
EDIT: btw, screen flickering only occurs during the drawing of a path (before the path gets drawn to the hMemDC, it gets drawn to the hDC of the window)
Code samples:
(EDIT: If you need to see any more code that you think is relevant, comment and I'll edit)
Path::DrawTo(HDC)
bool Path::DrawTo(HDC hDC)
{
if(hDC == NULL || m_PointVector.size() <= 0) {
return false;
}
switch (m_Tool) {
case Tool_Pen:
{
Point2D p = m_PointVector.at(0);
if(m_PointVector.size() > 1) {
HPEN oldPen = (HPEN)SelectObject(hDC,m_hPen);
MoveToEx(hDC, p.x, p.y, nullptr);
for(UINT i = 1; i < m_PointVector.size(); ++i) {
p = m_PointVector.at(i);
LineTo(hDC,p.x,p.y);
}
SelectObject(hDC,oldPen);
break;
} //else
SetPixel(hDC,p.x-1,p.y,m_Col);
SetPixel(hDC,p.x,p.y,m_Col);
SetPixel(hDC,p.x+1,p.y,m_Col);
SetPixel(hDC,p.x,p.y-1,m_Col);
SetPixel(hDC,p.x,p.y+1,m_Col);
break;
}
case Tool_Line:
{
if(m_PointVector.size() > 1) {
Point2D p = m_PointVector.at(0);
HPEN oldPen = (HPEN)SelectObject(hDC,m_hPen);
MoveToEx(hDC, p.x, p.y, nullptr);
for(UINT i = 1; i < m_PointVector.size(); ++i) {
p = m_PointVector.at(i);
LineTo(hDC,p.x,p.y);
}
SelectObject(hDC,oldPen);
}
break;
}
case Tool_Ellipse:
{
if(m_PointVector.size() > 1) {
HPEN oldPen = (HPEN)SelectObject(hDC,m_hPen);
SelectObject(hDC,m_hBrush);
Point2D p1 = m_PointVector.at(0);
Point2D p2 = m_PointVector.at(1);
if(p1.x > p2.x) {
int iTemp = p1.x;
p1.x = p2.x;
p2.x = iTemp;
}
if(p1.y > p2.y) {
int iTemp = p1.y;
p1.y = p2.y;
p2.y = iTemp;
}
Ellipse(hDC,p1.x,p1.y,p2.x,p2.y);
SelectObject(hDC,oldPen);
}
break;
}
case Tool_Rectangle:
{
if(m_PointVector.size() > 1) {
HPEN oldPen = (HPEN)SelectObject(hDC,m_hPen);
SelectObject(hDC,m_hBrush);
Point2D p1 = m_PointVector.at(0);
Point2D p2 = m_PointVector.at(1);
if(p1.x > p2.x) {
int iTemp = p1.x;
p1.x = p2.x;
p2.x = iTemp;
}
if(p1.y > p2.y) {
int iTemp = p1.y;
p1.y = p2.y;
p2.y = iTemp;
}
Rectangle(hDC,p1.x,p1.y,p2.x,p2.y);
SelectObject(hDC,oldPen);
}
break;
}
case Tool_LineTrack:
{
HPEN oldPen = (HPEN)SelectObject(hDC,m_hPen);
SelectObject(hDC,m_hBrush);
int vSize = (int)m_PointVector.size();
Point2D p = m_PointVector.at(0);
if (vSize <= 1) {
Ellipse(hDC,p.x-10,p.y-10,p.x+10,p.y+10);
}
else {
//draw LineTrack
Point2D pTemp = m_PointVector.at(1);
MoveToEx(hDC,p.x,p.y,nullptr);
for (int i = 1; i < vSize; ++i) {
p = m_PointVector.at(i);
pTemp = m_PointVector.at(i-1);
LineTo(hDC,p.x,p.y);
Ellipse(hDC,pTemp.x-10,pTemp.y-10,pTemp.x+10,pTemp.y+10);
}
Ellipse(hDC,p.x-10,p.y-10,p.x+10,p.y+10);
}
SelectObject(hDC,oldPen);
break;
}
}
return true;
}
WndProc(HWND, UINT, WPARAM, LPARAM)
LRESULT MyApp::WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
if(iMsg == WM_CREATE)
{
CREATESTRUCT *pCS = (CREATESTRUCT*)lParam;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG)pCS->lpCreateParams);
}
else
{
//retrieve the stored "this" pointer
MyApp* pApp = (MyApp*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
switch (iMsg)
{
case WM_PAINT:
{
pApp->Paint();
return 0;
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
int wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_NEW:
{
////
return 0;
}
return 0;
case IDM_LOAD:
{
//////
return 0;
}
case IDM_SAVE:
{
//////
return 0;
}
case IDM_SAVEAS:
{
//////
return 0;
}
case IDM_COLOURMAIN:
{
COLORREF col;
if(MyWin32Funcs::OnColorPick(col)) {
pApp->m_pPath->SetColor1(col);
}
return 0;
}
case IDM_COLOURSECONDARY:
{
COLORREF col;
if(MyWin32Funcs::OnColorPick(col)) {
pApp->m_pPath->SetColor2(col);
}
return 0;
}
case IDM_PEN:
{
pApp->m_pPath->SetTool(Tool_Pen);
return 0;
}
case IDM_LINE:
{
pApp->m_pPath->SetTool(Tool_Line);
return 0;
}
case IDM_ELLIPSE:
{
pApp->m_pPath->SetTool(Tool_Ellipse);
return 0;
}
case IDM_RECTANGLE:
{
pApp->m_pPath->SetTool(Tool_Rectangle);
return 0;
}
case IDM_LINETRACK:
{
pApp->m_pPath->SetTool(Tool_LineTrack);
return 0;
}
default:
{
//////
return 0;
}
}
}
case WM_LBUTTONUP:
{
//////
Point2D p;
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
switch(pApp->m_pPath->GetTool()) {
case Tool_Pen:
{
pApp->m_bPaintToBitmap = true;
InvalidateRect(pApp->m_hWnd,NULL,true);
break;
}
case Tool_Ellipse:
{
pApp->m_bPaintToBitmap = true;
InvalidateRect(pApp->m_hWnd,NULL,true);
break;
}
case Tool_Rectangle:
{
pApp->m_bPaintToBitmap = true;
InvalidateRect(pApp->m_hWnd,NULL,true);
break;
}
case Tool_Line:
{
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
case Tool_LineTrack:
{
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
}
return 0;
}
case WM_RBUTTONUP:
{
//////
int x = LOWORD(lParam);
int y = HIWORD(lParam);
switch(pApp->m_pPath->GetTool()) {
case Tool_Line:
{
pApp->m_bPaintToBitmap = true;
InvalidateRect(pApp->m_hWnd,NULL,true);
break;
}
case Tool_LineTrack:
{
pApp->m_bPaintToBitmap = true;
InvalidateRect(pApp->m_hWnd,NULL,true);
break;
}
}
return 0;
}
case WM_LBUTTONDOWN:
{
Point2D p;
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
switch(pApp->m_pPath->GetTool()) {
case Tool_Pen:
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
}
case WM_MOUSEMOVE:
{
Point2D p;
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
if (wParam & MK_LBUTTON) {
switch(pApp->m_pPath->GetTool()) {
case Tool_Pen:
{
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
case Tool_Ellipse:
{
if( pApp->m_pPath->GetLen() >= 1) {
pApp->m_pPath->SetPointAt(1,p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
}
else {
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
}
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
case Tool_Rectangle:
{
if( pApp->m_pPath->GetLen() >= 1) {
pApp->m_pPath->SetPointAt(1,p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
}
else {
pApp->m_pPath->AddPoint(p,pApp->m_pBitmapPainter->GetBmpSize(),pApp->m_pBitmapPainter->GetBmpOffset());
}
InvalidateRect(pApp->m_hWnd,NULL,false);
break;
}
}
}
return 0;
}
case WM_CLOSE:
{
//////
return 0;
}
}
PostQuitMessage(0);
return 0;
}
}
}
return DefWindowProc (hWnd, iMsg, wParam, lParam) ;
}
MyApp::Paint()
void MyApp::Paint()
{
BeginPaint(m_hWnd,&m_PaintStruct);
if (m_bPaintToBitmap) {
Point2D p;
p.x = BMPXOFFSET;
p.y = BMPYOFFSET;
m_pPath->Offset(p);
m_pPath->DrawTo(m_pBitmapPainter->GetMemDC());
m_pPath->ClrPath();
m_pBitmapPainter->Paint(); //this is where BitBlt() occurs
m_bPaintToBitmap = false;
if(m_pBitmapPainter->IsAdjusted() == false) {
m_pBitmapPainter->SetbAdjusted(true);
}
}
else {
m_pBitmapPainter->Paint(); //this is where BitBlt() occurs
m_pPath->DrawTo(m_hDC);
}
EndPaint(m_hWnd,&m_PaintStruct);
}
Any help would much be appreciated.
I think what you're seeing is flicker, not tearing. To minimize flicker, your WM_PAINT should write to the window DC exactly once. Typically, this one operation is a BitBlt:
HDC hdc = BeginPaint(m_hwnd, &m_PaintStruct);
... paint to bitmap ...
BitBlt(hdc, ...); // blt from bitmap to screen
EndPaint(m_hwnd, &m_PaintStruct);
If you paint to the window DC in multiple steps, then you open the window for flicker.
Your description of the problem doesn't match the code. In your description, you say that you're blitting from the compatible DC to the window hDC. But in your code, your BitBlt is followed by a m_pPath->DrawTo(m_hDC). If a refresh occurs during the DrawTo, then the screen will refresh with a partially-drawn view, resulting in flicker.
If you are drawing the entire client area, override WM_ERASEBKGND, and simply return TRUE. That will reduce the flicker.
As others have pointed out; use the HDC given by WM_PAINT, as it may contain clipping regions, and other stuff that may optimize the screen update.
EDIT
If you are not drawing the entire client area, you can perform background painting in the areas you know where your WM_PAINT handler won't paint.
BeginPaint gives you HDC you are supposed to paint to. You are ignoring it.
What OS are you running on ? If its Vista or Windows 7, are you running in some sort of "compatibility mode" disabling desktop compositing ?
Supposedly one of the advantages of the Desktop Window Manager (DWM) introduced with Vista is (source):
In Windows XP, applications update their windows directly when the OS
requests them to. These requests could be executed asynchronously with
respect to the refresh rate of the monitor or to any updates that may
be currently running. The effect of these requests is that the user
sees windows tearing and re-drawing incorrectly or slowly. The DWM
style of window presentation eliminates the tearing artifacts,
providing a high quality desktop experience. The benefit to the end
user is that the system appears to be more responsive and the
experience is cleaner.
ie with the DWM, compositing to the desktop will be synchronized with drawing to avoid ever seeing partially drawn "torn" things.
If that's not it, are you sure you're not confusing "tearing" with background erase flicker or something ?