Source code for peng3d.actor.player

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  player.py
#
#  Copyright 2016 notna <notna@apparat.org>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#
#

__all__ = [
    "BasicPlayer",
    "FirstPersonPlayer",
    "FourDirectionalMoveController",
    "EgoMouseRotationalController",
    "BasicFlightController",
]

import math

try:
    import pyglet
except ImportError:
    pass

from . import Actor, RotatableActor, Controller


[docs]class FourDirectionalMoveController(Controller): """ Controller allowing the user to control the actor with the keyboard. You can configure the used keybinds with the :confval:`controls.controls.forward` etc. The keybinds can also be changed with their ``keybindname``\\ , e.g. ``peng3d:actor.<actor uuid>.player.controls.forward`` for forward. The movement speed may also be changed via the :py:attr:`movespeed` instance attribute, which defaults to :confval:`controls.controls.movespeed`\\ . You may also access the currently held keys via :py:attr:`move`\\ , which is a list with 2 items, forwards/backwards and left/right. """ def __init__(self, *args, **kwargs): super(FourDirectionalMoveController, self).__init__(*args, **kwargs) self.move = [0, 0] self.movespeed = self.peng.cfg["controls.controls.movespeed"]
[docs] def registerEventHandlers(self): """ Registers needed keybinds and schedules the :py:meth:`update` Method. You can control what keybinds are used via the :confval:`controls.controls.forward` etc. Configuration Values. """ # Forward self.peng.keybinds.add( self.peng.cfg["controls.controls.forward"], "peng3d:actor.%s.player.controls.forward" % self.actor.uuid, self.on_fwd_down, False, ) # Backward self.peng.keybinds.add( self.peng.cfg["controls.controls.backward"], "peng3d:actor.%s.player.controls.backward" % self.actor.uuid, self.on_bwd_down, False, ) # Strafe Left self.peng.keybinds.add( self.peng.cfg["controls.controls.strafeleft"], "peng3d:actor.%s.player.controls.strafeleft" % self.actor.uuid, self.on_left_down, False, ) # Strafe Right self.peng.keybinds.add( self.peng.cfg["controls.controls.straferight"], "peng3d:actor.%s.player.controls.straferight" % self.actor.uuid, self.on_right_down, False, ) pyglet.clock.schedule_interval(self.update, 1.0 / 60)
[docs] def update(self, dt): """ Should be called regularly to move the actor. This method does nothing if the :py:attr:`enabled` property is set to false. Note that this method is called automatically and should not be manually called. """ if not self.enabled: return speed = self.movespeed d = dt * speed # distance covered this tick. dx, dy, dz = self.get_motion_vector() # New position in space, before accounting for gravity. dx, dy, dz = dx * d, dy * d, dz * d # dy+=self.vert*VERT_SPEED x, y, z = self.actor._pos newpos = dx + x, dy + y, dz + z self.actor.pos = newpos
[docs] def get_motion_vector(self): """ Returns the movement vector according to held buttons and the rotation. :return: 3-Tuple of ``(dx,dy,dz)`` :rtype: tuple """ if any(self.move): x, y = self.actor._rot strafe = math.degrees(math.atan2(*self.move)) y_angle = math.radians(y) x_angle = math.radians(x + strafe) dy = 0.0 dx = math.cos(x_angle) dz = math.sin(x_angle) else: dy = 0.0 dx = 0.0 dz = 0.0 return (dx, dy, dz)
# Event Handlers def on_fwd_down(self, symbol, modifiers, release): self.move[0] -= 1 if not release else -1 def on_bwd_down(self, symbol, modifiers, release): self.move[0] += 1 if not release else -1 def on_left_down(self, symbol, modifiers, release): self.move[1] -= 1 if not release else -1 def on_right_down(self, symbol, modifiers, release): self.move[1] += 1 if not release else -1
[docs]class EgoMouseRotationalController(Controller): """ Controller allowing the user to rotate the actor with the mouse. """ def __init__(self, *args, **kwargs): super(EgoMouseRotationalController, self).__init__(*args, **kwargs)
[docs] def registerEventHandlers(self): """ Registers the motion and drag handlers. Note that because of the way pyglet treats mouse dragging, there is also an handler registered to the on_mouse_drag event. """ self.world.registerEventHandler("on_mouse_motion", self.on_mouse_motion) self.world.registerEventHandler("on_mouse_drag", self.on_mouse_drag)
def on_mouse_motion(self, x, y, dx, dy): if self.enabled: m = self.peng.cfg["controls.mouse.sensitivity"] x, y = self.actor._rot x, y = x + dx * m, y + dy * m y = max(-90, min(90, y)) x %= 360 newrot = (x, y) self.actor.rot = newrot def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): self.on_mouse_motion(x, y, dx, dy)
[docs]class BasicFlightController(Controller): """ Controller allowing the user to move up and down with the jump and crouch controls. The used keybinds may be configured via :confval:`controls.controls.crouch` and :confval:`controls.controls.jump`\\ . The vertical speed used when flying may be configured via :confval:`controls.controls.verticalspeed` or the :py:attr:`speed` attribute. """ def __init__(self, *args, **kwargs): super(BasicFlightController, self).__init__(*args, **kwargs) self.speed = self.peng.cfg["controls.controls.verticalspeed"] self.move = 0
[docs] def registerEventHandlers(self): """ Registers the up and down handlers. Also registers a scheduled function every 60th of a second, causing pyglet to redraw your window with 60fps. """ # Crouch/fly down self.peng.keybinds.add( self.peng.cfg["controls.controls.crouch"], "peng3d:actor.%s.player.controls.crouch" % self.actor.uuid, self.on_crouch_down, False, ) # Jump/fly up self.peng.keybinds.add( self.peng.cfg["controls.controls.jump"], "peng3d:actor.%s.player.controls.jump" % self.actor.uuid, self.on_jump_down, False, ) pyglet.clock.schedule_interval(self.update, 1.0 / 60)
[docs] def update(self, dt): """ Should be called regularly to move the actor. This method does nothing if the :py:attr:`enabled` property is set to False. This method is called automatically and should not be called manually. """ if not self.enabled: return dy = self.speed * dt * self.move x, y, z = self.actor._pos newpos = x, dy + y, z self.actor.pos = newpos
def on_crouch_down(self, symbol, modifiers, release): self.move -= 1 if not release else -1 def on_jump_down(self, symbol, modifiers, release): self.move += 1 if not release else -1
[docs]class BasicPlayer(RotatableActor): """ Basic Player class, subclass of :py:class:`RotatableActor()`\\ . This class adds no features currently, it can be used to identify player actors via :py:func:`isinstance()`\\ . """ pass
[docs]class FirstPersonPlayer(BasicPlayer): """ Old class allowing to create standard first-person players easily. :deprecated: See :py:class:`EgoMouseRotationalController()` and :py:class:`FourDirectionalMoveController()` instead """ def __init__(self, peng, world, uuid=None, pos=[0, 0, 0], rot=[0, 0]): super(FirstPersonPlayer, self).__init__(peng, world, uuid, pos, rot) self.move = [0, 0] self.movespeed = self.peng.cfg["controls.controls.movespeed"] self.active = True # Event handler registration # Forward self.peng.keybinds.add( self.peng.cfg["controls.controls.forward"], "peng3d:actor.player.controls.forward", self.on_fwd_down, ) self.peng.keybinds.add( "release-" + self.peng.cfg["controls.controls.forward"], "peng3d:actor.player.controls.forward.release", self.on_fwd_up, ) # Backward self.peng.keybinds.add( self.peng.cfg["controls.controls.backward"], "peng3d:actor.player.controls.backward", self.on_bwd_down, ) self.peng.keybinds.add( "release-" + self.peng.cfg["controls.controls.backward"], "peng3d:actor.player.controls.backward.release", self.on_bwd_up, ) # Strafe Left self.peng.keybinds.add( self.peng.cfg["controls.controls.strafeleft"], "peng3d:actor.player.controls.strafeleft", self.on_left_down, ) self.peng.keybinds.add( "release-" + self.peng.cfg["controls.controls.strafeleft"], "peng3d:actor.player.controls.strafeleft.release", self.on_left_up, ) # Strafe Right self.peng.keybinds.add( self.peng.cfg["controls.controls.straferight"], "peng3d:actor.player.controls.straferight", self.on_right_down, ) self.peng.keybinds.add( "release-" + self.peng.cfg["controls.controls.straferight"], "peng3d:actor.player.controls.straferight.release", self.on_right_up, ) # Mouse self.world.registerEventHandler("on_mouse_motion", self.on_mouse_motion) self.world.registerEventHandler("on_mouse_drag", self.on_mouse_drag) pyglet.clock.schedule_interval(self.update, 1.0 / 60)
[docs] def update(self, dt): """ Internal method used for moving the player. :param float dt: Time delta since the last call to this method """ speed = self.movespeed d = dt * speed # distance covered this tick. dx, dy, dz = self.get_motion_vector() # New position in space, before accounting for gravity. dx, dy, dz = dx * d, dy * d, dz * d # dy+=self.vert*VERT_SPEED x, y, z = self._pos newpos = dx + x, dy + y, dz + z self.pos = newpos
[docs] def get_motion_vector(self): """ Returns the movement vector according to held buttons and the rotation. :return: 3-Tuple of ``(dx,dy,dz)`` :rtype: tuple """ if any(self.move): x, y = self._rot strafe = math.degrees(math.atan2(*self.move)) y_angle = math.radians(y) x_angle = math.radians(x + strafe) dy = 0.0 dx = math.cos(x_angle) dz = math.sin(x_angle) else: dy = 0.0 dx = 0.0 dz = 0.0 return (dx, dy, dz)
# Event Handlers def on_fwd_down(self, symbol, modifiers): self.move[0] -= 1 def on_fwd_up(self, symbol, modifiers): self.move[0] += 1 def on_bwd_down(self, symbol, modifiers): self.move[0] += 1 def on_bwd_up(self, symbol, modifiers): self.move[0] -= 1 def on_left_down(self, symbol, modifiers): self.move[1] -= 1 def on_left_up(self, symbol, modifiers): self.move[1] += 1 def on_right_down(self, symbol, modifiers): self.move[1] += 1 def on_right_up(self, symbol, modifiers): self.move[1] -= 1 def on_mouse_motion(self, x, y, dx, dy): if self.active: m = 0.15 x, y = self._rot x, y = x + dx * m, y + dy * m y = max(-90, min(90, y)) x %= 360 newrot = (x, y) self.rot = newrot def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): self.on_mouse_motion(x, y, dx, dy)