Mouse... Mice... Meese... Moose?

Started by
5 comments, last by random 23 years, 11 months ago
Hi all, [edit: egads... that looked like crap... trying to format the code some] First time posting here, so I'll try to include all the relevant data... (ie, this is gonna be long Little bit of background here. I'm using DirectDraw, Win32, and all the (*cough*) annoyances of winbl...Windows programming. I've been working on my game for a while now and I have always had an annoying problem. Whenever the mouse is moved, the callback function is flooded with WM_MOUSEMOVE messages and almost everything slows down considerably. My framerate still stays high (50, but I'm filling and animating the screen) but any changes done in the callback function take longer to process. It *looks* like I am losing framerate, but I'm really not. The framerate timer does not change but maybe one or two fps. Here's some sample code:

#include 
... <-- do stuff
LRESULT CALLBACK callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  // update our mouse here.  It just filters out the mouse
  // messages and returns if it used it or not.  If it used
  // it, it skips to the end, otherwise it processes the
  // message
  if (!mouse.update(msg, lparam))
  {
    switch (msg)
    {
      case WM_TIMER:
      {
        switch (wparam)
        {
          case FPS30_TIMER: // every 30ms, a message is sent
          {
            if (++spriteCurr > spriteEnd) spriteCurr = spriteStart;
          } break;
          case FRAMERATE_TIMER:
          {
            itoa(framecount, frtext, 10);
            framecount = 0;
          } break;
        }
      } break;
...
  }
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmd, int show)
{
...
  // set our respective timers to go off
  SetTimer(hwnd, FRAMERATE_TIMER, framerate_timer_speed, NULL);
  SetTimer(hwnd, FPS30_TIMER,     fps30_timer_speed,     NULL);

  while(1)
  {
    static const Point frloc = {0, 0}; // framerate position

    for (int y = 0; y < 30; y++)
    {
      Point spriteLoc = {sprman[spriteCurr].width()>>1, y * sprman[spriteCurr].height()};
      for (int i = 0; i < 40; i++)
      {
        screen.blit(spriteLoc, sprmanager[spriteCurr]);
        spriteLoc.x += sprmanager[spriteCurr].width();
      }
    }

    fontset.print(frloc, frtext); // framerate counter
    screen.publish();
    screen.clear();
    framecount++;

    if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
    {
      if (!GetMessage(&msg, NULL, 0, 0))
        return msg.wParam;
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }

  return 1;
}
Here's my mouse.update function from the beginning of the callback function. The first item up for grabs is the mouse move message so it (AFAIK) should return as soon as possible.

int Mouse::update(const unsigned int msg, const unsigned int lparam)
{
  if (msg & 0x0200) // is it a valid mouse message?
  {
    switch (msg)
    {
      case WM_MOUSEMOVE:   { xy   = lparam; } break; // set x and y
...
I guess my question is, is there anything I can do about it? If so, I'd really love to know what (as it is *Really* starting to grate on my nerves =p ). Any suggestions, no matter how drastic, are welcome... err... let me rephrase that. Any suggestions, barring skipping DirectX and Winbl...Windows... Thanks all and sorry for the length. random --- Wait, you mean I have to actually... THINK?! Edited by - random on 5/16/00 10:32:17 PM
---Wait, you mean I have to actually... THINK?!
Advertisement
Haven''t had to worry about this myself yet, but are you handling your mouse in a seperate thread? Can''t say for sure, but it sounds like you are experiencing a coupling problem. Having a seperate thread for the mouse will decouple the two processing elements. Each thread runs as fast as possible no matter what the other is doing. You have to provide data from the mouse thread to the game processing thread. But the game processing thread runs full tilt even if the mouse thread is slow processing whatever its up to (i.e. processing a few thousand WM_MOUSEMOVES say). This is apparently a common technique for mouse handling in games.

I''m sure someone who has actually dealt with this will ring in with their opinion anytime now.

Mike Roberts
aka milo
mlbobs@telocity.com
That was quick!

I am not using threads at all right now. I actually hammered a lot of code recently and started a rewrite because I had other issues. I guess if I''m gonna put in threads, this is the time to do it, eh?

Thanks for the response, I''ll start reading up on them!

random

---
Wait, you mean I have to actually... THINK?!
---Wait, you mean I have to actually... THINK?!
BAM!

Sorry, but I am ecstatic

I got it to work with only minimal code change. It not only works, but it works BEAUTIFULLY!

Here''s what I did, in case someone else wants to do it as well. This is the full WinMain source code (minus the needless bits). I''ll comment it some too.

First off, if you''re using MSVC++ to program, you''ll need to make some project settings changes. Under Project/Settings, on the C/C++ tab, go to Code Generation and change to "Multithreaded" for the Release version and "Debug Multithreaded" for the Debug version. You''ll need to add a library to link in as well. Under the Link tab, add "libcimt.lib" to be built in.

Next, you also need to #include process.h
Rebuild all and you''re set to use threads!

...#include ...unsigned long FPS30_Thread; // _beginthread handle...// here''s our actual thread function.  I am only going to// be putting time sensitive data in here so as to keep the// clog down as much as possible... then again, it''s threadsvoid FPS30_ThreadProc(void *param){  while (1)  {    if (++spriteCurr > spriteEnd) spriteCurr = spriteStart;    Sleep(30); // snore for 30ms  }}// I''m relegating the callback function to now ONLY handle// messages that I could care less about the timing.// Anything that I need to perform on a timed basis (except// drawing) will be done in a thread.LRESULT CALLBACK callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam){  if (!mouse.update(msg, lparam))  {    switch (msg)    {      case WM_TIMER:      {        switch (wparam)        {          case FRAMERATE_TIMER:          {            itoa(framecount, frtext, 10);            framecount = 0;          } break;        }      } break;      case WM_KEYDOWN:      {        if (wparam == VK_ESCAPE)          PostMessage(hwnd, WM_DESTROY, 0, 0);        keyboard.update(wparam);      } break;      case WM_DESTROY:      {        PostQuitMessage(0);      } break;      case WM_CREATE:      {        // Create our thread using the simpler _beginthread        // function... trust me, the other one is *Nasty*        FPS30_Thread = _beginthread(FPS30_ThreadProc, 0, NULL);      } break;      default:        break;    }  }  return DefWindowProc(hwnd, msg, wparam, lparam);}int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmd, int show){...  // set our respective timers to go off  // this part is of low priority so there is no real reason  // to create a thread for it  SetTimer(hwnd, FRAMERATE_TIMER, framerate_timer_speed, NULL);  while(1)  {    static const Point frloc = {0, 0}; // framerate position    for (int y = 0; y < 30; y++)    {      Point spriteLoc = {sprman[spriteCurr].width()>>1, y * sprman[spriteCurr].height()};      for (int i = 0; i < 40; i++)      {        screen.blit(spriteLoc, sprman[spriteCurr]);        spriteLoc.x += sprman[spriteCurr].width();      }    }    fontset.print(frloc, frtext); // framerate counter    screen.publish();    screen.clear();    framecount++;    if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))    {      if (!GetMessage(&msg, NULL, 0, 0))        return msg.wParam;      TranslateMessage(&msg);      DispatchMessage(&msg);    }  }  return 1;}


That should do it. Hope you understand it if you''re trying it for the first time... it worked *Wonders* for me!



---
Wait, you mean I have to actually... THINK?!
---Wait, you mean I have to actually... THINK?!
While you worry about your FPS dropping, other problems might come up and bite you in the ass. Here are two simple examples:


1) Redrawing the mouse cursor on the GDI surface might cause it to look ghostly as it flickers between visible and not visible states (due to page flipping).

2) As the complexity of your game grows, FPS tends to drop. In your code, the mouse will start to become less and less responsive at the times when you need it most (when all them high-poly multi-textured ghastly goblins are a comin''). Few situations in video games are more frustrating than when your mouse doesn''t move where you want it to go, or when it''s all choppy and the cursor starts teleporting around the screen.

Use a separate thread to manage your mouse code. It''ll help encapsulate that part of your design into an easy to understand section. And if you do it properly you''ll be able to reuse that section of code for every application you write later on.

Good luck.

------When thirsty for life, drink whisky. When thirsty for water, add ice.
Damn. I go to the fridge to get a beer, not thinking anything of the fact I hadn't clicked on the "Reply to Topic" button, not caring that my post will be delayed by 10 minutes or so. What happens, random beats me to the punch. Good beer though.

Edited by - Graylien on May 16, 2000 12:34:01 AM
------When thirsty for life, drink whisky. When thirsty for water, add ice.
Thanks for the response. Quick question about putting the mouse in a separate thread, though.

When I put it in a separate thread, how do I get the messages to the mouse, or should I use a different mechanism for updating it? For example, should I use the DirectInput side of DirectX, or should I just use the Windows functions for updating the location and hits? I have my own assembly bit for reading the mouse, but Windows, in all it''s glory, blows up on it because it''s dos code. *sigh*

At least the beer was good!

random

---
Wait, you mean I have to actually... THINK?!
---Wait, you mean I have to actually... THINK?!

This topic is closed to new replies.

Advertisement