gamepad.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. #!/usr/bin/env python
  2. '''
  3. Demonstrate usage of gamepads and other input devices
  4. In this sample you can use a gamepad type device to control the camera and
  5. show some messages on screen. Using the left stick on the controler will
  6. move the camera where the right stick will rotate the camera.
  7. '''
  8. from direct.showbase.ShowBase import ShowBase
  9. from panda3d.core import TextNode, InputDevice, loadPrcFileData, Vec3
  10. from panda3d.core import TextPropertiesManager
  11. from direct.gui.OnscreenText import OnscreenText
  12. loadPrcFileData("", """
  13. default-fov 60
  14. notify-level-device debug
  15. """)
  16. # Informational text in the bottom-left corner. We use the special \5
  17. # character to embed an image representing the gamepad buttons.
  18. INFO_TEXT = """Move \5lstick\5 to strafe, \5rstick\5 to turn
  19. Press \5ltrigger\5 and \5rtrigger\5 to go down/up
  20. Press \5face_x\5 to reset camera"""
  21. class App(ShowBase):
  22. def __init__(self):
  23. ShowBase.__init__(self)
  24. # Print all events sent through the messenger
  25. #self.messenger.toggleVerbose()
  26. # Load the graphics for the gamepad buttons and register them, so that
  27. # we can embed them in our information text.
  28. graphics = loader.loadModel("models/xbone-icons.egg")
  29. mgr = TextPropertiesManager.getGlobalPtr()
  30. for name in ["face_a", "face_b", "face_x", "face_y", "ltrigger", "rtrigger", "lstick", "rstick"]:
  31. graphic = graphics.find("**/" + name)
  32. graphic.setScale(1.5)
  33. mgr.setGraphic(name, graphic)
  34. graphic.setZ(-0.5)
  35. # Show the informational text in the corner.
  36. self.lblInfo = OnscreenText(
  37. parent = self.a2dBottomLeft,
  38. pos = (0.1, 0.3),
  39. fg = (1, 1, 1, 1),
  40. bg = (0.2, 0.2, 0.2, 0.9),
  41. align = TextNode.A_left,
  42. text = INFO_TEXT)
  43. self.lblInfo.textNode.setCardAsMargin(0.5, 0.5, 0.5, 0.2)
  44. self.lblWarning = OnscreenText(
  45. text = "No devices found",
  46. fg=(1,0,0,1),
  47. scale = .25)
  48. self.lblAction = OnscreenText(
  49. text = "Action",
  50. fg=(1,1,1,1),
  51. scale = .15)
  52. self.lblAction.hide()
  53. # Is there a gamepad connected?
  54. self.gamepad = None
  55. devices = self.devices.getDevices(InputDevice.DeviceClass.gamepad)
  56. if devices:
  57. self.connect(devices[0])
  58. # Accept device dis-/connection events
  59. self.accept("connect-device", self.connect)
  60. self.accept("disconnect-device", self.disconnect)
  61. self.accept("escape", exit)
  62. # Accept button events of the first connected gamepad
  63. self.accept("gamepad-back", exit)
  64. self.accept("gamepad-start", exit)
  65. self.accept("gamepad-face_x", self.reset)
  66. self.accept("gamepad-face_a", self.action, extraArgs=["face_a"])
  67. self.accept("gamepad-face_a-up", self.actionUp)
  68. self.accept("gamepad-face_b", self.action, extraArgs=["face_b"])
  69. self.accept("gamepad-face_b-up", self.actionUp)
  70. self.accept("gamepad-face_y", self.action, extraArgs=["face_y"])
  71. self.accept("gamepad-face_y-up", self.actionUp)
  72. self.environment = loader.loadModel("environment")
  73. self.environment.reparentTo(render)
  74. # Disable the default mouse-camera controls since we need to handle
  75. # our own camera controls.
  76. self.disableMouse()
  77. self.reset()
  78. self.taskMgr.add(self.moveTask, "movement update task")
  79. def connect(self, device):
  80. """Event handler that is called when a device is discovered."""
  81. # We're only interested if this is a gamepad and we don't have a
  82. # gamepad yet.
  83. if device.device_class == InputDevice.DeviceClass.gamepad and not self.gamepad:
  84. print("Found %s" % (device))
  85. self.gamepad = device
  86. # Enable this device to ShowBase so that we can receive events.
  87. # We set up the events with a prefix of "gamepad-".
  88. self.attachInputDevice(device, prefix="gamepad")
  89. # Hide the warning that we have no devices.
  90. self.lblWarning.hide()
  91. def disconnect(self, device):
  92. """Event handler that is called when a device is removed."""
  93. if self.gamepad != device:
  94. # We don't care since it's not our gamepad.
  95. return
  96. # Tell ShowBase that the device is no longer needed.
  97. print("Disconnected %s" % (device))
  98. self.detachInputDevice(device)
  99. self.gamepad = None
  100. # Do we have any other gamepads? Attach the first other gamepad.
  101. devices = self.devices.getDevices(InputDevice.DeviceClass.gamepad)
  102. if devices:
  103. self.connect(devices[0])
  104. else:
  105. # No devices. Show the warning.
  106. self.lblWarning.show()
  107. def reset(self):
  108. """Reset the camera to the initial position."""
  109. self.camera.setPosHpr(0, -200, 10, 0, 0, 0)
  110. def action(self, button):
  111. # Just show which button has been pressed.
  112. self.lblAction.text = "Pressed \5%s\5" % button
  113. self.lblAction.show()
  114. def actionUp(self):
  115. # Hide the label showing which button is pressed.
  116. self.lblAction.hide()
  117. def moveTask(self, task):
  118. dt = base.clock.dt
  119. if not self.gamepad:
  120. return task.cont
  121. strafe_speed = 85
  122. vert_speed = 50
  123. turn_speed = 100
  124. # If the left stick is pressed, we will go faster.
  125. lstick = self.gamepad.findButton("lstick")
  126. if lstick.pressed:
  127. strafe_speed *= 2.0
  128. # we will use the first found gamepad
  129. # Move the camera left/right
  130. strafe = Vec3(0)
  131. left_x = self.gamepad.findAxis(InputDevice.Axis.left_x)
  132. left_y = self.gamepad.findAxis(InputDevice.Axis.left_y)
  133. strafe.x = left_x.value
  134. strafe.y = left_y.value
  135. # Apply some deadzone, since the sticks don't center exactly at 0
  136. if strafe.lengthSquared() >= 0.01:
  137. self.camera.setPos(self.camera, strafe * strafe_speed * dt)
  138. # Use the triggers for the vertical position.
  139. trigger_l = self.gamepad.findAxis(InputDevice.Axis.left_trigger)
  140. trigger_r = self.gamepad.findAxis(InputDevice.Axis.right_trigger)
  141. lift = trigger_r.value - trigger_l.value
  142. self.camera.setZ(self.camera.getZ() + (lift * vert_speed * dt))
  143. # Move the camera forward/backward
  144. right_x = self.gamepad.findAxis(InputDevice.Axis.right_x)
  145. right_y = self.gamepad.findAxis(InputDevice.Axis.right_y)
  146. # Again, some deadzone
  147. if abs(right_x.value) >= 0.1 or abs(right_y.value) >= 0.1:
  148. self.camera.setH(self.camera, turn_speed * dt * -right_x.value)
  149. self.camera.setP(self.camera, turn_speed * dt * right_y.value)
  150. # Reset the roll so that the camera remains upright.
  151. self.camera.setR(0)
  152. return task.cont
  153. app = App()
  154. app.run()