Merge pull request #25 from n0remac/cleanup

Animations and Map Refactor
This commit is contained in:
2020-04-20 05:49:10 -05:00
committed by GitHub
43 changed files with 264 additions and 179 deletions

View File

@@ -3,12 +3,16 @@ config.py
Holds all constants used for setting up the game.
May later hold functions for loading/saving configuration files.
"""
import os
from enum import Enum
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
RESOURCES = os.path.join(BASE_PATH, "resources")
IMAGES = os.path.join(RESOURCES, "images")
class Config(object):
"""
A simple class dedicated to loading, storing and organizing constants.
@@ -20,29 +24,50 @@ class Config(object):
SCREEN_TITLE = "Triple Dungeon"
TILE_WIDTH = 63
IDLE_UPDATES_PER_FRAME = 20
RUN_UPDATES_PER_FRAME = 10
RUN_UPDATES_PER_FRAME = 8
# Constants used to scale our sprites from their original size
CHARACTER_SCALING = 1
TILE_SCALING = 2
# The number of pixels across the level
LEVEL_SIZE = 10 * TILE_SCALING * TILE_WIDTH
# Movement speed of player, in pixels per frame
PLAYER_MOVEMENT_SPEED = 7
# How many pixels to keep as a minimum margin between the character and the edge of the screen.
# How many pixels to keep as a minimum margin between the characters and the edge of the screen.
LEFT_VIEWPORT_MARGIN = 250
RIGHT_VIEWPORT_MARGIN = 250
BOTTOM_VIEWPORT_MARGIN = 50
TOP_VIEWPORT_MARGIN = 100
class Sprites(object):
class Enums(Enum):
"""
A simple class used for tracking different simple
"""
# Play Direction Enums
RIGHT = 0
LEFT = 1
UP = 2
DOWN = 3
IDLE = 4
class SpritePaths(object):
"""
Simple class for holding sprite paths.
"""
__CHARACTERS = os.path.join(IMAGES, "characters")
__MONSTERS = os.path.join(IMAGES, "monsters")
# Single frame sprites
SKELETON = os.path.join(__MONSTERS, "skeleton.png")
GHOST = os.path.join(__MONSTERS, "ghost", "ghost1.png")
FROG = os.path.join(__MONSTERS, "frog", "frog1.png")
# Animated sprites
KNIGHT = os.path.join(__CHARACTERS, "knight")

View File

@@ -3,12 +3,13 @@ main.py
The main class used to load the game.
Holds the main game window, as well as manages basic functions for organizing the game.
"""
import random
import arcade
from config import Config
from map import Dungeon
from mobs import Player, Enemy
from config import Config, Sprites
from mobs import Player
class Game(arcade.Window):
@@ -25,53 +26,44 @@ class Game(arcade.Window):
self.wall_list = None
self.floor_list = None
self.enemy_list = None
self.player_list = None
# Separate variable that holds the player sprite
self.player = None
self.dungeon = None
# list to keep track of keypresses
self.prev_keypress = []
self.prev_keypress = []
# Our physics engine
self.physics_engine = None
# Used to keep track of our scrolling
self.view_bottom = 0
self.view_left = 0
arcade.set_background_color(arcade.csscolor.BLACK)
arcade.set_background_color(arcade.color.BLACK)
def setup(self):
""" Set up the game here. Call this function to restart the game. """
# Create the Sprite lists
self.player_list = arcade.SpriteList()
self.wall_list = arcade.SpriteList()
self.floor_list = arcade.SpriteList()
self.enemy_list = arcade.SpriteList()
# Set up the player, specifically placing it at these coordinates.
# Set up the player, specifically placing it at these coordinates.
self.player = Player()
self.player.scale = 1
self.player.center_x = Config.SCREEN_WIDTH / 2
self.player.center_y = Config.SCREEN_HEIGHT / 2
self.player_list = self.player
# Create the dungeon
dungeon = Dungeon()
self.floor_list = dungeon.floor_list
self.wall_list = dungeon.wall_list
self.dungeon = Dungeon(0, 3)
self.player.center_x, self.player.center_y = random.choice(self.dungeon.levelList).center()
# Create monsters
# This needs to be updated to comply with the new mobs.py code
#self.enemy_list.append(Enemy("resources/images/monsters/ghost/ghost1.png", 200, 200, 4).get_enemy())
#self.enemy_list.append(Enemy("resources/images/monsters/frog/frog1.png", 200, 1000, 4).get_enemy())
# self.enemy_list.append(Enemy("resources/images/monsters/ghost/ghost1.png", 200, 200, 4))
# self.enemy_list.append(Enemy("resources/images/monsters/frog/frog1.png", 200, 1000, 4))
# Create the 'physics engine'
self.physics_engine = arcade.PhysicsEngineSimple(self.player, self.wall_list)
self.physics_engine = arcade.PhysicsEngineSimple(self.player, self.dungeon.getWalls())
def on_draw(self):
""" Render the screen. """
@@ -80,43 +72,46 @@ class Game(arcade.Window):
arcade.start_render()
# Draw our sprites
self.floor_list.draw()
self.player_list.draw()
self.dungeon.render()
self.player.draw()
self.enemy_list.draw()
self.wall_list.draw()
x, y = self.player.center_x, self.player.center_y + 100
arcade.draw_text(f"({x}, {y})", x, y, arcade.color.WHITE, 15)
def on_key_press(self, key, modifiers):
"""Called whenever a key is pressed. """
if key == arcade.key.UP or key == arcade.key.W:
self.player_list.change_y = Config.PLAYER_MOVEMENT_SPEED
self.player.change_y = Config.PLAYER_MOVEMENT_SPEED
self.prev_keypress.append(key)
elif key == arcade.key.DOWN or key == arcade.key.S:
self.player_list.change_y = -Config.PLAYER_MOVEMENT_SPEED
self.player.change_y = -Config.PLAYER_MOVEMENT_SPEED
self.prev_keypress.append(key)
elif key == arcade.key.LEFT or key == arcade.key.A:
self.player_list.change_x = -Config.PLAYER_MOVEMENT_SPEED
self.player.change_x = -Config.PLAYER_MOVEMENT_SPEED
self.prev_keypress.append(key)
elif key == arcade.key.RIGHT or key == arcade.key.D:
self.player_list.change_x = Config.PLAYER_MOVEMENT_SPEED
self.player.change_x = Config.PLAYER_MOVEMENT_SPEED
self.prev_keypress.append(key)
elif key == 65307:
self.close()
self.close()
def on_key_release(self, key, modifiers):
"""Called when the user releases a key. """
if key == arcade.key.UP or key == arcade.key.W:
self.player_list.change_y = 0
self.player.change_y = 0
self.prev_keypress.remove(key)
elif key == arcade.key.DOWN or key == arcade.key.S:
self.player_list.change_y = 0
self.player.change_y = 0
self.prev_keypress.remove(key)
elif key == arcade.key.LEFT or key == arcade.key.A:
self.player_list.change_x = 0
self.player.change_x = 0
self.prev_keypress.remove(key)
elif key == arcade.key.RIGHT or key == arcade.key.D:
self.player_list.change_x = 0
self.player.change_x = 0
self.prev_keypress.remove(key)
if self.prev_keypress:
self.on_key_press(self.prev_keypress.pop(0), 0)
@@ -127,29 +122,29 @@ class Game(arcade.Window):
# Move the player with the physics engine
self.physics_engine.update()
self.player_list.update_animation()
self.player.update_animation()
changed = False # Track if we need to change the viewport
# Below manages all scrolling mechanics
# Scroll left
left_boundary = self.view_left + Config.LEFT_VIEWPORT_MARGIN
if self.player_list.left < left_boundary:
self.view_left -= left_boundary - self.player_list.left
if self.player.left < left_boundary:
self.view_left -= left_boundary - self.player.left
changed = True
# Scroll right
right_boundary = self.view_left + Config.SCREEN_WIDTH - Config.RIGHT_VIEWPORT_MARGIN
if self.player_list.right > right_boundary:
self.view_left += self.player_list.right - right_boundary
if self.player.right > right_boundary:
self.view_left += self.player.right - right_boundary
changed = True
# Scroll up
top_boundary = self.view_bottom + Config.SCREEN_HEIGHT - Config.TOP_VIEWPORT_MARGIN
if self.player_list.top > top_boundary:
self.view_bottom += self.player_list.top - top_boundary
if self.player.top > top_boundary:
self.view_bottom += self.player.top - top_boundary
changed = True
# Scroll down
bottom_boundary = self.view_bottom + Config.BOTTOM_VIEWPORT_MARGIN
if self.player_list.bottom < bottom_boundary:
self.view_bottom -= bottom_boundary - self.player_list.bottom
if self.player.bottom < bottom_boundary:
self.view_bottom -= bottom_boundary - self.player.bottom
changed = True
if changed:

View File

@@ -5,12 +5,16 @@ Pathfinding will also depend on objects here, and is thus integral to it's funct
"""
from __future__ import annotations
from config import Config
import json
import arcade
import json
import numpy as np
from itertools import chain
from config import Config
class Dungeon(object):
"""
@@ -24,44 +28,37 @@ class Dungeon(object):
:param level_count: The number of Active Levels that should be stored within the Dungeon.
:param size: The diameter of the dungeon. Allows for a total of size^2 slots for levels.
"""
# setup
self.level_count = level_count
self.size = size
self.floor_list = arcade.SpriteList()
self.wall_list = arcade.SpriteList()
level_size = 10 * Config.TILE_SCALING * Config.TILE_WIDTH
# get center level
# center = Level.load_file(1, 1, 'resources/levels/map1/center.json')
# side = Level.load_file(2, 1, 'resources/levels/map1/room.json')
center = Level()
center.load_file('resources/levels/map1/center.json')
center.render()
center_floor, center_wall = center.floor_list, center.wall_list
self.floor_list.extend(center_floor)
self.wall_list.extend(center_wall)
center = "resources/levels/map1/center.json"
self.levels = [
[Level.load_file(x, y, center) for y in range(size)] for x in range(size)
]
# get a side room
room = Level()
room.load_file('resources/levels/map1/room.json')
room.rotate_level(2)
room.render()
room_floor, room_wall = room.get_lists()
room_floor.move(level_size, 0)
room_wall.move(level_size, 0)
self.floor_list.extend(room_floor)
self.wall_list.extend(room_wall)
def getWalls(self) -> arcade.SpriteList:
"""
Simple one time function for getting all Wall sprites from all Levels.
Used by the physics engine during game startup.
# get a side room
room = Level()
room.load_file('resources/levels/map1/room.json')
room.render()
room_floor, room_wall = room.get_lists()
room_floor.move(-level_size, 0)
room_wall.move(-level_size, 0)
self.floor_list.extend(room_floor)
self.wall_list.extend(room_wall)
:return: A SpriteList containing all Sprites.
"""
def add_level(self, sprit_list):
for x in sprit_list:
self.levels.append(x)
walls = arcade.SpriteList()
walls.extend(
list(chain.from_iterable(
chain.from_iterable([level.wallSprites for level in column if level is not None]) for column in
self.levels
))
)
return walls
def render(self) -> None:
"""
@@ -71,7 +68,19 @@ class Dungeon(object):
for column in self.levels:
for level in column:
if level is not None:
level.render()
level.floorSprites.draw()
level.wallSprites.draw()
@property
def levelList(self) -> list:
"""
Retrieves all Level objects from Dungeon instance.
:return: A list containing all Level objects.
"""
return list(filter(
lambda level: level is not None, chain.from_iterable(self.levels)
))
class Level:
@@ -91,60 +100,70 @@ class Level:
self.x, self.y = level_x, level_y
self.sprites = []
self.level = []
self.structure = []
self.floorSprites = arcade.SpriteList()
self.wallSprites = arcade.SpriteList()
# Tuples containing the Node positions of where walls, floor and entrances are.
# All positions are generated based on the level's X and Y position, so that all points within
# the dungeon can be mapped by a proper pathfinding system.
self.floor_list = []
self.wall_list = []
# self.entrances = []
def load_file(self, path: str):
@staticmethod
def load_file(level_x: int, level_y: int, path: str) -> Level:
"""
Builds a Level from a given file path.
:param level_x: The level's X position within the Dungeon level matrix.
:param level_y: The level's Y position within the Dungeon level matrix.
:param path: Path to the Level file.
:return: The new generated Level file.
"""
self.floor_list = arcade.SpriteList()
self.wall_list = arcade.SpriteList()
level = Level(level_x, level_y)
with open(path) as file:
level = json.load(file)
data = json.load(file)
# Loads elements and structure data from level file
level.sprites = data['elements']
level.structure = data['structure']
self.sprites = level['elements']
self.level = level['structure']
def render(self) -> None:
tile_scale = Config.TILE_WIDTH * Config.TILE_SCALING
# Places all of the tiles & sprites
for x in range(0, 10):
for y in range(0, 10):
tilePath = level.sprites[level.structure[x][y]]
sprite = arcade.Sprite(tilePath, Config.TILE_SCALING)
sprite.center_x, sprite.center_y = x * tile_scale, y * tile_scale
if 'floor' in tilePath:
level.floorSprites.append(sprite)
elif 'wall' in tilePath:
level.wallSprites.append(sprite)
else:
print(f'Could not handle Tile: {tilePath}')
# Move everything into correct positions
level.floorSprites.move(*level.center())
level.wallSprites.move(*level.center())
return level
def center(self) -> tuple:
"""
Calls render on all sprites.
Returns the pixel center of the level.
:return: A tuple containing the X and Y coordinates of the level's center
"""
x = 0
y = 0
level_size = 10 * Config.TILE_SCALING * Config.TILE_WIDTH
# Create the level
# This shows using a loop to place multiple sprites horizontally and vertically
for y_pos in range(0, level_size , 63 * Config.TILE_SCALING):
for x_pos in range(0, level_size, 63 * Config.TILE_SCALING):
cur_tile = self.level[y][x]
sprite = self.sprites[cur_tile]
floor = arcade.Sprite(sprite, Config.TILE_SCALING)
floor.center_x = x_pos
floor.center_y = y_pos
if cur_tile == ' ':
self.floor_list.append(floor)
elif cur_tile == 'w':
self.wall_list.append(floor)
x += 1
x = 0
y += 1
def get_lists(self):
return self.floor_list, self.wall_list
return self.x * Config.LEVEL_SIZE, self.y * Config.LEVEL_SIZE
def rotate_level(self, times_rotated):
"""
Rotates the
:param times_rotated:
:return:
"""
m = np.array(self.level)
for i in range(0, times_rotated % 4):
m = np.rot90(m)

View File

@@ -5,19 +5,15 @@ Organizes all classes related to Mobs, Entities, Enemies, Players and Items.
import arcade
from config import Config, Sprites
from config import Config, Enums, SpritePaths
from sprites import PlayerAnimations
# Constants used to track if the player is facing left or right
RIGHT_FACING = 0
LEFT_FACING = 1
FRONT_FACING = 2
UP_FACING = 3
DOWN_FACING = 4
class Mob(arcade.Sprite):
"""
Represents a Mob. No defined behaviour, it has no intelligence.
"""
def __init__(self, max_health=100, max_armor=0, *args, **kwargs) -> None:
# Set up parent class
super().__init__()
@@ -42,70 +38,52 @@ class Player(Mob):
Represents a Player.
While this is a instance, there should only be one in the world at any given time.
"""
def __init__(self, *args, **kwargs) -> None:
super(Player, self).__init__(*args, **kwargs)
main_path = "resources/images/character/knight/"
self.animations = PlayerAnimations(SpritePaths.KNIGHT)
# Used for mapping directions to animations
self.map = {
Enums.IDLE: self.animations.idles,
Enums.UP: self.animations.up,
Enums.DOWN: self.animations.down,
Enums.RIGHT: self.animations.right,
Enums.LEFT: self.animations.left
}
# Default to face-front
self.character_face_direction = FRONT_FACING
self.refreshIndex = 0
self.prev = Enums.IDLE
self.texture = next(self.map[self.prev])
# Load textures for idle standing
for i in range(4):
texture = arcade.load_texture(f"{main_path}knight iso char_idle_{i}.png")
self.idle_textures.append(texture)
def update_animation(self, delta_time: float = 1 / 60) -> None:
"""
Updates animations for the Player.
:param delta_time: No idea.
"""
# Load textures for running horizontally
for i in range(6):
self.walking_textures.append([arcade.load_texture(f"{main_path}knight iso char_run left_{i}.png"),arcade.load_texture(f"{main_path}knight iso char_run left_{i}.png", mirrored=True)])
# Load textures for running down
for i in range(5):
self.down_textures.append(arcade.load_texture(f"{main_path}knight iso char_run down_{i}.png"))
# Increase the refresh index according
self.refreshIndex = (self.refreshIndex + 1) % Config.RUN_UPDATES_PER_FRAME
# Load textures for running up
for i in range(5):
self.up_textures.append(arcade.load_texture(f"{main_path}knight iso char_run up_{i}.png"))
def update_animation(self, delta_time: float = 1/60):
# Figure out if we need to flip face left, right, up, or down
if self.change_x > 0:
self.character_face_direction = LEFT_FACING
elif self.change_x < 0:
self.character_face_direction = RIGHT_FACING
elif self.change_x == 0 and self.change_y == 0:
self.character_face_direction = FRONT_FACING
# idle animation
# Logic to determine what direction we're in.
if self.change_x == 0 and self.change_y == 0:
self.cur_texture += 1
if self.cur_texture > 3 * Config.IDLE_UPDATES_PER_FRAME:
self.cur_texture = 0
self.texture = self.idle_textures[self.cur_texture // Config.IDLE_UPDATES_PER_FRAME]
return
cur = Enums.IDLE
elif self.change_y > 0: # Up
cur = Enums.UP
elif self.change_y < 0: # Down
cur = Enums.DOWN
elif self.change_x > 0: # Left
cur = Enums.RIGHT
elif self.change_x < 0: # Right
cur = Enums.LEFT
else: # Idle
cur = Enums.IDLE
#walk up animation
if self.change_y > 0:
self.cur_texture += 1
if self.cur_texture > 4 * Config.RUN_UPDATES_PER_FRAME:
self.cur_texture = 0
self.texture = self.up_textures[self.cur_texture // Config.RUN_UPDATES_PER_FRAME]
return
# If we're in a new direction or the refresh index has reset
if self.prev is not cur or self.refreshIndex == 0:
self.texture = next(self.map[cur])
#walk down animation
if self.change_y < 0:
self.cur_texture += 1
if self.cur_texture > 4 * Config.RUN_UPDATES_PER_FRAME:
self.cur_texture = 0
self.texture = self.down_textures[self.cur_texture // Config.RUN_UPDATES_PER_FRAME]
return
# Walking left/right animation
self.cur_texture += 1
if self.cur_texture > 5 * Config.RUN_UPDATES_PER_FRAME:
self.cur_texture = 0
self.texture = self.walking_textures[self.cur_texture // Config.RUN_UPDATES_PER_FRAME][self.character_face_direction]
self.prev = cur
def tick(self):
"""
@@ -120,12 +98,10 @@ class Enemy(Mob):
Represents an Enemy Mob.
Will take basic offensive actions against Player objects.
"""
def __init__(self, *args, **kwargs) -> None:
super(Enemy, self).__init__(*args, **kwargs)
def get_enemy(self):
return self
def tick(self) -> None:
"""
A on_update function, the Enemy Mob should scan for the player, decide how to path to it, and

View File

Before

Width:  |  Height:  |  Size: 597 B

After

Width:  |  Height:  |  Size: 597 B

View File

Before

Width:  |  Height:  |  Size: 597 B

After

Width:  |  Height:  |  Size: 597 B

View File

Before

Width:  |  Height:  |  Size: 621 B

After

Width:  |  Height:  |  Size: 621 B

View File

Before

Width:  |  Height:  |  Size: 621 B

After

Width:  |  Height:  |  Size: 621 B

View File

Before

Width:  |  Height:  |  Size: 547 B

After

Width:  |  Height:  |  Size: 547 B

View File

Before

Width:  |  Height:  |  Size: 566 B

After

Width:  |  Height:  |  Size: 566 B

View File

Before

Width:  |  Height:  |  Size: 545 B

After

Width:  |  Height:  |  Size: 545 B

View File

Before

Width:  |  Height:  |  Size: 570 B

After

Width:  |  Height:  |  Size: 570 B

View File

Before

Width:  |  Height:  |  Size: 583 B

After

Width:  |  Height:  |  Size: 583 B

View File

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 438 B

View File

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 498 B

View File

Before

Width:  |  Height:  |  Size: 508 B

After

Width:  |  Height:  |  Size: 508 B

View File

Before

Width:  |  Height:  |  Size: 444 B

After

Width:  |  Height:  |  Size: 444 B

View File

Before

Width:  |  Height:  |  Size: 476 B

After

Width:  |  Height:  |  Size: 476 B

View File

Before

Width:  |  Height:  |  Size: 488 B

After

Width:  |  Height:  |  Size: 488 B

View File

Before

Width:  |  Height:  |  Size: 441 B

After

Width:  |  Height:  |  Size: 441 B

View File

Before

Width:  |  Height:  |  Size: 499 B

After

Width:  |  Height:  |  Size: 499 B

View File

Before

Width:  |  Height:  |  Size: 509 B

After

Width:  |  Height:  |  Size: 509 B

View File

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 451 B

View File

Before

Width:  |  Height:  |  Size: 480 B

After

Width:  |  Height:  |  Size: 480 B

View File

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 498 B

View File

Before

Width:  |  Height:  |  Size: 409 B

After

Width:  |  Height:  |  Size: 409 B

View File

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 429 B

View File

Before

Width:  |  Height:  |  Size: 435 B

After

Width:  |  Height:  |  Size: 435 B

View File

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 449 B

View File

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 498 B

View File

Before

Width:  |  Height:  |  Size: 547 B

After

Width:  |  Height:  |  Size: 547 B

View File

Before

Width:  |  Height:  |  Size: 552 B

After

Width:  |  Height:  |  Size: 552 B

View File

Before

Width:  |  Height:  |  Size: 767 B

After

Width:  |  Height:  |  Size: 767 B

View File

Before

Width:  |  Height:  |  Size: 441 B

After

Width:  |  Height:  |  Size: 441 B

View File

Before

Width:  |  Height:  |  Size: 569 B

After

Width:  |  Height:  |  Size: 569 B

View File

Before

Width:  |  Height:  |  Size: 481 B

After

Width:  |  Height:  |  Size: 481 B

View File

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 438 B

View File

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 563 B

View File

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 486 B

View File

Before

Width:  |  Height:  |  Size: 383 B

After

Width:  |  Height:  |  Size: 383 B

View File

Before

Width:  |  Height:  |  Size: 396 B

After

Width:  |  Height:  |  Size: 396 B

View File

Before

Width:  |  Height:  |  Size: 633 B

After

Width:  |  Height:  |  Size: 633 B

70
triple-dungeon/sprites.py Normal file
View File

@@ -0,0 +1,70 @@
"""
sprites.py
A file dedicated to managing sprites and animations for characters.
"""
from itertools import cycle
import arcade
import os
import re
from typing import Pattern
class AnimationSet(object):
"""
A class that helps assist with grabbing new animations from a set.
"""
def __init__(self, directory: str):
"""
Initializes the AnimationSet class by loading files and
:param directory: A directory containing valid animation files in the correct format.
"""
self.directory = directory
self.animations = os.listdir(directory)
def getAnimations(self, pattern: Pattern) -> iter:
"""
Loads all animations from the AnimationSet's directory that match the pattern.
The pattern must have 1 group that specifies the animation's index.
:param pattern: A RegEx Pattern object.
:return: A infinite iterable looping through arcade.Texture objects.
"""
# Finds all matching files
matches = map(lambda file: re.match(pattern, file), self.animations)
matches = list(filter(lambda match: match is not None, matches))
# Sort in ascending order based on the connected animation index. Zero-indexing or not does not affect order.
matches.sort(key=lambda match: int(match.group(1)))
# Grab the filename and load it to the file directory
matches = list(map(lambda match: arcade.load_texture(os.path.join(self.directory, match.group(0))), matches))
return cycle(matches)
class PlayerAnimations(AnimationSet):
"""
A class dedicated to serving player animations.
Player animations must be served to the class in the correct format.
The correct format is: {action}[_{direction}]_{index}.png
action: [idle, run, slice] - The action being taken.
direction: [down, right, left, up] - The direction of the action, if applicable. Omit the underscore if not.
index: [0,) - The index of the animation. Index should be enumerated in ascending order.
"""
def __init__(self, directory: str):
"""
Initializes the PlayerAnimations class.
"""
super(PlayerAnimations, self).__init__(directory)
# Grabs all animations needed. These are infinite iters, use next(iter) to grab the next animation.
self.idles = self.getAnimations(re.compile(r'idle_(\d+).png'))
self.down = self.getAnimations(re.compile(r'run_down_(\d+).png'))
self.right = self.getAnimations(re.compile(r'run_right_(\d+).png'))
self.up = self.getAnimations(re.compile(r'run_up_(\d+).png'))
self.left = self.getAnimations(re.compile(r'run_left_(\d+).png'))