Reputation: 50
So I'm creating a program, where 5 rectangles are spawned and they move in different directions like DVD Screensaver.
The problem is when I start moving them in infinite loop the program stops working not allowing any input to be made. If I make the loop not infinite, it will stop working until the loop ends, and after that the program allows you to do something.
I think the problem is in the way I try to move the rectangles, but I'm not sure.
void MovePredator(HDC hdc, PAINTSTRUCT ps,int size, int amount)
{
for (;;)
{
FillRect(hdc, &ps.rcPaint, (HBRUSH)(1));
for (int i = 0; i < amount; ++i)
{
int Offset = size / 2;
if (Predator[i].LocationX - Offset == 0 || Predator[i].LocationX + Offset == 1420)
{
Predator[i].MoveX *= -1;
}
if (Predator[i].LocationY - Offset == 0 || Predator[i].LocationY + Offset == 700)
{
Predator[i].MoveY *= -1;
}
Predator[i].LocationX += Predator[i].MoveX;
Predator[i].LocationY += Predator[i].MoveY;
Rectangle(hdc, Predator[i].LocationX - Offset, Predator[i].LocationY - Offset, Predator[i].LocationX + Offset, Predator[i].LocationY + Offset);
}
Sleep(10);
}
}
void SpawnPredator(HDC hdc, int size, int amount)
{
int Offset = size / 2;
for (int i = 0; i < amount; ++i)
{
Predator[i].LocationX = rand() % 1300 + 50;
Predator[i].LocationY = rand() % 600 + 50;
Predator[i].MoveX = rand()%2;
Predator[i].MoveY = rand()%2;
if (Predator[i].MoveX == 0) Predator[i].MoveX = -1;
if (Predator[i].MoveY == 0) Predator[i].MoveY = -1;
Rectangle(hdc, Predator[i].LocationX - Offset, Predator[i].LocationY - Offset, Predator[i].LocationX + Offset, Predator[i].LocationY + Offset);
}
}
This is how the loop interacts (I deleted cases that it doesn't interact with)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
srand(time(NULL));
// TODO: Добавьте сюда любой код прорисовки, использующий HDC...
SpawnPredator(hdc, 50, 5);
MovePredator(hdc, ps, 50, 5);
EndPaint(hWnd, &ps);
}
}
}
Upvotes: 1
Views: 510
Reputation: 50
Thanks to everyone who helped. This is my final setup (I will edit this post if I find a better way to do this)
#define STEP 1
int idtimer = -1;
struct Mob
{
int LocationX = 0;
int LocationY = 0;
int MoveX = 0;
int MoveY = 0;
};
struct Mob Predator[100];
void SpawnPredator(int amount)
{
for (int i = 0; i < amount; ++i)
{
Predator[i].LocationX = rand() % 1300 + 50;
Predator[i].LocationY = rand() % 600 + 50;
Predator[i].MoveX = rand() % 2;
Predator[i].MoveY = rand() % 2;
if (Predator[i].MoveX == 0) Predator[i].MoveX = -STEP;
else Predator[i].MoveX = STEP;
if (Predator[i].MoveY == 0) Predator[i].MoveY = -STEP;
else Predator[i].MoveY = STEP;
}
}
void MovePredator(int size, int amount)
{
for (int i = 0; i < amount; ++i)
{
int Offset = size / 2;
if (Predator[i].LocationX - Offset <= 0 || Predator[i].LocationX + Offset >= 1420)
{
Predator[i].MoveX *= -1;
}
if (Predator[i].LocationY - Offset <= 0 || Predator[i].LocationY + Offset >= 700)
{
Predator[i].MoveY *= -1;
}
Predator[i].LocationX += Predator[i].MoveX;
Predator[i].LocationY += Predator[i].MoveY;
}
}
void PaintPredator(HDC hdc, int size, int amount)
{
for (int i = 0; i < amount; ++i)
{
int Offset = size / 2;
Rectangle(hdc, Predator[i].LocationX - Offset, Predator[i].LocationY - Offset, Predator[i].LocationX + Offset, Predator[i].LocationY + Offset);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
srand(time(NULL));
switch (message)
{
case WM_CREATE:
{
SpawnPredator(5);
SetTimer(hWnd, idtimer = 1, 10, NULL);
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Разобрать выбор в меню:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_TIMER:
{
MovePredator(50, 5);
InvalidateRect(hWnd, NULL, FALSE);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(4));
PaintPredator(hdc, 50, 5);
//Rectangle(hdc, 0, 10, 20, 30);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
Upvotes: 0
Reputation: 595349
DO NOT perform infinite loops inside of a UI message handler. This is the main reason why your app does not work. You are not allowing your app to stay responsive to messages from the OS.
Also, DO NOT perform non-drawing logic inside a paint handler. Updating your predators is not a task that belongs in your WM_PAINT
event at all. Use a timer instead, and have it invalidate the window to trigger a repaint whenever a change occurs. Simply paint the existing predators as-is whenever you are asked to paint the window.
Also, your WndProc()
is missing a call to DefWindowProc()
for all unhandled messages.
Try something more like this:
void MovePredator(int size, int amount)
{
int Offset = size / 2;
for (int i = 0; i < amount; ++i)
{
if (Predator[i].LocationX - Offset == 0 || Predator[i].LocationX + Offset == 1420)
{
Predator[i].MoveX *= -1;
}
if (Predator[i].LocationY - Offset == 0 || Predator[i].LocationY + Offset == 700)
{
Predator[i].MoveY *= -1;
}
Predator[i].LocationX += Predator[i].MoveX;
Predator[i].LocationY += Predator[i].MoveY;
}
}
void SpawnPredator(int amount)
{
for (int i = 0; i < amount; ++i)
{
Predator[i].LocationX = rand() % 1300 + 50;
Predator[i].LocationY = rand() % 600 + 50;
Predator[i].MoveX = rand() % 2;
Predator[i].MoveY = rand() % 2;
if (Predator[i].MoveX == 0)
Predator[i].MoveX = -1;
if (Predator[i].MoveY == 0)
Predator[i].MoveY = -1;
}
}
void PaintPredator(HDC hdc, int size, int amount)
{
int Offset = size / 2;
for (int i = 0; i < amount; ++i)
{
Rectangle(hdc, Predator[i].LocationX - Offset, Predator[i].LocationY - Offset, Predator[i].LocationX + Offset, Predator[i].LocationY + Offset);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
srand(time(NULL));
SetTimer(hWnd, 1, 10, NULL);
return 0;
case WM_TIMER:
SpawnPredator(5);
MovePredator(50, 5);
InvalidateRect(hWnd, NULL);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(1));
PaintPredator(hdc, 50, 5);
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
Upvotes: 2
Reputation: 2937
The reason of this application hang is in nature of Windows application event based design.
What is going on at the background, any windows application is not allowed to access any hardware directly unlike operating system which able to run only one program at time like DOS for example. This is needed to share hardware like video card or mouse between multiple programs. Instead of the direct access to hardware, Windows kernel manipulate the hardware itself when running applications sending special request to the kernel (system calls), Win API is actually a set of functions can be used to send such request to the kernel.
When your application creates or draws into a Window, it actually asking operating system kernel to do it. Kernel select when and how to process this operation, and then use video card device driver to draw (over the hardware abstraction layer or special fast APIs like OpenGL or Direct X etc.).
Another thing - how application would know if user do some input using a mouse or keyboard. Windows stores those inputs in the special internal queue of structures - called events, each application thread have such a queue. Application itself should have a loop which waits for an OS event, and react on them - such loops called run loop and usually looks like following:
MSG messages; // Here messages to the application are saved
// Run the message loop. It will run until GetMessage() returns 0
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
If you are using a library like MFC this loop is provided by the library, however it is still exist.
When you have created a window, you or your library – register a WindowProcedure
callback function, this function react on messages run loop send to a window object like WM_PAINT. Such function usually calls to DefWindowProc
function when your program don’t needs to handle some specific event.
WM_PAINT
occurs when you maximize, minimize, restores or show hide a window, or you can send this message to window yourself using SendMessage function.
If you will do an infinity loop, or block a thread doing the run loop during handling a message – it will freezes run loop, as well as DefWindowProc or another custom messages handling would not be called when they needs to be called, and your application hangs.
Following MSDN article shows how to make an animation you’d like to implement.
Upvotes: 3