Basic FPS camera movement

Howdy! Im pretty newbie in Panda and im following a minecraft-type game tutorial.
Thing is, this tutorial’s camera movement is somewhat flawed. This is basically the code:

    def captureMouse(self):
        self.cameraSwingActivated = True

        md = self.win.getPointer(0)
        self.lastMouseX = md.getX()
        self.lastMouseY = md.getY()

        properties = WindowProperties()
        properties.setCursorHidden(True)
        properties.setMouseMode(WindowProperties.MRelative)
        self.win.requestProperties(properties)

    def releaseMouse(self):
        self.cameraSwingActivated = False

        properties = WindowProperties()
        properties.setCursorHidden(False)
        properties.setMouseMode(WindowProperties.MAbsolute)
        self.win.requestProperties(properties)

   def update(self, task):
        dt = globalClock.getDt()
        
        if self.cameraSwingActivated:
            md = self.win.getPointer(0)
            mouseX = md.getX()
            mouseY = md.getY()
            
            mouseChangeX = mouseX - self.lastMouseX
            mouseChangeY = mouseY - self.lastMouseY
            
            self.cameraSwingFactor = 10
            
            currentH = self.camera.getH()
            currentP = self.camera.getP()
            
            self.camera.setHpr(
                currentH - mouseChangeX * dt * self.cameraSwingFactor,
                min(90, max(-90, currentP - mouseChangeY * dt * self.cameraSwingFactor)),
                0
            )
            
            self.lastMouseX = mouseX
            self.lastMouseY = mouseY
        
        return task.cont

The problems are such:

  1. The camera can move so much before the mouse go out of the bounds of the game window, making so the camera stops moving.
  2. If set the mouse mode to MConfined the mouse will stop moving once it reaches the window bounds, thus stopping the camera
  3. Setting the mouse position with
base.win.movePointer(0, int(self.win.getXSize() / 2), int(self.win.getYSize() / 2))

makes the camera flicker a lot due to the mouse moving back and fourth.

In conclusion, is there a solution to make the mouse not leave the window screen but make it still count as being moved if it reaches the window bounds?

if someone knows another way i can do a proper FPS camera id be very thankfull to know, until there i will tinker what i can.

here are some fellow codes that could be of help but i didnt know how to translate to my needs:

In short, I suspect that the code should work–save that the “MRelative” mouse-mode doesn’t (currently) work under Windows.

Instead–either for Windows specifically or just as the sole mechanism–what I believe tends to be done is to, every frame after reading the mouse-position, move the mouse-cursor back to the centre.

This is done via the “movePointer” function of the “GraphicsWindow” class–like so:

# I'm presuming that this is happening in a class derived from
# ShowBase--adjust as called for if not.

# ... Here read the mouse-position and use it as normal ...

# Now, get the size of the window
xSize = self.win.getXSize()
ySize = self.win.getYSize()

# And move the 0th mouse-pointer to the centre of that window
self.win.movePointer(0, xSize // 2, ySize // 2)

There are some nuances that I’m glossing over there, however, in particular for windows that have sizes not divisible by two. But that’s the basic idea.

Alternatively, I believe that there is a fix in the “master” branch of the Panda source-code. It’s set to be included in the 1.11 release–but can be had now if you don’t mind downloading the source and manually building it.

SOLUTION: if you have the same problem then your probably on windows as well, and for us unfortunately theres no basic solution for this. What u can do instead is to use the MConfined mouse mode and wrap the mouse around the screen. This is the code i use:

        dt = globalClock.getDt()
        
        if self.cameraSwingActivated:
            md = self.win.getPointer(0)
            mouseX = md.getX()
            mouseY = md.getY()
               
            mouseChangeX = mouseX - self.lastMouseX
            mouseChangeY = mouseY - self.lastMouseY
            
            if not self.shouldUpdateMouseLastPosition:            
                mouseChangeX = 0
                mouseChangeY = 0
                self.shouldUpdateMouseLastPosition = True
            
            self.cameraSwingFactor = 10
            
            currentH = self.camera.getH()
            currentP = self.camera.getP()
            
            self.camera.setHpr(
                currentH - mouseChangeX * dt * self.cameraSwingFactor,
                min(90, max(-90, currentP - mouseChangeY * dt * self.cameraSwingFactor)),
                0
            )
        
            # wrap the cursor around:
            if mouseX == self.win.getXSize() - 1:
                self.win.movePointer(0, 1, int(mouseY))
                self.shouldUpdateMouseLastPosition = False
                
            else: 
                if mouseX == 0:
                    self.shouldUpdateMouseLastPosition = False
                    self.win.movePointer(0, self.win.getXSize() - 1, int(mouseY))
                    
            if mouseY == self.win.getYSize() - 1:
                self.shouldUpdateMouseLastPosition = False
                self.win.movePointer(0, int(mouseX), 0)
            else: 
                if mouseY == 0:
                    self.shouldUpdateMouseLastPosition = False
                    self.win.movePointer(0, int(mouseX), self.win.getYSize() - 1)
                    
            self.lastMouseX = mouseX
            self.lastMouseY = mouseY

This should be easier once 1.11 comes around, for the time being this code works just fine, but far from ideal, this may cause troubles when needing to do raycasting and alike.

doesnt actually work. I suppose that the mouse keeps snapping to the center, changing its position and as such the camera also snaps. This causes flickering. Luckly for me i found a solution, shout outs to Moguri on discord server for suggesting to wrap the mouse around the screen. Wether or not this will comeback to haunt me later is unknown…

I’ve used it myself, and it can indeed work.

Looking more closely at the code above, the problem, I suspect, is that it seems to be taking the difference between the current mouse-position and the last mouse-position.

(Which in all fairness makes some sense when using the “MConfined” mouse-mode, but not really when using–or in this case emulating the “MRelative” mode.)

What I’d suggest instead is to just use the current mouse-position–since the mouse is reset to the centre of the screen after reading its position–its position in in a given frame is essentially the movement made since the last frame.

Something like this, in brief pseudo-code:

mouseData = getPointer(...)
x = mouseData.getX()
y = mouseData.getY()

xSize, ySize = win.size
xSize = xSize // 2
ySize = ySize //2

# Offset the coordinates such that the centre of
# the screen is (0, 0)
mouseMovementX = x - xSize
mouseMovementY = y - ySize

win.movePointer(...)
1 Like