Skip to content

topdownengine.mobile_object Reference

MobileObject

Bases: GameObject

Subclass of GameObject that serves as a wrapper around Controllers with some extra movement functionality.

Attributes:

Name Type Description
animation_paths dict[str, str] | None

The paths animations were loaded from.

frame_size tuple[int] | None

The frame size used to load/generate animations.

directional_anims bool

Whether or not to load and use directional animations.

current_dir str

Current direction (only set if directional_anims is True).

controller BaseController

The controller the MobileObject should use.

jump_vel float

The z-velocity that should be used while jumping.

Source code in topdownengine/mobile_object/__init__.py
class MobileObject(GameObject):
    """Subclass of GameObject that serves as a wrapper around Controllers with some 
    extra movement functionality.

    Attributes:
        animation_paths (dict[str,str]|None): The paths animations were loaded from.
        frame_size (tuple[int]|None): The frame size used to load/generate animations.
        directional_anims (bool): Whether or not to load and use directional animations.
        current_dir (str): Current direction (only set if directional_anims is True).
        controller (BaseController): The controller the MobileObject should use.
        jump_vel (float): The z-velocity that should be used while jumping.
    """
    def __init__(
        self, 
        controller: Any,
        animation_paths: dict[str,str]|None=None, 
        frame_size: tuple[int]|None=None, 
        directional_anims: bool=False
    ) -> None:
        """Initialize the MobileObject.

        Args:
            controller (BaseController): The controller the MobileObject should use.
            animation_paths (dict[str,str], optional): The animation paths to load animations from. Defaults to None.
            frame_size (tuple[int]|None, optional): The frame size to use to load/generate animations. Defaults to None.
            directional_anims (bool, optional): Whether or not to load and use directional animations. Defaults to False.
        """
        # Set animation paths dict and frame size before calling super().__init__()
        # This make it automatically load in the animations without
        # having to call it a second time.
        self.animation_paths = animation_paths
        if frame_size is not None:
            self.frame_size = frame_size
        self.directional_anims = directional_anims
        if self.directional_anims:
            self.current_dir = "d"

        super().__init__()
        self.controller = controller
        self.jump_vel = 0.75

    def update(self, dt: float, game: Game) -> None:
        """This method updates the MobileObject instance.

        Args:
            dt (float): The deltatime.
            game (Game): The Game object.
        """
        self.controller.update(self, dt)
        super().update(dt, game)
        if self.velocity.length():
            self.current_animation = "walk"
            angle = self.velocity.as_polar()[1]
            if -67.5 <= angle <= 67.5: # Right
                self.current_dir = "r"

            elif (-180 <= angle <= -112.5) or (112.5 <= angle <= 180): # Left 
                self.current_dir = "l"

            elif 67.5 <= angle <= 112.5: # Down
                self.current_dir = "d"

            elif -112.5 <= angle <= -67.5: # Up
                self.current_dir = "u"
        else:
            self.current_animation = "idle"

    def jump(self) -> None:
        """This method sets the MobileObject's z velocity to the jump velocity
        if the MobileObject is grounded (MobileObject.elevation == MobileObject.z).
        """
        if self.elevation == self.z:
            self.z_vel = self.jump_vel

current_frame property

Current animation frame the GameObject is on.

draw_index property

The draw index of the GameObject.

groups property writable

All of the groups this GameObject is in.

image property

Image for drawing.

rect property

Rect object for drawing.

world_colliders property

Return a list of collider Rects in world-space, as opposed to GameObject.colliders, which uses relative positioning to the GameObject itself.

__init__(controller, animation_paths=None, frame_size=None, directional_anims=False)

Initialize the MobileObject.

Parameters:

Name Type Description Default
controller BaseController

The controller the MobileObject should use.

required
animation_paths dict[str, str]

The animation paths to load animations from. Defaults to None.

None
frame_size tuple[int] | None

The frame size to use to load/generate animations. Defaults to None.

None
directional_anims bool

Whether or not to load and use directional animations. Defaults to False.

False
Source code in topdownengine/mobile_object/__init__.py
def __init__(
    self, 
    controller: Any,
    animation_paths: dict[str,str]|None=None, 
    frame_size: tuple[int]|None=None, 
    directional_anims: bool=False
) -> None:
    """Initialize the MobileObject.

    Args:
        controller (BaseController): The controller the MobileObject should use.
        animation_paths (dict[str,str], optional): The animation paths to load animations from. Defaults to None.
        frame_size (tuple[int]|None, optional): The frame size to use to load/generate animations. Defaults to None.
        directional_anims (bool, optional): Whether or not to load and use directional animations. Defaults to False.
    """
    # Set animation paths dict and frame size before calling super().__init__()
    # This make it automatically load in the animations without
    # having to call it a second time.
    self.animation_paths = animation_paths
    if frame_size is not None:
        self.frame_size = frame_size
    self.directional_anims = directional_anims
    if self.directional_anims:
        self.current_dir = "d"

    super().__init__()
    self.controller = controller
    self.jump_vel = 0.75

add_to(*groups)

Adds this GameObject instance to these groups.

Parameters:

Name Type Description Default
*groups GameObjectGroup

The groups to add to.

()
Source code in topdownengine/game_object.py
def add_to(self, *groups: GameObjectGroup):
    """Adds this GameObject instance to these groups.

    Args:
        *groups (GameObjectGroup): The groups to add to.
    """
    for group in groups:
        group.add(self)

generate_colliders()

Default list of Rect objects for collisions.

Source code in topdownengine/game_object.py
def generate_colliders(self) -> list[pg.Rect|pg.FRect]:
    "Default list of Rect objects for collisions."
    elev_pos = self.position - pg.Vector2(0, self.elevation)
    if self.SUBPIXEL:
        r = self.current_frame.get_frect(
            topleft=elev_pos * self.SCALE
        )
    else:
        elev_pos.x = int(elev_pos.x)
        elev_pos.y = int(elev_pos.y)
        r = self.current_frame.get_rect(
            topleft=elev_pos * self.SCALE
        )

    r.height -= r.height / 2 if self.SUBPIXEL else r.height // 2

    return [tde_math.scale_rect(r, 1/self.SCALE)]

jump()

This method sets the MobileObject's z velocity to the jump velocity if the MobileObject is grounded (MobileObject.elevation == MobileObject.z).

Source code in topdownengine/mobile_object/__init__.py
def jump(self) -> None:
    """This method sets the MobileObject's z velocity to the jump velocity
    if the MobileObject is grounded (MobileObject.elevation == MobileObject.z).
    """
    if self.elevation == self.z:
        self.z_vel = self.jump_vel

load_animations()

Load unscaled animations.

Source code in topdownengine/game_object.py
def load_animations(self) -> None:
    "Load unscaled animations."
    self.animations = dict()

    if getattr(self, "animation_paths", None) is None:
        # When there is no animation path data, add red
        # square idle animation with changing colors.
        self.animations["idle"] = []
        for i in range(4):
            image = pg.Surface(getattr(self, "frame_size", (16, 16)))
            image.fill((255/(i+1), 0, 0))
            self.animations["idle"].append(image.convert_alpha())
    else:
        for k, v in self.animation_paths.items():
            if getattr(self, "directional_anims", False):
                dirs = ["d", "r", "u", "l"]
                all_anims = VisualUtils.load_animations(v, *self.frame_size)
                all_anims.append(VisualUtils.flip_animation(all_anims[1], True, False))
                for i, anim in enumerate(all_anims):
                    self.animations[f"{k}_{dirs[i]}"] = anim

            else:
                self.animations[k] = VisualUtils.load_animation(v, *self.frame_size)

remove_from(*groups)

Removes this GameObject instance from these groups.

Parameters:

Name Type Description Default
*groups GameObjectGroup

The groups to remove from.

()
Source code in topdownengine/game_object.py
def remove_from(self, *groups: GameObjectGroup):
    """Removes this GameObject instance from these groups.

    Args:
        *groups (GameObjectGroup): The groups to remove from.
    """
    for group in groups:
        group.remove(self)

scale_animations()

Scale animations.

Source code in topdownengine/game_object.py
def scale_animations(self) -> None:
    "Scale animations."
    for _, anim in self.animations.items():
        for i, frame in enumerate(anim):
            anim[i] = pg.transform.scale(
                frame,
                (frame.width * self.SCALE, frame.height * self.SCALE)
            )

set_scale(new_scale, game) classmethod

This method sets the target scale of all GameObjects.

Parameters:

Name Type Description Default
new_scale int

The new target scale being set to.

required
game Game | None

The Game object being used. While you may pass in None, you MUST pass in a Game instance if you have already defined GameObjects.

required
Source code in topdownengine/game_object.py
@classmethod
def set_scale(cls, new_scale: int, game: Game|None) -> None:
    """This method sets the target scale of all GameObjects.

    Args:
        new_scale (int): The new target scale being set to.
        game (Game|None): The Game object being used. While you may pass in None, you MUST pass in a Game instance if you have already defined GameObjects.
    """
    if game is not None:
        new_scale = game.set_target_scale(new_scale)
    cls.SCALE = new_scale
    cls.load_and_scale_shadows()
    if game is None: 
        return
    for go in game.game_object_group.game_objects:
        go.load_animations()
        go.scale_animations()

update(dt, game)

This method updates the MobileObject instance.

Parameters:

Name Type Description Default
dt float

The deltatime.

required
game Game

The Game object.

required
Source code in topdownengine/mobile_object/__init__.py
def update(self, dt: float, game: Game) -> None:
    """This method updates the MobileObject instance.

    Args:
        dt (float): The deltatime.
        game (Game): The Game object.
    """
    self.controller.update(self, dt)
    super().update(dt, game)
    if self.velocity.length():
        self.current_animation = "walk"
        angle = self.velocity.as_polar()[1]
        if -67.5 <= angle <= 67.5: # Right
            self.current_dir = "r"

        elif (-180 <= angle <= -112.5) or (112.5 <= angle <= 180): # Left 
            self.current_dir = "l"

        elif 67.5 <= angle <= 112.5: # Down
            self.current_dir = "d"

        elif -112.5 <= angle <= -67.5: # Up
            self.current_dir = "u"
    else:
        self.current_animation = "idle"

topdownengine.mobile_object.controller

BaseController

A base class for all MobileObject controllers.

Source code in topdownengine/mobile_object/controller.py
class BaseController:
    "A base class for all MobileObject controllers."
    def update(self, mobile_object: MobileObject, dt: float) -> None:
        """Update function for MobileObject controllers.

        Args:
            mobile_object (MobileObject): The MobileObject to update.
            dt (float): The deltatime.
        """
        pass

    def move(self, mobile_object: MobileObject, dt: float, dir: pg.Vector2) -> None:
        """Change a MobileObject's velocity using a given dir Vector.

        Args:
            mobile_object (MobileObject): The MobileObject to move.
            dt (float): The deltatime.
            dir (pg.Vector2): Vector to move by.
        """
        if dir.length() != 0: 
            dir.normalize_ip()
            dir *= self.speed

        dt_seconds = dt / 1000.0
        weight = 1.0 - math.exp(-self.snapping_speed * dt_seconds)
        mobile_object.velocity = tde_math.lerp(mobile_object.velocity, dir, weight)

move(mobile_object, dt, dir)

Change a MobileObject's velocity using a given dir Vector.

Parameters:

Name Type Description Default
mobile_object MobileObject

The MobileObject to move.

required
dt float

The deltatime.

required
dir Vector2

Vector to move by.

required
Source code in topdownengine/mobile_object/controller.py
def move(self, mobile_object: MobileObject, dt: float, dir: pg.Vector2) -> None:
    """Change a MobileObject's velocity using a given dir Vector.

    Args:
        mobile_object (MobileObject): The MobileObject to move.
        dt (float): The deltatime.
        dir (pg.Vector2): Vector to move by.
    """
    if dir.length() != 0: 
        dir.normalize_ip()
        dir *= self.speed

    dt_seconds = dt / 1000.0
    weight = 1.0 - math.exp(-self.snapping_speed * dt_seconds)
    mobile_object.velocity = tde_math.lerp(mobile_object.velocity, dir, weight)

update(mobile_object, dt)

Update function for MobileObject controllers.

Parameters:

Name Type Description Default
mobile_object MobileObject

The MobileObject to update.

required
dt float

The deltatime.

required
Source code in topdownengine/mobile_object/controller.py
def update(self, mobile_object: MobileObject, dt: float) -> None:
    """Update function for MobileObject controllers.

    Args:
        mobile_object (MobileObject): The MobileObject to update.
        dt (float): The deltatime.
    """
    pass

KeyboardInputController

Bases: BaseController

A MobileObject controller that uses keyboard inputs.

Attributes:

Name Type Description
input_mgr KeyboardInputManager

The input manager.

speed float

The speed.

snapping_speed float

The snapping speed.

Source code in topdownengine/mobile_object/controller.py
class KeyboardInputController(BaseController):
    """A MobileObject controller that uses keyboard inputs.

    Attributes:
        input_mgr (KeyboardInputManager): The input manager.
        speed (float): The speed.
        snapping_speed (float): The snapping speed.
    """
    def __init__(self) -> None:
        "Initializes the input manager."
        self.input_mgr = KeyboardInputManager()
        self.speed = 2
        self.snapping_speed = 10.0

    def update(self, mobile_object: MobileObject, dt: float) -> None:
        """Moves the MobileObject based on keyboard input.

        Args:
            mobile_object (MobileObject): The MobileObject to update.
            dt (float): The deltatime.
        """
        input = self.input_mgr.get_input()

        if "Jump" in input:
            mobile_object.jump()

        dir = pg.Vector2(
            int("Move Right" in input) - int("Move Left" in input),
            int("Move Down" in input) - int("Move Up" in input)
        )
        self.move(mobile_object, dt, dir)

__init__()

Initializes the input manager.

Source code in topdownengine/mobile_object/controller.py
def __init__(self) -> None:
    "Initializes the input manager."
    self.input_mgr = KeyboardInputManager()
    self.speed = 2
    self.snapping_speed = 10.0

move(mobile_object, dt, dir)

Change a MobileObject's velocity using a given dir Vector.

Parameters:

Name Type Description Default
mobile_object MobileObject

The MobileObject to move.

required
dt float

The deltatime.

required
dir Vector2

Vector to move by.

required
Source code in topdownengine/mobile_object/controller.py
def move(self, mobile_object: MobileObject, dt: float, dir: pg.Vector2) -> None:
    """Change a MobileObject's velocity using a given dir Vector.

    Args:
        mobile_object (MobileObject): The MobileObject to move.
        dt (float): The deltatime.
        dir (pg.Vector2): Vector to move by.
    """
    if dir.length() != 0: 
        dir.normalize_ip()
        dir *= self.speed

    dt_seconds = dt / 1000.0
    weight = 1.0 - math.exp(-self.snapping_speed * dt_seconds)
    mobile_object.velocity = tde_math.lerp(mobile_object.velocity, dir, weight)

update(mobile_object, dt)

Moves the MobileObject based on keyboard input.

Parameters:

Name Type Description Default
mobile_object MobileObject

The MobileObject to update.

required
dt float

The deltatime.

required
Source code in topdownengine/mobile_object/controller.py
def update(self, mobile_object: MobileObject, dt: float) -> None:
    """Moves the MobileObject based on keyboard input.

    Args:
        mobile_object (MobileObject): The MobileObject to update.
        dt (float): The deltatime.
    """
    input = self.input_mgr.get_input()

    if "Jump" in input:
        mobile_object.jump()

    dir = pg.Vector2(
        int("Move Right" in input) - int("Move Left" in input),
        int("Move Down" in input) - int("Move Up" in input)
    )
    self.move(mobile_object, dt, dir)

MovementAIController

Bases: BaseController

A MobileObject controller that follows another MobileObject.

Attributes:

Name Type Description
target_mobile_object MobileObject

The MobileObject to move toward.

speed float

The speed.

snapping_speed float

The snapping speed.

Source code in topdownengine/mobile_object/controller.py
class MovementAIController(BaseController):
    """A MobileObject controller that follows another MobileObject.

    Attributes:
        target_mobile_object (MobileObject): The MobileObject to move toward.
        speed (float): The speed.
        snapping_speed (float): The snapping speed.
    """
    def __init__(self, target_mobile_object: MobileObject) -> None:
        """Initialize the MovementAIController.

        Args:
            target_mobile_object (MobileObject): The MobileObject to move toward.
        """
        self.target_mobile_object = target_mobile_object
        self.speed = 1.5
        self.snapping_speed = 10.0

    def update(self, mobile_object: MobileObject, dt: float) -> None:
        """Move the MobileObject towards the target.

        Args:
            mobile_object (MobileObject): The MobileObject to update.
            dt (float): The deltatime.
        """
        dir = self.target_mobile_object.position - mobile_object.position
        self.move(mobile_object, dt, dir)

__init__(target_mobile_object)

Initialize the MovementAIController.

Parameters:

Name Type Description Default
target_mobile_object MobileObject

The MobileObject to move toward.

required
Source code in topdownengine/mobile_object/controller.py
def __init__(self, target_mobile_object: MobileObject) -> None:
    """Initialize the MovementAIController.

    Args:
        target_mobile_object (MobileObject): The MobileObject to move toward.
    """
    self.target_mobile_object = target_mobile_object
    self.speed = 1.5
    self.snapping_speed = 10.0

move(mobile_object, dt, dir)

Change a MobileObject's velocity using a given dir Vector.

Parameters:

Name Type Description Default
mobile_object MobileObject

The MobileObject to move.

required
dt float

The deltatime.

required
dir Vector2

Vector to move by.

required
Source code in topdownengine/mobile_object/controller.py
def move(self, mobile_object: MobileObject, dt: float, dir: pg.Vector2) -> None:
    """Change a MobileObject's velocity using a given dir Vector.

    Args:
        mobile_object (MobileObject): The MobileObject to move.
        dt (float): The deltatime.
        dir (pg.Vector2): Vector to move by.
    """
    if dir.length() != 0: 
        dir.normalize_ip()
        dir *= self.speed

    dt_seconds = dt / 1000.0
    weight = 1.0 - math.exp(-self.snapping_speed * dt_seconds)
    mobile_object.velocity = tde_math.lerp(mobile_object.velocity, dir, weight)

update(mobile_object, dt)

Move the MobileObject towards the target.

Parameters:

Name Type Description Default
mobile_object MobileObject

The MobileObject to update.

required
dt float

The deltatime.

required
Source code in topdownengine/mobile_object/controller.py
def update(self, mobile_object: MobileObject, dt: float) -> None:
    """Move the MobileObject towards the target.

    Args:
        mobile_object (MobileObject): The MobileObject to update.
        dt (float): The deltatime.
    """
    dir = self.target_mobile_object.position - mobile_object.position
    self.move(mobile_object, dt, dir)

StaticController

Bases: BaseController

A MobileObject controller that keeps the MobileObject still.

Source code in topdownengine/mobile_object/controller.py
class StaticController(BaseController):
    "A MobileObject controller that keeps the MobileObject still."
    def update(self, mobile_object: MobileObject, dt: float) -> None:
        """Sets the MobileObject's velocity to (0, 0).

        Args:
            mobile_object (MobileObject): The MobileObject to update.
            dt (float): The deltatime.
        """
        mobile_object.velocity = pg.Vector2()

move(mobile_object, dt, dir)

Change a MobileObject's velocity using a given dir Vector.

Parameters:

Name Type Description Default
mobile_object MobileObject

The MobileObject to move.

required
dt float

The deltatime.

required
dir Vector2

Vector to move by.

required
Source code in topdownengine/mobile_object/controller.py
def move(self, mobile_object: MobileObject, dt: float, dir: pg.Vector2) -> None:
    """Change a MobileObject's velocity using a given dir Vector.

    Args:
        mobile_object (MobileObject): The MobileObject to move.
        dt (float): The deltatime.
        dir (pg.Vector2): Vector to move by.
    """
    if dir.length() != 0: 
        dir.normalize_ip()
        dir *= self.speed

    dt_seconds = dt / 1000.0
    weight = 1.0 - math.exp(-self.snapping_speed * dt_seconds)
    mobile_object.velocity = tde_math.lerp(mobile_object.velocity, dir, weight)

update(mobile_object, dt)

Sets the MobileObject's velocity to (0, 0).

Parameters:

Name Type Description Default
mobile_object MobileObject

The MobileObject to update.

required
dt float

The deltatime.

required
Source code in topdownengine/mobile_object/controller.py
def update(self, mobile_object: MobileObject, dt: float) -> None:
    """Sets the MobileObject's velocity to (0, 0).

    Args:
        mobile_object (MobileObject): The MobileObject to update.
        dt (float): The deltatime.
    """
    mobile_object.velocity = pg.Vector2()