FPS-like mouse-look camera WinAPI problem

Started by
17 comments, last by bmarci 4 years, 5 months ago

I have a problem with implementing mouse-look camera movement, like in FPS games. For me common solution is:

  1. Process WM_MOUSEMOVE event in WndProc
  2. Calculate delta movement from the window's center using event's lParam
  3. Rotate camera
  4. Return cursor back to window's center using SetCursorPos

The problem is when SetCursorPos is called, another WM_MOUSEMOVE event is being fired. So camera rotates back.

What is the common way to create such type of camera on Windows platform (using WinAPI)?

I know that in WM_MOSEMOVE I can check is mouse.x == windowCenter.x and if it is - do nothing, but it's a hack from my point of view. Is there any "non-hacky" way to achieve the goal?

Advertisement
1 hour ago, b2soft said:

and if it is - do nothing

In this case also your delta is zero, so nothing happens as intended with the camera, even without any hack?

3 minutes ago, JoeJ said:

In this case also your delta is zero, so nothing happens as intended with the camera, even without any hack?

In case there are not only mouse-look camera, that can be a problem. Or to switch capture modes?

 

I thought there is some way to not to send a MOUSEMOVE event after SetCursorPos is called

12 minutes ago, b2soft said:

In case there are not only mouse-look camera

How do you mean this? What would be an example?

Capturing the mouse does not help, the additional WM_MOSEMOVE is still geneareted on SetCursorPos.

EDIT: In fact i do what i think you mean with capturing. So if the mouse pointer goes out the window on the right, it comes in at the left. But at this point i have to use SetCursorPos to make that jump happen, so the 'problem' presists.

I do this so i can drag gizmos as long as i want without getting stopped if mouse cursor hits the edges of the screen. (code below - the comments indicate that i stumbled over the same problem :) )

 


if (uMsg == WM_MOUSEMOVE)
			{
				if (globalApplication->MouseCaptured())
				{
					RECT winRect;
					GetClientRect (hWnd, &winRect);
					ClientToScreen (hWnd, (LPPOINT)&winRect.left);
					ClientToScreen (hWnd, (LPPOINT)&winRect.right);
					ClipCursor (&winRect);

					POINT curpos;
					GetCursorPos(&curpos);

					int width =  max (3, winRect.right - winRect.left);
					int height = max (3, winRect.bottom - winRect.top);
			
					bool moved = false;
					if (curpos.x <= winRect.left)					{curpos.x += (width-2);		globalApplication->mouseMulX++; moved = true;}
					else if (curpos.x >= winRect.left + width - 1)	{curpos.x -= (width-2);		globalApplication->mouseMulX--; moved = true;}
					if (curpos.y <= winRect.top)					{curpos.y += (height-2);	globalApplication->mouseMulY++; moved = true;}
					else if (curpos.y >= winRect.top + height - 1)	{curpos.y -= (height-2);	globalApplication->mouseMulY--; moved = true;}
			
					globalApplication->SetMousePosition (
						curpos.x - winRect.left - globalApplication->mouseMulX * (width-2), 
						curpos.y - winRect.top  - globalApplication->mouseMulY * (height-2) );

					if (moved)
					{
						SetCursorPos (curpos.x,curpos.y); // creates a new WM_MOUSEMOVE message
						//UpdateWindow(hWnd); // force WM_PAINT over WM_MOUSEMOVE
					}
				}
				else 
				{
					ClipCursor (0);
					globalApplication->mouseMulX = 0;
					globalApplication->mouseMulY = 0;
					globalApplication->SetMousePosition (LOWORD (lParam), HIWORD (lParam));
				}	
			}

 

You can also read the cursor position at any time with its sibling function GetCursorPos(). There are pro's and con's to using the built in messaging system, but personally I disregard it because of its asynchronous nature. Ususally I'll catch WM_QUIT or the like and that's it.

Just now, Prototype said:

You can also read the cursor position at any time with its sibling function GetCursorPos(). There are pro's and con's to using the built in messaging system, but personally I disregard it because of its asynchronous nature. Ususally I'll catch WM_QUIT or the like and that's it.

in such case I need to manage focus states of the window

2 hours ago, b2soft said:

but it's a hack from my point of view.

The thing is - OS is written wor GUI applications, not for games :( 

So hacks like this are often unavoidable. But maybe someone else knows a trick...

Just now, JoeJ said:

The thing is - OS is written wor GUI applications, not for games :( 

So hacks like this are often unavoidable. But maybe someone else knows a trick...

Thanks for your code. Might help me!

3 minutes ago, Prototype said:

You can also read the cursor position at any time with its sibling function GetCursorPos().

Sounds interesting. But i guess you still have to use SetCursorPos, and a useless message is generated?

 

2 hours ago, b2soft said:

So camera rotates back.

This sounds like you have some logic bug, because if you calculate the delta from the center it should be zero and the camerea should not ratate back.

Sounds your mistake is that you calculate camera orientation directly from the delta, but instead you want to calculate it's rotation (or angular velocity). So it sould be like: camera.angleY += mouseDelta.x * someScalingFactor

Just now, JoeJ said:

Sounds interesting. But i guess you still have to use SetCursorPos, and a useless message is generated?

 

This sounds like you have some logic bug, because if you calculate the delta from the center it should be zero and the camerea should not ratate back.

Sounds your mistake is that you calculate camera orientation directly from the delta, but instead you want to calculate it's rotation (or angular velocity). So it sould be like: camera.angleY += mouseDelta.x * someScalingFactor

No, code is OK. Bug is that the second message contains - delta, since MOUSEMOVE is caused after SetCursorPos(cetner), it moves cursor back, -delta)

This topic is closed to new replies.

Advertisement