CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-panda3d

Panda3D is a framework for 3D rendering and game development for Python and C++ programs.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

input.mddocs/

Input and Controls

Panda3D provides comprehensive input handling for keyboard, mouse, and gamepad devices through an event-driven system that integrates seamlessly with the task and messaging systems.

Capabilities

Keyboard Input

Keyboard input is handled through events generated for key press and release actions.

# Key event patterns
# Press events: key name (e.g., "a", "space", "escape")
# Release events: key name + "-up" (e.g., "a-up", "space-up")

# Common key events
"escape"                 # Escape key
"space"                  # Space bar
"enter"                  # Enter/Return key
"tab"                    # Tab key
"backspace"              # Backspace key
"delete"                 # Delete key

# Letter keys
"a", "a-up"             # A key press/release
"b", "b-up"             # B key press/release
# ... (all letters a-z)

# Number keys
"1", "1-up"             # Number 1 key
"2", "2-up"             # Number 2 key
# ... (numbers 0-9)

# Arrow keys
"arrow_up", "arrow_up-up"
"arrow_down", "arrow_down-up"
"arrow_left", "arrow_left-up"
"arrow_right", "arrow_right-up"

# Function keys
"f1", "f1-up"           # Function keys F1-F12
"f2", "f2-up"
# ... (f1 through f12)

# Modifier keys
"shift", "shift-up"     # Shift keys
"control", "control-up" # Control keys
"alt", "alt-up"         # Alt keys

# Special keys
"home", "home-up"
"end", "end-up"
"page_up", "page_up-up"
"page_down", "page_down-up"
"insert", "insert-up"

Mouse Input

Mouse input includes button clicks, movement, and wheel events.

# Mouse button events
"mouse1"                # Left mouse button press
"mouse1-up"             # Left mouse button release
"mouse2"                # Middle mouse button press
"mouse2-up"             # Middle mouse button release
"mouse3"                # Right mouse button press
"mouse3-up"             # Right mouse button release

# Mouse wheel events
"wheel_up"              # Mouse wheel scroll up
"wheel_down"            # Mouse wheel scroll down

# Mouse movement tracking through MouseWatcher
class MouseWatcher:
    def hasMouse(self) -> bool:
        """Check if mouse is within window."""
    
    def getMouseX(self) -> float:
        """Get mouse X coordinate (-1 to 1)."""
    
    def getMouseY(self) -> float:
        """Get mouse Y coordinate (-1 to 1)."""
    
    def getMouse(self) -> Vec2:
        """Get mouse position as Vec2."""
    
    def isButtonDown(self, button: int) -> bool:
        """Check if mouse button is currently down."""

Input State Tracking

Track the current state of input devices for continuous input handling.

# Access through base.mouseWatcherNode
mouseWatcher = base.mouseWatcherNode

# Check current input states
def isKeyDown(key: str) -> bool:
    """Check if key is currently pressed."""
    return base.mouseWatcherNode.isButtonDown(key)

# Common input state checks
def getMousePos() -> tuple:
    """Get current mouse position."""
    if base.mouseWatcherNode.hasMouse():
        x = base.mouseWatcherNode.getMouseX()
        y = base.mouseWatcherNode.getMouseY()
        return (x, y)
    return None

def getMouseDelta() -> tuple:
    """Get mouse movement since last frame."""
    # Implementation depends on tracking previous position
    pass

Input Event Handling

Bind input events to functions using the event system.

# Basic event binding (from ShowBase or DirectObject)
def accept(event: str, method: callable, extraArgs: list = []) -> None:
    """Bind input event to method."""

def ignore(event: str) -> None:
    """Stop handling input event."""

def ignoreAll() -> None:
    """Stop handling all events."""

# Example event handlers
def handleKeyPress(self):
    """Handle key press event."""
    pass

def handleMouseClick(self):
    """Handle mouse click event."""
    pass

def handleMouseDrag(self):
    """Handle mouse drag event.""" 
    pass

Advanced Input Patterns

Common input handling patterns for games and applications.

# Movement input handling
MOVEMENT_KEYS = {
    "w": Vec3(0, 1, 0),      # Forward
    "s": Vec3(0, -1, 0),     # Backward  
    "a": Vec3(-1, 0, 0),     # Left
    "d": Vec3(1, 0, 0),      # Right
    "q": Vec3(0, 0, 1),      # Up
    "e": Vec3(0, 0, -1)      # Down
}

# Input combinations
def isShiftPressed() -> bool:
    """Check if shift modifier is active."""
    return base.mouseWatcherNode.isButtonDown("shift")

def isCtrlPressed() -> bool:
    """Check if control modifier is active."""
    return base.mouseWatcherNode.isButtonDown("control")

Usage Examples

Basic Input Handling

from direct.showbase.ShowBase import ShowBase
from panda3d.core import Vec3

class InputDemo(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        # Create player object
        self.player = self.loader.loadModel("models/player")
        self.player.reparentTo(self.render)
        self.player.setPos(0, 10, 0)
        
        # Setup input handling
        self.setupInput()
        
        # Movement state
        self.keys_pressed = set()
        self.mouse_pressed = set()
        
        # Start input update task
        self.taskMgr.add(self.updateInput, "update-input")
    
    def setupInput(self):
        """Setup all input event handlers."""
        # Movement keys
        movement_keys = ["w", "a", "s", "d", "q", "e"]
        for key in movement_keys:
            self.accept(key, self.onKeyPress, [key])
            self.accept(f"{key}-up", self.onKeyRelease, [key])
        
        # Action keys
        self.accept("space", self.jump)
        self.accept("shift", self.run)
        self.accept("shift-up", self.walk)
        
        # Mouse input
        self.accept("mouse1", self.onMousePress, [1])
        self.accept("mouse1-up", self.onMouseRelease, [1])
        self.accept("mouse3", self.onMousePress, [3])
        self.accept("mouse3-up", self.onMouseRelease, [3])
        
        # Special keys
        self.accept("escape", self.exitGame)
        self.accept("f1", self.showHelp)
        
        # Mouse wheel
        self.accept("wheel_up", self.zoomIn)
        self.accept("wheel_down", self.zoomOut)
    
    def onKeyPress(self, key):
        """Handle key press."""
        self.keys_pressed.add(key)
        print(f"Key pressed: {key}")
    
    def onKeyRelease(self, key):
        """Handle key release."""
        self.keys_pressed.discard(key)
        print(f"Key released: {key}")
    
    def onMousePress(self, button):
        """Handle mouse button press."""
        self.mouse_pressed.add(button)
        
        # Get mouse position
        if self.mouseWatcherNode.hasMouse():
            x = self.mouseWatcherNode.getMouseX()
            y = self.mouseWatcherNode.getMouseY()
            print(f"Mouse {button} pressed at ({x:.2f}, {y:.2f})")
    
    def onMouseRelease(self, button):
        """Handle mouse button release."""
        self.mouse_pressed.discard(button)
        print(f"Mouse {button} released")
    
    def updateInput(self, task):
        """Update input-based movement."""
        dt = globalClock.getDt()
        
        # Calculate movement vector
        movement = Vec3(0, 0, 0)
        speed = 10.0  # Base speed
        
        # Apply movement keys
        if "w" in self.keys_pressed:
            movement += Vec3(0, 1, 0)
        if "s" in self.keys_pressed:
            movement += Vec3(0, -1, 0)
        if "a" in self.keys_pressed:
            movement += Vec3(-1, 0, 0)
        if "d" in self.keys_pressed:
            movement += Vec3(1, 0, 0)
        if "q" in self.keys_pressed:
            movement += Vec3(0, 0, 1)
        if "e" in self.keys_pressed:
            movement += Vec3(0, 0, -1)
        
        # Apply speed modifiers
        if self.isShiftPressed():
            speed *= 2.0  # Run speed
        
        # Normalize and apply movement
        if movement.length() > 0:
            movement.normalize()
            movement *= speed * dt
            
            current_pos = self.player.getPos()
            self.player.setPos(current_pos + movement)
        
        return task.cont
    
    def isShiftPressed(self):
        """Check if shift is currently pressed."""
        return self.mouseWatcherNode.isButtonDown("shift")
    
    def jump(self):
        """Handle jump action."""
        print("Jump!")
        # Add jump logic here
    
    def run(self):
        """Start running."""
        print("Running...")
    
    def walk(self):
        """Stop running."""
        print("Walking...")
    
    def zoomIn(self):
        """Zoom camera in."""
        pos = self.camera.getPos()
        self.camera.setPos(pos * 0.9)
    
    def zoomOut(self):
        """Zoom camera out."""
        pos = self.camera.getPos()
        self.camera.setPos(pos * 1.1)
    
    def showHelp(self):
        """Show help information."""
        print("Help: WASD to move, Shift to run, Space to jump, ESC to exit")
    
    def exitGame(self):
        """Exit the game."""
        print("Exiting...")
        self.userExit()

app = InputDemo()
app.run()

Advanced Input Controller

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

class InputController(DirectObject):
    """Advanced input handling controller."""
    
    def __init__(self, controlled_object):
        DirectObject.__init__(self)
        
        self.controlled_object = controlled_object
        
        # Input state
        self.keys_down = set()
        self.mouse_buttons_down = set()
        self.previous_mouse_pos = None
        self.mouse_sensitivity = 1.0
        
        # Input settings
        self.move_speed = 10.0
        self.run_multiplier = 2.0
        self.mouse_look_enabled = False
        
        # Setup input bindings
        self.setupInputBindings()
        
        # Start update task
        self.addTask(self.updateInput, "input-controller")
    
    def setupInputBindings(self):
        """Setup all input event handlers."""
        # Movement keys
        movement_keys = ["w", "a", "s", "d", "space", "shift"]
        for key in movement_keys:
            self.accept(key, self.onKeyDown, [key])
            self.accept(f"{key}-up", self.onKeyUp, [key])
        
        # Mouse look
        self.accept("mouse2", self.enableMouseLook)
        self.accept("mouse2-up", self.disableMouseLook)
        
        # Mouse sensitivity
        self.accept("=", self.adjustMouseSensitivity, [0.1])
        self.accept("-", self.adjustMouseSensitivity, [-0.1])
    
    def onKeyDown(self, key):
        """Handle key press."""
        self.keys_down.add(key)
    
    def onKeyUp(self, key):
        """Handle key release."""
        self.keys_down.discard(key)
    
    def enableMouseLook(self):
        """Enable mouse look mode."""
        self.mouse_look_enabled = True
        self.previous_mouse_pos = self.getMousePos()
        
        # Hide cursor
        props = WindowProperties()
        props.setCursorHidden(True)
        base.win.requestProperties(props)
    
    def disableMouseLook(self):
        """Disable mouse look mode."""
        self.mouse_look_enabled = False
        
        # Show cursor
        props = WindowProperties()
        props.setCursorHidden(False)
        base.win.requestProperties(props)
    
    def getMousePos(self):
        """Get current mouse position."""
        if base.mouseWatcherNode.hasMouse():
            return (
                base.mouseWatcherNode.getMouseX(),
                base.mouseWatcherNode.getMouseY()
            )
        return None
    
    def updateInput(self, task):
        """Update input processing."""
        dt = globalClock.getDt()
        
        # Handle movement
        self.updateMovement(dt)
        
        # Handle mouse look
        if self.mouse_look_enabled:
            self.updateMouseLook()
        
        return task.cont
    
    def updateMovement(self, dt):
        """Update object movement based on input."""
        movement = Vec3(0, 0, 0)
        
        # Calculate movement direction
        if "w" in self.keys_down:
            movement += Vec3(0, 1, 0)
        if "s" in self.keys_down:
            movement += Vec3(0, -1, 0)
        if "a" in self.keys_down:
            movement += Vec3(-1, 0, 0)
        if "d" in self.keys_down:
            movement += Vec3(1, 0, 0)
        if "space" in self.keys_down:
            movement += Vec3(0, 0, 1)
        
        # Apply speed
        speed = self.move_speed
        if "shift" in self.keys_down:
            speed *= self.run_multiplier
        
        # Apply movement
        if movement.length() > 0:
            movement.normalize()
            movement *= speed * dt
            
            # Transform movement relative to object orientation
            movement = self.controlled_object.getRelativeVector(
                render, movement
            )
            
            current_pos = self.controlled_object.getPos()
            self.controlled_object.setPos(current_pos + movement)
    
    def updateMouseLook(self):
        """Update mouse look rotation."""
        current_mouse = self.getMousePos()
        
        if current_mouse and self.previous_mouse_pos:
            # Calculate mouse delta
            dx = current_mouse[0] - self.previous_mouse_pos[0]
            dy = current_mouse[1] - self.previous_mouse_pos[1]
            
            # Apply mouse sensitivity
            dx *= self.mouse_sensitivity * 100
            dy *= self.mouse_sensitivity * 100
            
            # Update object rotation
            current_hpr = self.controlled_object.getHpr()
            new_h = current_hpr.getX() - dx  # Horizontal rotation
            new_p = current_hpr.getY() + dy  # Vertical rotation
            
            # Clamp vertical rotation
            new_p = max(-90, min(90, new_p))
            
            self.controlled_object.setHpr(new_h, new_p, 0)
        
        self.previous_mouse_pos = current_mouse
    
    def adjustMouseSensitivity(self, change):
        """Adjust mouse sensitivity."""
        self.mouse_sensitivity = max(0.1, min(5.0, 
                                   self.mouse_sensitivity + change))
        print(f"Mouse sensitivity: {self.mouse_sensitivity:.1f}")
    
    def cleanup(self):
        """Clean up input controller."""
        self.disableMouseLook()
        self.removeAllTasks()
        self.ignoreAll()

Types

from panda3d.core import Vec2

# Mouse coordinates are normalized to -1.0 to 1.0 range
# (0, 0) is center of window
# (-1, -1) is bottom-left corner  
# (1, 1) is top-right corner

# Key names follow specific conventions:
# - Letters: "a" through "z"
# - Numbers: "0" through "9" 
# - Special: "space", "enter", "escape", "tab", etc.
# - Arrows: "arrow_up", "arrow_down", "arrow_left", "arrow_right"
# - Function: "f1" through "f12"
# - Modifiers: "shift", "control", "alt"

# Mouse button numbers:
MOUSE_LEFT = 1      # Left mouse button
MOUSE_MIDDLE = 2    # Middle mouse button  
MOUSE_RIGHT = 3     # Right mouse button

Install with Tessl CLI

npx tessl i tessl/pypi-panda3d

docs

animation.md

application-framework.md

asset-loading.md

audio.md

index.md

input.md

mathematics.md

scene-graph.md

task-event.md

user-interface.md

tile.json