0
# Input and Controls
1
2
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.
3
4
## Capabilities
5
6
### Keyboard Input
7
8
Keyboard input is handled through events generated for key press and release actions.
9
10
```python { .api }
11
# Key event patterns
12
# Press events: key name (e.g., "a", "space", "escape")
13
# Release events: key name + "-up" (e.g., "a-up", "space-up")
14
15
# Common key events
16
"escape" # Escape key
17
"space" # Space bar
18
"enter" # Enter/Return key
19
"tab" # Tab key
20
"backspace" # Backspace key
21
"delete" # Delete key
22
23
# Letter keys
24
"a", "a-up" # A key press/release
25
"b", "b-up" # B key press/release
26
# ... (all letters a-z)
27
28
# Number keys
29
"1", "1-up" # Number 1 key
30
"2", "2-up" # Number 2 key
31
# ... (numbers 0-9)
32
33
# Arrow keys
34
"arrow_up", "arrow_up-up"
35
"arrow_down", "arrow_down-up"
36
"arrow_left", "arrow_left-up"
37
"arrow_right", "arrow_right-up"
38
39
# Function keys
40
"f1", "f1-up" # Function keys F1-F12
41
"f2", "f2-up"
42
# ... (f1 through f12)
43
44
# Modifier keys
45
"shift", "shift-up" # Shift keys
46
"control", "control-up" # Control keys
47
"alt", "alt-up" # Alt keys
48
49
# Special keys
50
"home", "home-up"
51
"end", "end-up"
52
"page_up", "page_up-up"
53
"page_down", "page_down-up"
54
"insert", "insert-up"
55
```
56
57
### Mouse Input
58
59
Mouse input includes button clicks, movement, and wheel events.
60
61
```python { .api }
62
# Mouse button events
63
"mouse1" # Left mouse button press
64
"mouse1-up" # Left mouse button release
65
"mouse2" # Middle mouse button press
66
"mouse2-up" # Middle mouse button release
67
"mouse3" # Right mouse button press
68
"mouse3-up" # Right mouse button release
69
70
# Mouse wheel events
71
"wheel_up" # Mouse wheel scroll up
72
"wheel_down" # Mouse wheel scroll down
73
74
# Mouse movement tracking through MouseWatcher
75
class MouseWatcher:
76
def hasMouse(self) -> bool:
77
"""Check if mouse is within window."""
78
79
def getMouseX(self) -> float:
80
"""Get mouse X coordinate (-1 to 1)."""
81
82
def getMouseY(self) -> float:
83
"""Get mouse Y coordinate (-1 to 1)."""
84
85
def getMouse(self) -> Vec2:
86
"""Get mouse position as Vec2."""
87
88
def isButtonDown(self, button: int) -> bool:
89
"""Check if mouse button is currently down."""
90
```
91
92
### Input State Tracking
93
94
Track the current state of input devices for continuous input handling.
95
96
```python { .api }
97
# Access through base.mouseWatcherNode
98
mouseWatcher = base.mouseWatcherNode
99
100
# Check current input states
101
def isKeyDown(key: str) -> bool:
102
"""Check if key is currently pressed."""
103
return base.mouseWatcherNode.isButtonDown(key)
104
105
# Common input state checks
106
def getMousePos() -> tuple:
107
"""Get current mouse position."""
108
if base.mouseWatcherNode.hasMouse():
109
x = base.mouseWatcherNode.getMouseX()
110
y = base.mouseWatcherNode.getMouseY()
111
return (x, y)
112
return None
113
114
def getMouseDelta() -> tuple:
115
"""Get mouse movement since last frame."""
116
# Implementation depends on tracking previous position
117
pass
118
```
119
120
### Input Event Handling
121
122
Bind input events to functions using the event system.
123
124
```python { .api }
125
# Basic event binding (from ShowBase or DirectObject)
126
def accept(event: str, method: callable, extraArgs: list = []) -> None:
127
"""Bind input event to method."""
128
129
def ignore(event: str) -> None:
130
"""Stop handling input event."""
131
132
def ignoreAll() -> None:
133
"""Stop handling all events."""
134
135
# Example event handlers
136
def handleKeyPress(self):
137
"""Handle key press event."""
138
pass
139
140
def handleMouseClick(self):
141
"""Handle mouse click event."""
142
pass
143
144
def handleMouseDrag(self):
145
"""Handle mouse drag event."""
146
pass
147
```
148
149
### Advanced Input Patterns
150
151
Common input handling patterns for games and applications.
152
153
```python { .api }
154
# Movement input handling
155
MOVEMENT_KEYS = {
156
"w": Vec3(0, 1, 0), # Forward
157
"s": Vec3(0, -1, 0), # Backward
158
"a": Vec3(-1, 0, 0), # Left
159
"d": Vec3(1, 0, 0), # Right
160
"q": Vec3(0, 0, 1), # Up
161
"e": Vec3(0, 0, -1) # Down
162
}
163
164
# Input combinations
165
def isShiftPressed() -> bool:
166
"""Check if shift modifier is active."""
167
return base.mouseWatcherNode.isButtonDown("shift")
168
169
def isCtrlPressed() -> bool:
170
"""Check if control modifier is active."""
171
return base.mouseWatcherNode.isButtonDown("control")
172
```
173
174
## Usage Examples
175
176
### Basic Input Handling
177
178
```python
179
from direct.showbase.ShowBase import ShowBase
180
from panda3d.core import Vec3
181
182
class InputDemo(ShowBase):
183
def __init__(self):
184
ShowBase.__init__(self)
185
186
# Create player object
187
self.player = self.loader.loadModel("models/player")
188
self.player.reparentTo(self.render)
189
self.player.setPos(0, 10, 0)
190
191
# Setup input handling
192
self.setupInput()
193
194
# Movement state
195
self.keys_pressed = set()
196
self.mouse_pressed = set()
197
198
# Start input update task
199
self.taskMgr.add(self.updateInput, "update-input")
200
201
def setupInput(self):
202
"""Setup all input event handlers."""
203
# Movement keys
204
movement_keys = ["w", "a", "s", "d", "q", "e"]
205
for key in movement_keys:
206
self.accept(key, self.onKeyPress, [key])
207
self.accept(f"{key}-up", self.onKeyRelease, [key])
208
209
# Action keys
210
self.accept("space", self.jump)
211
self.accept("shift", self.run)
212
self.accept("shift-up", self.walk)
213
214
# Mouse input
215
self.accept("mouse1", self.onMousePress, [1])
216
self.accept("mouse1-up", self.onMouseRelease, [1])
217
self.accept("mouse3", self.onMousePress, [3])
218
self.accept("mouse3-up", self.onMouseRelease, [3])
219
220
# Special keys
221
self.accept("escape", self.exitGame)
222
self.accept("f1", self.showHelp)
223
224
# Mouse wheel
225
self.accept("wheel_up", self.zoomIn)
226
self.accept("wheel_down", self.zoomOut)
227
228
def onKeyPress(self, key):
229
"""Handle key press."""
230
self.keys_pressed.add(key)
231
print(f"Key pressed: {key}")
232
233
def onKeyRelease(self, key):
234
"""Handle key release."""
235
self.keys_pressed.discard(key)
236
print(f"Key released: {key}")
237
238
def onMousePress(self, button):
239
"""Handle mouse button press."""
240
self.mouse_pressed.add(button)
241
242
# Get mouse position
243
if self.mouseWatcherNode.hasMouse():
244
x = self.mouseWatcherNode.getMouseX()
245
y = self.mouseWatcherNode.getMouseY()
246
print(f"Mouse {button} pressed at ({x:.2f}, {y:.2f})")
247
248
def onMouseRelease(self, button):
249
"""Handle mouse button release."""
250
self.mouse_pressed.discard(button)
251
print(f"Mouse {button} released")
252
253
def updateInput(self, task):
254
"""Update input-based movement."""
255
dt = globalClock.getDt()
256
257
# Calculate movement vector
258
movement = Vec3(0, 0, 0)
259
speed = 10.0 # Base speed
260
261
# Apply movement keys
262
if "w" in self.keys_pressed:
263
movement += Vec3(0, 1, 0)
264
if "s" in self.keys_pressed:
265
movement += Vec3(0, -1, 0)
266
if "a" in self.keys_pressed:
267
movement += Vec3(-1, 0, 0)
268
if "d" in self.keys_pressed:
269
movement += Vec3(1, 0, 0)
270
if "q" in self.keys_pressed:
271
movement += Vec3(0, 0, 1)
272
if "e" in self.keys_pressed:
273
movement += Vec3(0, 0, -1)
274
275
# Apply speed modifiers
276
if self.isShiftPressed():
277
speed *= 2.0 # Run speed
278
279
# Normalize and apply movement
280
if movement.length() > 0:
281
movement.normalize()
282
movement *= speed * dt
283
284
current_pos = self.player.getPos()
285
self.player.setPos(current_pos + movement)
286
287
return task.cont
288
289
def isShiftPressed(self):
290
"""Check if shift is currently pressed."""
291
return self.mouseWatcherNode.isButtonDown("shift")
292
293
def jump(self):
294
"""Handle jump action."""
295
print("Jump!")
296
# Add jump logic here
297
298
def run(self):
299
"""Start running."""
300
print("Running...")
301
302
def walk(self):
303
"""Stop running."""
304
print("Walking...")
305
306
def zoomIn(self):
307
"""Zoom camera in."""
308
pos = self.camera.getPos()
309
self.camera.setPos(pos * 0.9)
310
311
def zoomOut(self):
312
"""Zoom camera out."""
313
pos = self.camera.getPos()
314
self.camera.setPos(pos * 1.1)
315
316
def showHelp(self):
317
"""Show help information."""
318
print("Help: WASD to move, Shift to run, Space to jump, ESC to exit")
319
320
def exitGame(self):
321
"""Exit the game."""
322
print("Exiting...")
323
self.userExit()
324
325
app = InputDemo()
326
app.run()
327
```
328
329
### Advanced Input Controller
330
331
```python
332
from direct.showbase.DirectObject import DirectObject
333
from panda3d.core import Vec3
334
335
class InputController(DirectObject):
336
"""Advanced input handling controller."""
337
338
def __init__(self, controlled_object):
339
DirectObject.__init__(self)
340
341
self.controlled_object = controlled_object
342
343
# Input state
344
self.keys_down = set()
345
self.mouse_buttons_down = set()
346
self.previous_mouse_pos = None
347
self.mouse_sensitivity = 1.0
348
349
# Input settings
350
self.move_speed = 10.0
351
self.run_multiplier = 2.0
352
self.mouse_look_enabled = False
353
354
# Setup input bindings
355
self.setupInputBindings()
356
357
# Start update task
358
self.addTask(self.updateInput, "input-controller")
359
360
def setupInputBindings(self):
361
"""Setup all input event handlers."""
362
# Movement keys
363
movement_keys = ["w", "a", "s", "d", "space", "shift"]
364
for key in movement_keys:
365
self.accept(key, self.onKeyDown, [key])
366
self.accept(f"{key}-up", self.onKeyUp, [key])
367
368
# Mouse look
369
self.accept("mouse2", self.enableMouseLook)
370
self.accept("mouse2-up", self.disableMouseLook)
371
372
# Mouse sensitivity
373
self.accept("=", self.adjustMouseSensitivity, [0.1])
374
self.accept("-", self.adjustMouseSensitivity, [-0.1])
375
376
def onKeyDown(self, key):
377
"""Handle key press."""
378
self.keys_down.add(key)
379
380
def onKeyUp(self, key):
381
"""Handle key release."""
382
self.keys_down.discard(key)
383
384
def enableMouseLook(self):
385
"""Enable mouse look mode."""
386
self.mouse_look_enabled = True
387
self.previous_mouse_pos = self.getMousePos()
388
389
# Hide cursor
390
props = WindowProperties()
391
props.setCursorHidden(True)
392
base.win.requestProperties(props)
393
394
def disableMouseLook(self):
395
"""Disable mouse look mode."""
396
self.mouse_look_enabled = False
397
398
# Show cursor
399
props = WindowProperties()
400
props.setCursorHidden(False)
401
base.win.requestProperties(props)
402
403
def getMousePos(self):
404
"""Get current mouse position."""
405
if base.mouseWatcherNode.hasMouse():
406
return (
407
base.mouseWatcherNode.getMouseX(),
408
base.mouseWatcherNode.getMouseY()
409
)
410
return None
411
412
def updateInput(self, task):
413
"""Update input processing."""
414
dt = globalClock.getDt()
415
416
# Handle movement
417
self.updateMovement(dt)
418
419
# Handle mouse look
420
if self.mouse_look_enabled:
421
self.updateMouseLook()
422
423
return task.cont
424
425
def updateMovement(self, dt):
426
"""Update object movement based on input."""
427
movement = Vec3(0, 0, 0)
428
429
# Calculate movement direction
430
if "w" in self.keys_down:
431
movement += Vec3(0, 1, 0)
432
if "s" in self.keys_down:
433
movement += Vec3(0, -1, 0)
434
if "a" in self.keys_down:
435
movement += Vec3(-1, 0, 0)
436
if "d" in self.keys_down:
437
movement += Vec3(1, 0, 0)
438
if "space" in self.keys_down:
439
movement += Vec3(0, 0, 1)
440
441
# Apply speed
442
speed = self.move_speed
443
if "shift" in self.keys_down:
444
speed *= self.run_multiplier
445
446
# Apply movement
447
if movement.length() > 0:
448
movement.normalize()
449
movement *= speed * dt
450
451
# Transform movement relative to object orientation
452
movement = self.controlled_object.getRelativeVector(
453
render, movement
454
)
455
456
current_pos = self.controlled_object.getPos()
457
self.controlled_object.setPos(current_pos + movement)
458
459
def updateMouseLook(self):
460
"""Update mouse look rotation."""
461
current_mouse = self.getMousePos()
462
463
if current_mouse and self.previous_mouse_pos:
464
# Calculate mouse delta
465
dx = current_mouse[0] - self.previous_mouse_pos[0]
466
dy = current_mouse[1] - self.previous_mouse_pos[1]
467
468
# Apply mouse sensitivity
469
dx *= self.mouse_sensitivity * 100
470
dy *= self.mouse_sensitivity * 100
471
472
# Update object rotation
473
current_hpr = self.controlled_object.getHpr()
474
new_h = current_hpr.getX() - dx # Horizontal rotation
475
new_p = current_hpr.getY() + dy # Vertical rotation
476
477
# Clamp vertical rotation
478
new_p = max(-90, min(90, new_p))
479
480
self.controlled_object.setHpr(new_h, new_p, 0)
481
482
self.previous_mouse_pos = current_mouse
483
484
def adjustMouseSensitivity(self, change):
485
"""Adjust mouse sensitivity."""
486
self.mouse_sensitivity = max(0.1, min(5.0,
487
self.mouse_sensitivity + change))
488
print(f"Mouse sensitivity: {self.mouse_sensitivity:.1f}")
489
490
def cleanup(self):
491
"""Clean up input controller."""
492
self.disableMouseLook()
493
self.removeAllTasks()
494
self.ignoreAll()
495
```
496
497
## Types
498
499
```python { .api }
500
from panda3d.core import Vec2
501
502
# Mouse coordinates are normalized to -1.0 to 1.0 range
503
# (0, 0) is center of window
504
# (-1, -1) is bottom-left corner
505
# (1, 1) is top-right corner
506
507
# Key names follow specific conventions:
508
# - Letters: "a" through "z"
509
# - Numbers: "0" through "9"
510
# - Special: "space", "enter", "escape", "tab", etc.
511
# - Arrows: "arrow_up", "arrow_down", "arrow_left", "arrow_right"
512
# - Function: "f1" through "f12"
513
# - Modifiers: "shift", "control", "alt"
514
515
# Mouse button numbers:
516
MOUSE_LEFT = 1 # Left mouse button
517
MOUSE_MIDDLE = 2 # Middle mouse button
518
MOUSE_RIGHT = 3 # Right mouse button
519
```