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.

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

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)

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:
        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()