Real-Time Strategy Camera

This won’t work verbatim because it relies on some other code, but hopefully it’s clear how everything works. I’ll try to answer questions if you have any. This works best fullscreen - I had some problems getting it to work the way I wanted in a window.

import settings

from direct.showbase.DirectObject import DirectObject
from panda3d.core import VBase3

class RTS_Camera( DirectObject ):
    def __init__(self):
        self.floater = render.attachNewNode('cam_floater')  # Camera looks at this node to create an angle

        self.tolerance = 10             # How many pixels from the edge of the window
        self.cameraMoveDist = 14        # How many units to move the camera
        self.cameraZoomDist = 4         # How many units to zoom the camera
        self.cameraZBounds = [4, 60]    # Lower and upper bounds of camera height
        
        base.disableMouse()                 # Turn off default mouse control
        base.camera.setPos(0, -10, 15)      # Default camera position
        base.camera.lookAt(self.floater)    # Make the camera look at the floater (creates angle)
        
        self.accept( 'wheel_up', self.zoom, [1] )       # Listen for mouse scrolling
        self.accept( 'wheel_down', self.zoom, [-1] )    # for zooming
    
    # This gets run every frame by the game engine.
    # Here we find the position of the cursor - if it is
    # near the edges of the screen (within a tolerance),
    # we will update the position of the camera
    def handleMouseInput(self):
        movementDirection = VBase3(0, 0, 0) # Which direction to move the camera
        
        # Get the position of the cursor if it is in the window
        if( base.mouseWatcherNode.hasMouse() ):
            md = base.win.getPointer(0) 
            x = md.getX() 
            y = md.getY()
            
            # If the cursor is on the edges, move the camera
            if( x + self.tolerance > settings.WIDTH ):
                movementDirection.setX(1)
            elif( x < self.tolerance ):
                movementDirection.setX(-1)
            if( y + self.tolerance > settings.HEIGHT ):
                movementDirection.setY(-1)
            elif( y < self.tolerance ):
                movementDirection.setY(1)
                
            self.moveCamera( movementDirection )
                
    def moveCamera(self, dir):
        deltaPos = dir * self.cameraMoveDist * globalClock.getDt()
        
        self.floater.setPos( self.floater.getPos() + deltaPos )
        base.camera.setPos( base.camera.getPos() + deltaPos )
        
    # Determines the vector from the camera to the floater and moves the
    # camera closer to or farther from the floater along the vector
    def zoom(self, dir):
        camToFloater = self.floater.getPos() - base.camera.getPos()
        camToFloater.normalize()
        camToFloater *= dir * self.cameraZoomDist
        
        newPos = base.camera.getPos() + camToFloater
        z = newPos.getZ()
        
        if( z < self.cameraZBounds[1] and z > self.cameraZBounds[0]):
            base.camera.setPos(newPos)