示例#1
0
class Jumping(CollisionSystem):
    '''
        Make the character jump.

        Components used :func:`wecs.core.and_filter` 'character'
            | :class:`wecs.panda3d.character.CharacterController`
            | :class:`wecs.panda3d.character.JumpingMovement`
            | :class:`wecs.panda3d.character.FallingMovement`
            | :class:`wecs.panda3d.model.Scene`
            | :class:`wecs.panda3d.model.Clock`
            | :class:`wecs.panda3d.model.Model`
    '''
    entity_filters = {
        'character': and_filter([
            Proxy('scene_node'),
            Proxy('character_node'),
            Clock,
            CharacterController,
            JumpingMovement,
            FallingMovement,
        ]),
    }
    proxies = {
        'character_node': ProxyType(Model, 'node'),
        'scene_node': ProxyType(Model, 'parent'),
    }

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            controller = entity[CharacterController]
            falling_movement = entity[FallingMovement]
            jumping_movement = entity[JumpingMovement]
            if controller.jumps and falling_movement.ground_contact:
                falling_movement.inertia += jumping_movement.impulse
示例#2
0
class StartBallMotion(System):
    """
    StartBallMotion ensures that the game restarts after the start key is pressed.
    """
    entity_filters = {
        'ball': and_filter([
            Proxy('model'),
            Resting,
            Ball,
        ]),
    }
    start_key = KeyboardButton.space()

    def update(self, entities_by_filter):
        """
        Check whether the resting ball should be started?
        Note that restarting the ball's movement means removing the Entity's
        Resting Component, and adding it's Movement component with the desired
        direction.
        """

        start_key_is_pressed = base.mouseWatcherNode.is_button_down(
            StartBallMotion.start_key)

        if start_key_is_pressed:
            for entity in set(entities_by_filter['ball']):
                del entity[Resting]
                entity[Movement] = Movement(
                    direction=Vec3(-0.1 * randrange(5, 10), 0, 0))
示例#3
0
class BallTouchesBoundary(System):
    """
    BallTouchesBoundary ensures the balls is bounced of the top and bottom
    of the court.
    """
    entity_filters = {
        'ball': and_filter([
            Proxy('model'),
            Movement,
            Ball,
        ]),
    }

    def update(self, entities_by_filter):
        for entity in entities_by_filter['ball']:
            model_proxy = self.proxies['model']
            model_node = model_proxy.field(entity)
            movement = entity[Movement]

            # The ball's size is assumed to be 0.1, and if it moved over
            # the upper or lower boundary (1 / -1), we reflect it.
            z = model_node.get_z()
            if z > 0.9:
                model_node.set_z(0.9 - (z - 0.9))
                movement.direction.z = -movement.direction.z
            if z < -0.9:
                model_node.set_z(-0.9 - (z + 0.9))
                movement.direction.z = -movement.direction.z
示例#4
0
class ErectCharacter(System):
    entity_filters = {
        'character': [
            Proxy('model_node'),
            CharacterController,
        ],
    }
    proxies = {'model_node': ProxyType(Model, 'node')}

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            character = entity[CharacterController]
            model_node = self.proxies['model_node'].field(entity)
            up = character.gravity * -1

            roll = math.atan(character.gravity.x /
                             character.gravity.z) / (2.0 * math.pi) * 360.0
            model_node.set_r(model_node, roll)

            # FIXME: We now shoud recalculate gravity by also rolling the vector.

            pitch = math.atan(character.gravity.y /
                              character.gravity.z) / (2.0 * math.pi) * 360.0
            model_node.set_p(model_node, -pitch)

            character.gravity = Vec3(0, 0, -character.gravity.length())
示例#5
0
class GivePaddlesMoveCommands(System):
    entity_filters = {
        'paddle': and_filter([
            Proxy('model'),
            Movement,
            Paddle,
        ]),
    }

    def update(self, entities_by_filter):
        for entity in entities_by_filter['paddle']:
            paddle = entity[Paddle]
            movement = entity[Movement]

            # What keys does the player use?
            if paddle.player == Players.LEFT:
                up_key = KeyboardButton.ascii_key(b'w')
                down_key = KeyboardButton.ascii_key(b's')
            elif paddle.player == Players.RIGHT:
                up_key = KeyboardButton.up()
                down_key = KeyboardButton.down()

            # Read player input
            delta = 0
            if base.mouseWatcherNode.is_button_down(up_key):
                delta += 1
            if base.mouseWatcherNode.is_button_down(down_key):
                delta -= 1

            # Store movement
            movement.direction.z = delta
示例#6
0
class MoveObject(System):
    """
    :class:MoveObject update the position of the Entity's :class:Model according to it's
    movement direction.
    """
    entity_filters = {
        'movable': and_filter([
            Proxy('model'),
            Movement,
        ]),
    }

    def update(self, entities_by_filter):
        """
        On update, iterate all 'movable' entities. For each:
            - Get its position
            - Get its movement(direction)
            - Get its model
            - finally, update its model according to position and direction

        Note the position is update by the direction multiplied by dt, which is the deltaTime
        since the previous update, as the update function is called several times per second.

        :param entities_by_filter:
        """
        for entity in entities_by_filter['movable']:
            movement = entity[Movement]
            model_proxy = self.proxies['model']
            model = entity[model_proxy.component_type]

            movement = movement.direction * globalClock.dt
            model.node.set_pos(model.node, movement)
示例#7
0
class Bumping(CollisionSystem):
    '''
        Stop the character from moving through solid geometry.

        Components used :func:`wecs.core.and_filter` 'character'
            | :class:`wecs.panda3d.model.Scene`
            | :class:`wecs.panda3d.character.CharacterController`
            | :class:`wecs.panda3d.character.BumpingMovement`
            | :class:`wecs.panda3d.model.Clock`
            | :class:`wecs.panda3d.model.Model`
    '''
    entity_filters = {
        'character':
        and_filter([
            Proxy('scene_node'),
            Proxy('character_node'),
            Clock,
            CharacterController,
            BumpingMovement,
        ]),
    }
    proxies = {
        'character_node': ProxyType(Model, 'node'),
        'scene_node': ProxyType(Model, 'parent'),
    }

    def enter_filter_character(self, entity):
        movement = entity[BumpingMovement]
        self.init_sensors(entity, movement)
        bumper = movement.solids['bumper']
        node = bumper['node']
        movement.queue.add_collider(node, node)

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            scene_proxy = self.proxies['scene_node']
            scene = entity[scene_proxy.component_type]
            scene_node = scene_proxy.field(entity)
            character = entity[CharacterController]
            movement = entity[BumpingMovement]
            bumper = movement.solids['bumper']
            node = bumper['node']
            node.set_pos(character.translation)
            movement.traverser.traverse(scene_node)
            character.translation = node.get_pos()
示例#8
0
class AnimateCharacter(System):
    entity_filters = {
        'animated_character':
        and_filter([Proxy('actor'), Animation, CharacterController])
    }
    proxies = {'actor': ProxyType(Actor, 'node')}

    def update(self, entities_by_filter):
        for entity in entities_by_filter['animated_character']:
            controller = entity[CharacterController]
            animation = entity[Animation]
            actor_proxy = self.proxies['actor']
            actor = actor_proxy.field(entity)

            if FallingMovement in entity:
                grounded = entity[FallingMovement].ground_contact
            else:
                grounded = False

            initial = "idle"
            if not grounded:
                if controller.translation.z > 0.1:
                    initial = "jump"
                elif controller.translation.z < -0.1:
                    initial = "fall"
            elif controller.crouches:
                initial = "crouch"
            animation.to_play = [initial, "walk_forward", "run_forward"]
            # TODO: bad constant, 1.4? Should be fixed in animation
            # when the right value is found in lab.
            forward_speed = abs(controller.translation.y * 1.4)
            idle = max(0, (1 - forward_speed))
            walk = 1 - abs(forward_speed - 0.5) * 2
            run = max(0, forward_speed * 2 - 1)
            blends = [idle, walk, run]
            # sideways movement
            # TODO: same here, another constant. Fix in animation after lab.
            strafe_speed = (controller.translation.x * 1.4)
            if not strafe_speed == 0:
                blends.append(abs(strafe_speed))
                if strafe_speed > 0:
                    animation.to_play.append("walk_right")
                elif strafe_speed < 0:
                    animation.to_play.append("walk_left")

            animation.framerate = (0.5 + (forward_speed + abs(strafe_speed)))
            # If walking backwards simply play the animation in reverse
            # TODO: Only do this when there's no animations for walking backwards.
            if controller.translation.y < 0:
                animation.framerate = -animation.framerate
            if controller.translation.z < -0.2:
                animation.framerate *= 0.2

            animation.blends = blends
示例#9
0
class TurningBackToCamera(System):
    '''
        Turns character away from the camera.

        Components used :func:`wecs.core.and_filter` 'character'
            | :class:`wecs.panda3d.character.TurningBackToCameraMovement`
            | :class:`wecs.panda3d.character.CharacterController`
            | :class:`wecs.panda3d.model.Model`
            | :class:`wecs.panda3d.camera.ThirdPersonCamera`
            | :class:`wecs.panda3d.camera.TurntableCamera`
            | :class:`wecs.panda3d.model.Clock`
    '''
    entity_filters = {
        'character': and_filter([
            Proxy('character_node'),
            Clock,
            AutomaticTurningMovement,
            TurningBackToCameraMovement,
            CharacterController,
            or_filter([
                WalkingMovement,
                FloatingMovement,
            ]),
            Camera,
            ObjectCentricCameraMode,
        ])
    }
    proxies = {'character_node': ProxyType(Model, 'node')}

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            character = entity[CharacterController]
            camera = entity[Camera]
            center = entity[ObjectCentricCameraMode]
            turning = entity[TurningBackToCameraMovement]
            autoturning = entity[AutomaticTurningMovement]
            model_node = self.proxies['character_node'].field(entity)

            dt = entity[Clock].game_time

            autoturning.direction = model_node.get_relative_vector(
                camera.pivot,
                Vec3(0, 1, 0),
            )
            if character.move.xy.length() >= turning.threshold * dt:
                autoturning.alignment = turning.view_axis_alignment
            else:
                autoturning.alignment = 0.0
示例#10
0
class Animate(System):
    entity_filters = {
        'animation': and_filter([
            Proxy('actor'),
            Animation,
        ])
    }
    proxies = {'actor': ProxyType(Actor, 'node')}

    def update(self, entities_by_filter):
        for entity in entities_by_filter['animation']:
            animation = entity[Animation]
            actor_proxy = self.proxies['actor']
            actor = actor_proxy.field(entity)

            if not animation.playing == animation.to_play:
                if len(animation.to_play) > 0:
                    actor.enableBlend()
                else:
                    actor.disableBlend()

                # TODO: Don't stop and swap different animations instantly
                # but ease in (and bounce?) between them.

                # Stop animations not in to_play.
                for name in animation.playing:
                    if name not in animation.to_play:
                        actor.stop(name)
                        actor.setControlEffect(name, 0)

                # Play newly added animations.
                for n, name in enumerate(animation.to_play):
                    if name not in animation.playing:
                        actor.loop(name)
                animation.playing = animation.to_play

            # Set blends each frame
            for b, blend in enumerate(animation.blends):
                if b < len(animation.playing):
                    name = animation.playing[b]
                    actor.setControlEffect(name,
                                           blend / len(animation.playing))

            # Set framerate each frame
            for name in animation.playing:
                actor.setPlayRate(animation.framerate, name)
示例#11
0
    class BareTypeProxy(System):
        entity_filters = {
            'test': Proxy('proxy'),
        }
        proxies = {
            'proxy': ProxyType(TestComponent, 'foo'),
        }

        def update(self, entity_by_filters):
            for entity in entity_by_filters['test']:
                proxy = self.proxies['proxy']
                # test = entity[proxy.component_type]

                token = proxy.field(entity)

                global token_out
                token_out = token
示例#12
0
class ReorientInputBasedOnCamera(System):
    """
    By default, player input is relative to the character. If it is
    viewed from a third person perspective, it is usually preferable to
    rotate them so that they align with the camera instead.
    """
    entity_filters = {
        'reorient': and_filter([
            Camera,
            ObjectCentricCameraMode,
            CameraReorientedInput,
            Proxy('model'),
            CharacterController,
        ]),
    }
    proxies = {
        'model': ProxyType(Model, 'node'),
    }

    def enter_filter_reorient(self, entity):
        camera = entity[Camera]
        reorienter = entity[CameraReorientedInput]

        reorienter.node.reparent_to(camera.camera)

    def exit_filter_reorient(self, entity):
        reorienter = entity[CameraReorientedInput]

        reorienter.node.detach_node()

    def update(self, entities_by_filter):
        for entity in entities_by_filter['reorient']:
            model_proxy = self.proxies['model']
            model = entity[model_proxy.component_type]
            model_node = model_proxy.field(entity)
            character = entity[CharacterController]
            camera = entity[Camera]
            reorienter = entity[CameraReorientedInput]

            reorienter.node.set_p(model_node, 0)
            character.translation = model_node.get_relative_vector(
                reorienter.node,
                character.translation,
            )
示例#13
0
class FaceMovement(System):
    entity_filters = {
        'character': and_filter([
            Proxy('geometry_node'),
            Clock,
            CharacterController,
            FacingMovement,
        ]),
    }
    proxies = {'geometry_node': ProxyType(Geometry, 'node')}

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            geometry_proxy = self.proxies['geometry_node']
            geometry = entity[geometry_proxy.component_type]
            geometry_node = geometry_proxy.field(entity)

            controller = entity[CharacterController]
            x, y, z = controller.last_translation_speed
            geometry_node.look_at(x, y, 0)
示例#14
0
class ExecuteMovement(System):
    '''
    Transcribe the final intended movement to the model, making it an actual movement.

        Components used :func:`wecs.core.and_filter` 'character'
            | :class:`wecs.panda3d.character.CharacterController`
            | :class:`wecs.panda3d.model.Model`
            | :class:`wecs.panda3d.model.Clock`
    '''
    entity_filters = {
        'character':
        and_filter([
            Proxy('character_node'),
            Clock,
            CharacterController,
        ]),
    }
    proxies = {'character_node': ProxyType(Model, 'node')}

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            model_proxy = self.proxies['character_node']
            model = entity[model_proxy.component_type]
            character = entity[CharacterController]
            dt = entity[Clock].game_time

            # Translation: Simple self-relative movement for now.
            model_node = model_proxy.field(entity)
            model_node.set_pos(model_node, character.translation)
            character.last_translation_speed = character.translation / dt

            # Rotation
            if character.clamp_pitch:
                # Adjust intended pitch until it won't move you over a pole.
                preclamp_pitch = model_node.get_p() + character.rotation.y
                clamped_pitch = max(min(preclamp_pitch, 89.9), -89.9)
                character.rotation.y += clamped_pitch - preclamp_pitch

            model_node.set_hpr(model_node, character.rotation)
            character.last_rotation_speed = character.rotation / dt
示例#15
0
class PaddleTouchesBoundary(System):
    entity_filters = {
        'paddles': and_filter([
            Proxy('model'),
            Paddle,
        ]),
    }

    def update(self, entities_by_filter):
        for entity in set(entities_by_filter['paddles']):
            model = entity[Model]
            position = entity[Position]
            paddle = entity[Paddle]

            z = position.value.z
            size = paddle.size

            if z + size > 1:
                position.value.z = 1 - size
            elif (z - size) < -1:
                position.value.z = -1 + size
            model.node.set_z(position.value.z)
示例#16
0
class DirectlyIndicateDirection(System):
    entity_filters = {
        'character': and_filter([
            Proxy('model'),
            AutomaticTurningMovement,
            TwinStickMovement,
            CharacterController,
            Camera,
            ObjectCentricCameraMode,
        ])
    }
    proxies = {'model': ProxyType(Model, 'node')}
    input_context = 'character_direction'

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            input = entity[Input]
            if self.input_context in input.contexts:
                context = base.device_listener.read_context(self.input_context)
                self.process_input(context, entity)

    def process_input(self, context, entity):
        model_proxy = self.proxies['model']
        model_node = model_proxy.field(entity)
        camera = entity[Camera]
        turning = entity[AutomaticTurningMovement]

        if context['direction'] is not None:
            turning.direction = model_node.get_relative_vector(
                camera.pivot,
                Vec3(
                    context['direction'].x,
                    context['direction'].y,
                    0,
                ),
            )
        else:
            turning.direction = Vec3(0, 1, 0)
示例#17
0
class AdjustGravity(System):
    entity_filters = {
        'character': [
            Proxy('model_node'),
            CharacterController,
        ],
    }
    proxies = {'model_node': ProxyType(Model, 'node')}

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            character = entity[CharacterController]
            model_node = self.proxies['model_node'].field(entity)

            gravity_node = base.render
            attractor = model_node.get_pos(gravity_node)
            attractor.y = 0.0
            attractor.normalize()
            attractor *= 9.81
            local_gravity = model_node.get_relative_vector(
                gravity_node,
                attractor,
            )
            character.gravity = local_gravity
示例#18
0
class ResizePaddles(System):
    """
    ResizePaddles ensures that the paddle's size stays updated.
    The idea is that other systems may influence the size by changing
    the paddle's Component state. ResizePaddles will make the actual 
    change to the Model.
    """
    entity_filters = {
        'paddle': and_filter([
            Proxy('model'),
            Paddle,
        ]),
    }

    def update(self, entities_by_filter):
        """
        Update the paddle size by setting the scale of the paddle's 
        Model.
        """
        for entity in entities_by_filter['paddle']:
            model_proxy = self.proxies['model']
            paddle = entity[Paddle]

            model_proxy.field(entity).set_scale(paddle.size)
示例#19
0
class ProxyingNullSystem(NullSystem):
    entity_filters = {
        "null": Proxy('null_proxy'),
    }
示例#20
0
class UpdateCharacter(System):
    '''
    Convert input to character movement.

        Components used :func:`wecs.core.and_filter` 'character'
            | :class:`wecs.panda3d.character.CharacterController`
            | :class:`wecs.panda3d.model.Clock`
            | :class:`wecs.panda3d.mode.Model`
    '''
    entity_filters = {
        'character': and_filter([
            Proxy('character_node'),  # Not used, but required for a complete character
            Clock,
            CharacterController,
        ]),
        'input': and_filter([
            CharacterController,
            Input,
        ]),
    }
    proxies = {'character_node': ProxyType(Model, 'node')}
    input_context = 'character_movement'

    def update(self, entities_by_filter):
        for entity in entities_by_filter['input']:
            input = entity[Input]
            if self.input_context in input.contexts:
                context = base.device_listener.read_context(self.input_context)
                character = entity[CharacterController]

                if context['direction'] is not None:
                    character.move.x = context['direction'].x
                    character.move.y = context['direction'].y
                else:
                    character.move = Vec2(0, 0)
                if context['rotation'] is not None:
                    character.heading = -context['rotation'].x
                    character.pitch = context['rotation'].y
                else:
                    character.heading = 0
                    character.pitch = 0

                # Special movement modes.
                # By default, you run ("sprint"), unless you press e, in
                # which case you walk. You can crouch by pressing q; this
                # overrides walking and running. Jump by pressing space.
                # This logic is implemented by the Walking system. Here,
                # only intention is signalled.
                character.jumps = False
                character.sprints = False
                character.crouches = False

                if 'jump' in context:
                    character.jumps = context['jump']
                if 'sprint' in context:
                    character.sprints = context['sprint']
                if 'crouch' in context:
                    character.crouches = context['crouch']

        for entity in entities_by_filter['character']:
            controller = entity[CharacterController]
            dt = entity[Clock].game_time

            # Rotation
            controller.rotation = Vec3(
                controller.heading * dt,
                controller.pitch * dt,
                0,
            )

            # Translation
            # Controllers gamepad etc.) fill a whole rectangle of input
            # space, but characters are limited to a circle. If you're
            # strafing diagonally, you still don't get sqrt(2) speed.
            xy_dist = sqrt(controller.move.x ** 2 + controller.move.y ** 2)
            xy_scaling = 1.0
            if xy_dist > 1:
                xy_scaling = 1.0 / xy_dist
            x = controller.move.x * xy_scaling
            y = controller.move.y * xy_scaling
            z = controller.move.z * xy_scaling
            controller.translation = Vec3(x * dt, y * dt, z * dt)
示例#21
0
class Inertiing(System):
    '''
        Accelerate character, as opposed to an instantanious velocity.

        Components used :func:`wecs.core.and_filter` 'character'
            | :class:`wecs.panda3d.character.CharacterController`
            | :class:`wecs.panda3d.character.InertialMovement`
            | :class:`wecs.panda3d.model.Model`
            | :class:`wecs.model.clock`
    '''
    entity_filters = {
        'character': and_filter([
            Proxy('character_node'),
            Clock,
            CharacterController,
            InertialMovement,
        ]),
    }
    proxies = {'character_node': ProxyType(Model, 'node')}

    def enter_filter_character(self, entity):
        movement = entity[InertialMovement]
        model_proxy = self.proxies['character_node']
        model = entity[model_proxy.component_type]
        model_node = model_proxy.field(entity)

        movement.node.reparent_to(model_node)
        movement.node.set_hpr(0, 0, 0)

    def exit_filter_character(self, entity):
        # detach InertialMovement.node
        import pdb;
        pdb.set_trace()

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            dt = entity[Clock].game_time
            model_proxy = self.proxies['character_node']
            model = entity[model_proxy.component_type]
            model_node = model_proxy.field(entity)
            character = entity[CharacterController]
            inertia = entity[InertialMovement]

            # Usually you want to apply inertia only to x and y, and
            # ignore z, so we cache it.
            old_z = character.translation.z

            # We use inertia.node to represent "last frame's" model
            # orientation, scaled for how much inertia you'd like to
            # keep model-relative. Wow, what a sentence...
            # When you run forward and turn around, where should inertia
            # carry you? Physically, towards your new backward
            # direction. The opposite end of the scale of realism is
            # that your inertia vector turns around with you, and keeps
            # carrying you towards your new forward.
            # So if inertia.rotated_inertia = 1.0, inertia.node will
            # be aligned with the model, and thus the inertia vector
            # turns with you. If inertia.rotated_inertia = 0.0,
            # inertia.node will extrapolate the model's past rotation,
            # and the inertia vector will thus be kept still relative to
            # the surroundings. And if it is between those, it will
            # interpolate accordingly.
            inertia.node.set_hpr(
                -character.last_rotation_speed * dt * (1 - inertia.rotated_inertia),
            )
            last_speed_vector = model_node.get_relative_vector(
                inertia.node,
                character.last_translation_speed,
            )

            # Now we calculate the wanted speed difference, and scale it
            # within gameplay limits.
            wanted_speed_vector = character.translation / dt
            if inertia.delta_inputs:
                delta_v = wanted_speed_vector
            else:
                delta_v = wanted_speed_vector - last_speed_vector
            max_delta_v = inertia.acceleration * dt
            if delta_v.length() > max_delta_v:
                capped_delta_v = delta_v / delta_v.length() * max_delta_v
            else:
                capped_delta_v = delta_v

            character.translation = (last_speed_vector + capped_delta_v) * dt

            if inertia.ignore_z:
                character.translation.z = old_z
示例#22
0
class Falling(CollisionSystem):
    '''
        Stop the character from falling through solid geometry.

        Components used :func:`wecs.core.and_filter` 'character'
            | :class:`wecs.panda3d.character.CharacterController`
            | :class:`wecs.panda3d.character.FallingMovement`
            | :class:`wecs.panda3d.model.Clock`
            | :class:`wecs.panda3d.model.Model`
    '''
    entity_filters = {
        'character': and_filter([
            Proxy('scene_node'),
            Proxy('character_node'),
            Clock,
            CharacterController,
            FallingMovement,
        ]),
    }
    proxies = {
        'character_node': ProxyType(Model, 'node'),
        'scene_node': ProxyType(Model, 'parent'),
    }

    def enter_filter_character(self, entity):
        self.init_sensors(entity, entity[FallingMovement])

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            # Adjust the falling inertia by gravity, and position the
            # lifter collider.
            self.predict_falling(entity)
            # Find collisions with the ground.
            self.run_sensors(entity, entity[FallingMovement])
            # Adjust the character's intended translation so that his
            # falling is stoppedby the ground.
            self.fall_and_land(entity)

    def predict_falling(self, entity):
        character = entity[CharacterController]
        model_proxy = self.proxies['character_node']
        model = entity[model_proxy.component_type]
        model_node = model_proxy.field(entity)
        scene_proxy = self.proxies['scene_node']
        scene = entity[scene_proxy.component_type]
        scene_node = scene_proxy.field(entity)

        clock = entity[Clock]
        controller = entity[CharacterController]
        falling_movement = entity[FallingMovement]

        # Adjust inertia by gravity
        frame_gravity = character.gravity * clock.game_time
        falling_movement.inertia += frame_gravity

        # Adjust lifter collider by inertia
        frame_inertia = falling_movement.inertia * clock.game_time
        lifter = falling_movement.solids['lifter']
        node = lifter['node']

        node.set_pos(controller.translation + frame_inertia)

    def fall_and_land(self, entity):
        falling_movement = entity[FallingMovement]
        clock = entity[Clock]
        controller = entity[CharacterController]

        falling_movement.ground_contact = False
        frame_falling = falling_movement.inertia * clock.game_time

        if len(falling_movement.contacts) > 0:
            lifter = falling_movement.solids['lifter']['node']
            center = falling_movement.solids['lifter']['center']
            radius = falling_movement.solids['lifter']['radius']

            height_corrections = []
            for contact in falling_movement.contacts:
                # FIXME: We're assuming a sphere here.
                # We only use the lower half of the sphere, no equator.
                if contact.get_surface_normal(lifter).get_z() > 0.0:
                    contact_point = contact.get_surface_point(lifter)
                    contact_point -= center  # In solid's space
                    xy = contact_point.xy
                    expected_z = -sqrt(radius ** 2 - xy.length() ** 2)
                    actual_z = contact_point.get_z()
                    height_corrections.append(actual_z - expected_z)
            if height_corrections:
                frame_falling += Vec3(0, 0, max(height_corrections))
                falling_movement.inertia = Vec3(0, 0, 0)
                falling_movement.ground_contact = True

        # Now we know how falling / lifting influences the movement
        controller.translation += frame_falling
示例#23
0
class BallTouchesPaddleLine(System):
    """
    BallTouchesPaddleLine takes care what happens when the ball
    reaches the line of one of the paddles:
    Either the paddle is in reach, and the ball reflects off it,
    or the other player has scored, and the game is reset to its
    starting state.
    """
    entity_filters = {
        'ball': and_filter([
            Proxy('model'),
            Movement,
            Ball,
        ]),
        'paddles': and_filter([
            Proxy('model'),
            Paddle,
        ]),
    }

    def update(self, entities_by_filter):
        """
        The Update goes over all the relevant Entities (which should be
        only the ball) and check whether it reached each paddle's line.
        A ball always has a Position component so it has so we can check
        it's x position. Same goes for the paddles.
        If x is touching the paddle's line, we check whether the ball
        hit the paddle or not.
        If it hit the paddle it would bounce back.
        If it misses the paddle, it would print "SCORE" and stop the
        balls movement.
        Note that to stop the ball's movement the update deletes the 
        Movement component from the ball's Entity. It also adds the
        Resting component to ensure that the StartBallMotion System will
        return the ball to a moving state when the game restarts.
        """
        paddles = {p[Paddle].player: p for p in entities_by_filter['paddles']}

        for entity in set(entities_by_filter['ball']):
            model_proxy = self.proxies['model']
            ball_node = model_proxy.field(entity)
            movement = entity[Movement]

            # Whose line are we behind, if any?
            ball_x = ball_node.get_x()
            if ball_x < -1:
                player = Players.LEFT
            elif ball_x > 1:
                player = Players.RIGHT
            else:
                continue

            ball_z = ball_node.get_z()

            paddle = paddles[player]
            paddle_node = model_proxy.field(paddle)
            paddle_paddle = paddle[Paddle]

            paddle_z = paddle_node.get_z()
            paddle_size = paddle_paddle.size

            if abs(paddle_z - ball_z) > paddle_size:
                # The paddle is too far away, a point is scored.
                print("SCORE!")
                del entity[Movement]
                entity[Resting] = Resting()
                ball_node.set_pos(0, 0, 0)
            else:
                # Reverse left-right direction
                movement.direction.x *= -1
                # Adjust up-down speed based on where the paddle was hit
                dist_to_center = paddle_z - ball_z
                normalized_dist = dist_to_center / paddle_size
                speed = abs(movement.direction.x)
                movement.direction.z -= normalized_dist * speed
示例#24
0
class AutomaticallyTurnTowardsDirection(System):
    '''
        Turns character away from the camera.

        Components used :func:`wecs.core.and_filter` 'character'
            | :class:`wecs.panda3d.character.TurningBackToCameraMovement`
            | :class:`wecs.panda3d.character.CharacterController`
            | :class:`wecs.panda3d.model.Model`
            | :class:`wecs.panda3d.camera.ThirdPersonCamera`
            | :class:`wecs.panda3d.camera.TurntableCamera`
            | :class:`wecs.panda3d.model.Clock`
    '''
    entity_filters = {
        'character': and_filter([
            Proxy('character_node'),
            Clock,
            AutomaticTurningMovement,
            CharacterController,
            or_filter([
                WalkingMovement,
                FloatingMovement,
            ]),
        ])
    }
    proxies = {'character_node': ProxyType(Model, 'node')}

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            character = entity[CharacterController]
            turning = entity[AutomaticTurningMovement]
            model_node = self.proxies['character_node'].field(entity)
            if WalkingMovement in entity:
                movement = entity[WalkingMovement]
            else:
                movement = entity[FloatingMovement]
            dt = entity[Clock].game_time

            if turning.direction.xy.length() > 0.0:
                # How much would he have to adjust heading to face
                # towards the given vector?
                direc = Vec2(turning.direction.xy)
                angle = Vec2(0, 1).signed_angle_deg(direc)
                
                # How far can we turn this frame? Clamp to that.
                max_angle = movement.turning_speed
                if abs(angle) > max_angle:
                    angle = copysign(max_angle, angle)
                # How much of that do we *want* to turn?
                angle *= turning.alignment

                # So let's turn, and clamp, in case we're already turning.
                old_rotation = character.rotation.x
                character.rotation.x += angle
                character.rotation.x = min(
                    character.rotation.x,
                    max_angle,
                )
                character.rotation.x = max(
                    character.rotation.x,
                    -max_angle,
                )
                # Since the camera rotates with the character, we need
                # to counteract that as well.
                delta_rotation = character.rotation.x - old_rotation
                turning.angle = delta_rotation

                # FIXME: This needs to be its own system
                camera = entity[Camera]
                camera.pivot.set_h(camera.pivot.get_h() - delta_rotation)
示例#25
0
class TurningBackToCamera(System):
    '''
        Turns character away from the camera.

        Components used :func:`wecs.core.and_filter` 'character'
            | :class:`wecs.panda3d.character.TurningBackToCameraMovement`
            | :class:`wecs.panda3d.character.CharacterController`
            | :class:`wecs.panda3d.model.Model`
            | :class:`wecs.panda3d.camera.ThirdPersonCamera`
            | :class:`wecs.panda3d.camera.TurntableCamera`
            | :class:`wecs.panda3d.model.Clock`
    '''
    entity_filters = {
        'character':
        and_filter([
            Proxy('character_node'),
            Clock,
            TurningBackToCameraMovement,
            CharacterController,
            or_filter([
                WalkingMovement,
                FloatingMovement,
            ]),
            Camera,
            ObjectCentricCameraMode,
        ])
    }
    proxies = {'character_node': ProxyType(Model, 'node')}

    def update(self, entities_by_filter):
        for entity in entities_by_filter['character']:
            character = entity[CharacterController]
            camera = entity[Camera]
            center = entity[ObjectCentricCameraMode]
            turning = entity[TurningBackToCameraMovement]
            if WalkingMovement in entity:
                movement = entity[WalkingMovement]
            else:
                movement = entity[FloatingMovement]
            dt = entity[Clock].game_time

            if character.move.x**2 + character.move.y**2 > (turning.threshold *
                                                            dt)**2:
                # What's the angle to turn?
                target_angle = camera.pivot.get_h() % 360
                if target_angle > 180.0:
                    target_angle = target_angle - 360.0
                # How far can we turn this frame? Clamp to that.
                max_angle = movement.turning_speed * dt
                if abs(target_angle) > max_angle:
                    target_angle *= max_angle / abs(target_angle)
                # How much of that do we *want* to turn?
                target_angle *= turning.view_axis_alignment

                # So let's turn, and clamp, in case we're already turning.
                old_rotation = character.rotation.x
                character.rotation.x += target_angle
                character.rotation.x = min(character.rotation.x,
                                           movement.turning_speed * dt)
                character.rotation.x = max(character.rotation.x,
                                           -movement.turning_speed * dt)
                # Since the camera rotates with the character, we need
                # to counteract that as well.
                delta_rotation = character.rotation.x - old_rotation
                camera.pivot.set_h(camera.pivot.get_h() - delta_rotation)
示例#26
0
class PrepareCameras(System):
    """
    Add a `Camera` to an entity with the `'model`' proxy to attach a 
    camera to its node.

    Add `MountedCameraMode` or `ObjectCentricCameraMode` to specify how
    the camera should place itself. 
    """
    entity_filters = {
        'camera': and_filter([
            Camera,
            Proxy('model'),
        ]),
        'mount': and_filter([
            Camera,
            MountedCameraMode,
        ]),
        'mount_actor': and_filter([
            Camera,
            MountedCameraMode,
            Actor,
        ]),
        'center': and_filter([
            Camera,
            ObjectCentricCameraMode,
        ]),
    }
    proxies = {
        'model': ProxyType(Model, 'node'),
    }

    def enter_filter_camera(self, entity):
        model_proxy = self.proxies['model']
        model = entity[model_proxy.component_type]
        camera = entity[Camera]

        camera.camera.reparent_to(camera.pivot)
        camera.pivot.reparent_to(model_proxy.field(entity))
        camera.camera.node().get_lens().set_fov(camera.fov)

    def exit_filter_camera(self, entity):
        camera = entity[Camera]

        camera.pivot.detach_node()
        camera.camera.detach_node()

    def enter_filter_mount(self, entity):
        camera = entity[Camera]
        camera.pivot.set_pos(0, 0, 0)
        camera.pivot.set_hpr(0, 0, 0)
        camera.camera.set_pos(0, 0, 0)
        camera.camera.set_hpr(0, 0, 0)

    def enter_filter_mount_actor(self, entity):
        camera = entity[Camera]
        node = entity[Actor].node
        joint = node.expose_joint(None, 'modelRoot', 'camera')
        if joint:
            camera.pivot.set_pos((0, 0, 0))
            camera.pivot.reparent_to(joint)

    def exit_filter_mount(self, entity):
        model_proxy = self.proxies['model']
        model = entity[model_proxy.component_type]
        camera = entity[Camera]

    def update(self, entities_by_filter):
        for entity in entities_by_filter['center']:
            center = entity[ObjectCentricCameraMode]

            center.heading = 0
            center.pitch = 0
            center.zoom = 0
示例#27
0
class MouseOverOnEntity(System):
    entity_filters = {
        'mouseoverable': [Proxy('model'), MouseOverable],
        'mouseoverable_geometry': [Proxy('geometry'), MouseOverableGeometry],
        'camera': [Camera, Input, MouseOveringCamera],
    }
    proxies = {
        'model': ProxyType(Model, 'node'),
        'geometry': ProxyType(Geometry, 'node'),
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.traverser = CollisionTraverser()
        self.queue = CollisionHandlerQueue()

        self.picker_ray = CollisionRay()
        self.picker_node = CollisionNode('mouse ray')
        self.picker_node.add_solid(self.picker_ray)
        self.picker_node.set_from_collide_mask(MOUSEOVER_MASK)
        self.picker_node.set_into_collide_mask(0x0)
        self.picker_node_path = NodePath(self.picker_node)

        self.traverser.add_collider(self.picker_node_path, self.queue)

    def enter_filter_mouseoverable(self, entity):
        model_proxy = self.proxies['model']
        model_node = model_proxy.field(entity)
        mouseoverable = entity[MouseOverable]

        into_node = CollisionNode('wecs_mouseoverable')
        into_node.add_solid(mouseoverable.solid)
        into_node.set_from_collide_mask(0x0)
        into_node.set_into_collide_mask(mouseoverable.mask)
        into_node_path = model_node.attach_new_node(into_node)
        into_node_path.set_python_tag('wecs_mouseoverable', entity._uid)

    def exit_filter_mouseoverable(self, entity):
        # FIXME: Undo all the other stuff that accumulated!
        entity[MouseOverable].solid.detach_node()

    def enter_filter_mouseoverable_geometry(self, entity):
        into_node = self.proxies['geometry'].field(entity)

        old_mask = into_node.get_collide_mask()
        new_mask = old_mask | entity[MouseOverableGeometry].mask
        into_node.set_collide_mask(new_mask)
        into_node.find('**/+GeomNode').set_python_tag('wecs_mouseoverable',
                                                      entity._uid)

    def update(self, entities_by_filter):
        for entity in entities_by_filter['camera']:
            mouse_overing = entity[MouseOveringCamera]
            camera = entity[Camera]
            input = entity[Input]

            # Reset overed entity to None
            mouse_overing.entity = None
            mouse_overing.collision_entry = None

            requested = 'mouse_over' in input.contexts
            has_mouse = base.mouseWatcherNode.has_mouse()
            if requested and has_mouse:
                # Attach and align testing ray, and run collisions
                self.picker_node_path.reparent_to(camera.camera)
                mpos = base.mouseWatcherNode.get_mouse()
                self.picker_ray.set_from_lens(
                    base.camNode,
                    mpos.getX(),
                    mpos.getY(),
                )
                self.traverser.traverse(camera.camera.get_top())

                # Remember reference to mouseovered entity, if any
                if self.queue.get_num_entries() > 0:
                    self.queue.sort_entries()
                    entry = self.queue.get_entry(0)
                    picked_node = entry.get_into_node_path()
                    picked_uid = picked_node.get_python_tag(
                        'wecs_mouseoverable')
                    mouse_overing.entity = picked_uid
                    mouse_overing.collision_entry = entry