Very helpful topic) Maybe my example of application of approaches described here may also be of some interest? ^^
from panda3d.core import *
class InputManager(object):
# Allow only 1 copy
def __new__(cls, *args, **kwargs):
if '_inst' not in vars(cls):
cls._inst = super(InputManager,cls).__new__(cls, *args, **kwargs)
return cls._inst
def __init__(self, noCompoundEvents=True, dontDriveCamera=True):
if noCompoundEvents:
# Turn off modifier compound events.
# If it is left on, pressing modifier+KEY won't send KEY!
base.mouseWatcherNode.setModifierButtons(ModifierButtons())
base.buttonThrowers[0].node().setModifierButtons(ModifierButtons())
if dontDriveCamera:
# Disable default mouse control of main camera
base.disableMouse()
self._keys_prev = 0
self._keys_changed = 0
self._keys_held = 0
self._keys_down = 0
self._keys_up = 0
self._printscreen = 0
self._wheelAccum = Vec2()
self._wheel = Vec2()
self._lockMouse = None
self._lastUnlockedPos = None
self._limitToScreen = True
self._wasInWindow = False
self._ignoreNextDelta = True
self._mouseSpeed = Vec2()
self._mousePos = Point2()
self._allKeys = {}
self._pollKeys = []
self.setMouseLocked(False, True)
# KeyboardButton.asciiKey() in range 0-127 won't raise an error
# 178 is current maximal key code (wheel_right key)
for i in xrange(178+1):
bh = KeyboardButton.asciiKey(i) if i < 128 else ButtonHandle(i)
if bh.getIndex() == 0: continue # Ignore 'none' button
self._addKey(bh)
base.accept('wheel_up', self._setWheel, [Vec2(0, -1)])
base.accept('wheel_down', self._setWheel, [Vec2(0, 1)])
base.accept('wheel_left', self._setWheel, [Vec2(-1, 0)])
base.accept('wheel_right', self._setWheel, [Vec2(1, 0)])
# Update keys before everything else
taskMgr.add(self._updateKeys, 'inputManager.Keys', sort=-60)
# Update mouse right after dataloop
try:
# See manual about sort and priority
task = taskMgr.getTasksNamed("dataLoop")[0]
taskMgr.add(self._updateMouse, 'inputManager.Mouse',
sort=task.getSort(), priority=task.getPriority()-1)
except IndexError:
taskMgr.add(self._updateMouse, 'inputManager.Mouse', sort=-49)
def _addKey(self, bh):
name = bh.getName()
if name.startswith("wheel"): return
mask = 1 << len(self._allKeys)
self._allKeys[name] = mask
if name == 'print_screen':
# See manual about keyboard support
base.accept('print_screen-up', self._setPrintScreen, [mask])
else:
self._pollKeys.append((bh, mask))
def _setWheel(self, delta):
self._wheelAccum += delta
def _setPrintScreen(self, mask):
self._printscreen = mask
def _updateMouse(self, task):
md = base.win.getPointer(0)
mpos = Point2(md.getX(), md.getY())
isInWindow = md.getInWindow()
if not self._lockMouse:
if self._wasInWindow and isInWindow:
self._mouseSpeed = mpos - self._mousePos
else:
self._mouseSpeed = Vec2()
self._mousePos = mpos
else:
sizeX = base.win.getXSize()
sizeY = base.win.getYSize()
halfX = sizeX * 0.5
halfY = sizeY * 0.5
if isInWindow:
if self._ignoreNextDelta:
self._mouseSpeed = Vec2()
self._ignoreNextDelta = False
else:
self._mouseSpeed = mpos - Point2(halfX, halfY)
self._mousePos += self._mouseSpeed
if self._limitToScreen:
x, y = self._mousePos
x = min(max(x, 0), sizeX)
y = min(max(y, 0), sizeY)
self._mousePos = Point2(x, y)
base.win.movePointer(0, int(halfX), int(halfY))
self._wasInWindow = isInWindow
return task.cont
def _updateKeys(self, task):
self._wheel = self._wheelAccum
self._wheelAccum = Vec2()
mask = 0
# Store function handle in local variable for faster access
isPressed = base.mouseWatcherNode.isButtonDown
for c in self._pollKeys:
if isPressed(c[0]): mask |= c[1]
self._keys_held = mask
self._keys_changed = self._keys_held ^ self._keys_prev
self._keys_down = self._keys_held & self._keys_changed
self._keys_up = self._keys_prev & self._keys_changed
self._keys_prev = self._keys_held
self._keys_up |= self._printscreen
self._printscreen = 0
return task.cont
def getAllKeynames(self):
return sorted(self._allKeys.iterkeys(),
key=lambda (x) : (len(x)>1, x))
def isPressed(self, key=None):
if key is None: return bool(self._keys_held)
return bool(self._keys_held & self._allKeys[key])
def isChanged(self, key=None):
if key is None: return bool(self._keys_changed)
return bool(self._keys_changed & self._allKeys[key])
def isHit(self, key=None):
if key is None: return bool(self._keys_down)
return bool(self._keys_down & self._allKeys[key])
def isReleased(self, key=None):
if key is None: return bool(self._keys_up)
return bool(self._keys_up & self._allKeys[key])
def getWheel(self):
return self._wheel
def getMouseLocked(self):
return self._lockMouse
def setMouseLocked(self, locked, unhideOnUnlock=True, useLastPosition=False):
if self._lockMouse == locked: return
props = WindowProperties()
if locked:
props.setCursorHidden(True)
props.setMouseMode(WindowProperties.MRelative)
props.setRawMice(True)
else:
if unhideOnUnlock: props.setCursorHidden(False)
props.setMouseMode(WindowProperties.MAbsolute)
props.setRawMice(False)
base.win.requestProperties(props)
self._ignoreNextDelta = False
if locked:
md = base.win.getPointer(0)
halfX = base.win.getXSize() * 0.5
halfY = base.win.getYSize() * 0.5
if md.getInWindow():
self._mousePos = Point2(md.getX(), md.getY())
else:
self._mousePos = Point2(halfX, halfY)
self._lastUnlockedPos = self._mousePos
self._ignoreNextDelta = True
else:
if useLastPosition:
x, y = self._lastUnlockedPos
else:
x, y = self._mousePos
if not (self._lockMouse is None):
base.win.movePointer(0, int(x), int(y))
self._lockMouse = locked
def getMouseLimited(self):
return self._limitToScreen
def setMouseLimited(self, limit):
self._limitToScreen = limit
def getMouseSpeed(self, relative=False, aspect=False):
if not (relative or aspect): return self._mouseSpeed
halfX = base.win.getXSize() * 0.5
halfY = base.win.getYSize() * 0.5
x = self._mouseSpeed[0] / halfX
y = -self._mouseSpeed[1] / halfY
if aspect: x *= base.getAspectRatio()
return Vec2(x, y)
def setMouseSpeed(self, speed, relative=False, aspect=False):
x, y = speed
if relative or aspect:
halfX = base.win.getXSize() * 0.5
halfY = base.win.getYSize() * 0.5
if aspect: x /= base.getAspectRatio()
x = x * halfX
y = -y * halfY
self._mouseSpeed = Vec2(x, y)
def getMousePos(self, relative=False, aspect=False):
if not (relative or aspect): return self._mousePos
halfX = base.win.getXSize() * 0.5
halfY = base.win.getYSize() * 0.5
x = (self._mousePos[0] - halfX) / halfX
y = -(self._mousePos[1] - halfY) / halfY
if aspect: x *= base.getAspectRatio()
return Point2(x, y)
def setMousePos(self, pos, relative=False, aspect=False):
x, y = pos
if relative or aspect:
halfX = base.win.getXSize() * 0.5
halfY = base.win.getYSize() * 0.5
if aspect: x /= base.getAspectRatio()
x = x * halfX + halfX
y = -y * halfY + halfY
self._mousePos = Point2(x, y)
if not self._lockMouse:
base.win.movePointer(0, int(x), int(y))