所以我正在创建一个程序,其中生成了5个矩形,它们以不同的方向(例如DVD屏幕保护程序)移动。
问题是当我开始无限循环移动它们时,程序停止工作,不允许进行任何输入。如果我使循环不是无限的,它将停止工作直到循环结束,然后程序允许您执行某些操作。
我认为问题出在我尝试移动矩形的方式上,但是我不确定。
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);
}
}
这是循环的交互方式(我删除了不与之交互的情况)
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);
}
}
}
答案 0 :(得分:3)
此应用程序挂起的原因本质上是Windows应用程序基于事件的设计。
后台发生了什么事,不允许Windows应用程序直接访问任何硬件,这与操作系统(例如DOS)一次只能运行一个程序的操作系统不同。需要在多个程序之间共享视频卡或鼠标等硬件。 Windows内核不是直接访问硬件,而是在运行向内核发送特殊请求(系统调用)的应用程序时操纵硬件本身,而Win API实际上是一组函数,可用于将此类请求发送给内核。
当您的应用程序创建或绘制到Window中时,它实际上要求操作系统内核来执行此操作。内核选择何时以及如何处理此操作,然后使用视频卡设备驱动程序进行绘制(通过硬件抽象层或特殊的快速API,如OpenGL或Direct X等)。
另一件事-如果用户使用鼠标或键盘进行某些输入,应用程序将如何知道。 Windows将这些输入存储在特殊的内部结构队列(称为事件)中,每个应用程序线程都有这样的队列。 应用程序本身应该有一个等待OS事件并对其做出反应的循环-这种循环称为运行循环,通常如下所示:
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);
}
如果使用的是MFC之类的库,则该库提供了此循环,但是该循环仍然存在。
创建窗口,您或您的库后,注册一个WindowProcedure
回调函数,该函数会对循环发送到窗口对象(如WM_PAINT)的消息进行响应。当您的程序不需要处理某些特定事件时,此类函数通常会调用DefWindowProc
函数。
WM_PAINT
出现在您最大化,最小化,还原或显示隐藏窗口时,或者您可以使用SendMessage函数自己将此消息发送到窗口。
如果在处理消息时执行无限循环或阻塞运行循环的线程–它将冻结运行循环,并且DefWindowProc或其他自定义消息处理将在需要调用时不被调用,并且您的应用程序挂起。
Following MSDN article显示了如何制作您想要实现的动画。
答案 1 :(得分:1)
请勿在UI消息处理程序内执行无限循环。这是您的应用无法运行的主要原因。您不允许您的应用保持对来自操作系统的消息的响应。
此外,请勿在绘制处理程序内执行非绘制逻辑。更新捕食者根本不是您WM_PAINT
事件中的任务。请改用计时器,并让它使窗口无效,以便在发生更改时触发重新绘制。要求您绘制窗户时,只需照原样绘制现有的掠食者即可。
此外,您的WndProc()
对于所有未处理的邮件都缺少对DefWindowProc()
的呼叫。
尝试更多类似的方法:
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);
}
答案 2 :(得分:0)
感谢所有提供帮助的人。这是我的最终设置(如果我找到更好的方法,将编辑这篇文章)
#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);
}