From f0963b4394ff37f1379c1ec2d79e7617eed5063e Mon Sep 17 00:00:00 2001 From: "Jean A. Eckelberg" Date: Thu, 5 Oct 2023 18:13:50 -0500 Subject: [PATCH 1/4] Fixed error with missing codec by using new codec, after doing so realized that the videowriter wasn't working when instantiated correctly due to new codec not supporting alpha values and writing the frames with flipped axes (#107) --- requirements.txt | 1 - visualizer/main.py | 20 +++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/requirements.txt b/requirements.txt index c5135ff..b9eca75 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ tqdm~=4.65.0 pygame~=2.3.0 numpy~=1.24.2 -Pillow~=9.5.0 opencv-python~=4.8.0.76 Sphinx~=7.0.1 Myst-Parser~=2.0.0 diff --git a/visualizer/main.py b/visualizer/main.py index b9e24db..60089df 100644 --- a/visualizer/main.py +++ b/visualizer/main.py @@ -5,7 +5,6 @@ import numpy import pygame import cv2 -from PIL import Image import game.config from typing import Callable @@ -42,10 +41,9 @@ def __init__(self): self.paused: bool = False self.recording: bool = False - size: tuple[int, int] = (self.config.SCREEN_SIZE.x, self.config.SCREEN_SIZE.y) # Scale for video saving (division can be adjusted, higher division = lower quality) - self.scaled: tuple[int, int] = (size[0] // 2, size[1] // 2) - self.writer: cv2.VideoWriter = cv2.VideoWriter("out.mp4", cv2.VideoWriter_fourcc(*'H264'), + self.scaled: tuple[int, int] = (self.size.x // 2, self.size.y // 2) + self.writer: cv2.VideoWriter = cv2.VideoWriter("out.mp4", cv2.VideoWriter_fourcc(*'mp4v'), self.default_frame_rate, self.scaled) def load(self) -> None: @@ -122,13 +120,13 @@ def __playback_controls(self, button_pressed: PlaybackButtons) -> None: # Method to deal with saving game to mp4 (called in render if save button pressed) def save_video(self) -> None: # Convert to PIL Image - new_image = pygame.image.tostring(self.screen.copy(), "RGBA", False) - new_image = Image.frombytes("RGBA", self.screen.get_rect().size, new_image) - # Scale image (using Bicubic, can be adjusted) - new_image.thumbnail(self.scaled, Image.BICUBIC) + new_image = pygame.surfarray.pixels3d(self.screen.copy()) + # Rotate ndarray + new_image = new_image.swapaxes(1, 0) + # shrink size for recording + new_image = cv2.resize(new_image, self.scaled) # Convert to OpenCV Image with numpy - new_image = numpy.array(new_image) - new_image = cv2.cvtColor(new_image, cv2.COLOR_RGBA2BGRA) + new_image = cv2.cvtColor(new_image, cv2.COLOR_RGBA2BGR) # Write image and go to next turn self.writer.write(new_image) @@ -302,7 +300,7 @@ def __results_loop(self, in_phase: bool) -> None: if not in_phase: break self.clock.tick(math.floor(self.default_frame_rate * self.playback_speed)) - + self.writer.release() if __name__ == '__main__': byte_visualiser: ByteVisualiser = ByteVisualiser() From ff26acdda7d49bad545d5477811f9fc26560d525 Mon Sep 17 00:00:00 2001 From: "Jean A. Eckelberg" Date: Fri, 6 Oct 2023 19:33:20 -0500 Subject: [PATCH 2/4] Please look over with great power and responsibility (#108) * Fixed issue with populating map * Can't put walls on stations --- game/common/map/game_board.py | 9 ++++----- game/common/map/tile.py | 2 +- .../tests/test_movement_controller_if_stations.py | 5 +---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/game/common/map/game_board.py b/game/common/map/game_board.py index d47d8fb..d7a1ebc 100644 --- a/game/common/map/game_board.py +++ b/game/common/map/game_board.py @@ -249,10 +249,10 @@ def __help_populate(self, vector_list: list[Vector], game_object_list: list[Game temp_tile: GameObject = self.game_map[vector.y][vector.x] - while hasattr(temp_tile.occupied_by, 'occupied_by'): + while temp_tile.occupied_by is not None and hasattr(temp_tile.occupied_by, 'occupied_by'): temp_tile = temp_tile.occupied_by - if temp_tile is None: + if temp_tile.occupied_by is not None: raise ValueError("Last item on the given tile doesn't have the 'occupied_by' attribute.") temp_tile.occupied_by = game_object @@ -263,13 +263,12 @@ def __help_populate(self, vector_list: list[Vector], game_object_list: list[Game # stack remaining game_objects on last vector temp_tile: GameObject = self.game_map[last_vec.y][last_vec.x] - while hasattr(temp_tile.occupied_by, 'occupied_by'): + while temp_tile.occupied_by is not None and hasattr(temp_tile.occupied_by, 'occupied_by'): temp_tile = temp_tile.occupied_by for game_object in remaining_objects: - if temp_tile is None: + if not hasattr(temp_tile, 'occupied_by') or temp_tile.occupied_by is not None: raise ValueError("Last item on the given tile doesn't have the 'occupied_by' attribute.") - temp_tile.occupied_by = game_object temp_tile = temp_tile.occupied_by diff --git a/game/common/map/tile.py b/game/common/map/tile.py index 3079a25..58060f8 100644 --- a/game/common/map/tile.py +++ b/game/common/map/tile.py @@ -21,7 +21,7 @@ class Tile(Occupiable): inherit from this class. """ def __init__(self, occupied_by: GameObject = None): - super().__init__() + super().__init__(occupied_by) self.object_type: ObjectType = ObjectType.TILE def from_json(self, data: dict) -> Self: diff --git a/game/test_suite/tests/test_movement_controller_if_stations.py b/game/test_suite/tests/test_movement_controller_if_stations.py index 4aba84b..5b3fb1f 100644 --- a/game/test_suite/tests/test_movement_controller_if_stations.py +++ b/game/test_suite/tests/test_movement_controller_if_stations.py @@ -27,10 +27,7 @@ def setUp(self) -> None: Vector(3, 1), Vector(3, 2)): [Station(None), Station(None), Station(None), Station(None), Station(None), Station(None), Station(None), Station(None)]} - - self.occ_station = OccupiableStation() - self.game_board = GameBoard(0, Vector(4, 4), self.locations, True) - self.wall = Wall() + self.game_board = GameBoard(0, Vector(4, 4), self.locations, False) # test movements up, down, left and right by starting with default 3,3 then know if it changes from there \/ self.avatar = Avatar(Vector(2, 2), 1) self.client = Player(None, None, [], self.avatar) From a15bb78e4d2dfe4a95e108083e0ac76ab8b73a9b Mon Sep 17 00:00:00 2001 From: Ian <114031398+KingPhilip14@users.noreply.github.com> Date: Fri, 6 Oct 2023 20:10:24 -0500 Subject: [PATCH 3/4] Folder docs complete (#109) * Documented 2 examples Documented 2 example files to explain the new architecture of the BytespriteFactory implementations. * Documented ByteSprite Documented the ByteSprite file * Finished folder docs Finished the folder documentation for bytesprite. * Removed duplicates Removed duplicate requirements from the requirements.txt * Removed sidebars Removed sidebars that are no longer used. * Removed comment Removed the comment related to sidebars --- requirements.txt | 4 - visualizer/adapter.py | 2 - visualizer/bytesprites/bytesprite.py | 118 +++++++++++++++---- visualizer/bytesprites/bytesprite_factory.py | 15 +++ visualizer/bytesprites/exampleBS.py | 20 ++++ visualizer/bytesprites/exampleTileBS.py | 27 +++++ visualizer/bytesprites/exampleWallBS.py | 21 ++++ visualizer/utils/sidebars.py | 29 ----- 8 files changed, 181 insertions(+), 55 deletions(-) delete mode 100644 visualizer/utils/sidebars.py diff --git a/requirements.txt b/requirements.txt index b9eca75..a18074a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,10 +6,6 @@ Sphinx~=7.0.1 Myst-Parser~=2.0.0 furo~=2023.7.26 parsec~=3.15 -opencv-python~=4.8.0.76 -Sphinx~=7.0.1 -Myst-Parser~=2.0.0 -furo~=2023.7.26 SQLAlchemy~=2.0.2 pydantic~=2.3.0 fastapi[all]~=0.103.1 diff --git a/visualizer/adapter.py b/visualizer/adapter.py index 9da206b..dc749fd 100644 --- a/visualizer/adapter.py +++ b/visualizer/adapter.py @@ -9,7 +9,6 @@ from game.utils.vector import Vector from visualizer.utils.text import Text from visualizer.utils.button import Button, ButtonColors -from visualizer.utils.sidebars import Sidebars from visualizer.bytesprites.bytesprite import ByteSprite from visualizer.templates.menu_templates import Basic, MenuTemplate from visualizer.templates.playback_template import PlaybackTemplate, PlaybackButtons @@ -90,7 +89,6 @@ def populate_bytesprite_factories(self) -> dict[int: Callable[[pygame.Surface], def render(self) -> None: # self.button.render() # any logic for rendering text, buttons, and other visuals - # to access sidebars do sidebars.[whichever sidebar you are doing] text = Text(self.screen, f'{self.turn_number} / {self.turn_max}', 48) text.rect.center = Vector.add_vectors(Vector(*self.screen.get_rect().midtop), Vector(0, 50)).as_tuple() text.render() diff --git a/visualizer/bytesprites/bytesprite.py b/visualizer/bytesprites/bytesprite.py index 8d5bd33..6cf75aa 100644 --- a/visualizer/bytesprites/bytesprite.py +++ b/visualizer/bytesprites/bytesprite.py @@ -9,10 +9,92 @@ class ByteSprite(pyg.sprite.Sprite): + """ + `ByteSprite Class Notes:` + + PyGame Notes + ------------ + Here are listed definitions of the PyGame objects that are used in this file: + PyGame.Rect: + "An object for storing rectangular coordinates." This is used to help position things on the screen. + + PyGame.Surface: + "An object for representing images." This is used mostly for getting the screen and individual images in + a spritesheet. + + + Class Variables + --------------- + Active Sheet: + The active_sheet is the list of images (sprites) that is currently being used. In other words, it's a strip + of sprites that will be used. + + Spritesheets: + This is a 2D array of sprites. For example, refer to the ``ExampleSpritesheet.png`` file. The entirety of the + 4x4 would be a spritesheet. One row of it would be used as an active sheet. + + Object Type: + This is an int that represents the enum value of the Object the sprite represents. For example, the + ``ExampleSpritesheet.png`` shows the Avatar. The Avatar object_type's enum value is found in the JSON logs and + is the number 4. This would change if the order of the ObjectType enum changes, so be mindful of that and + refer to the JSON logs for the exact values. + + Rect: + The rect is an object used for rectangular objects. You can offset the top left corner of the Rect by + passing parameters. + + Example: + + On the left, the Rect's offset is depicted as being at (0, 0), meaning there is no offset. On the right, + the Rect is seen further to the right, showing its offset from the left corner of the screen. + + Rect Example: + :: + ----------------------- ----------------------- + |------ | | ------ | + || | | | | | | + |______ | --------> | ______ | + | | | | + | | | | + | | | | + ----------------------- ----------------------- + + Screen: + The screen also a PyGame.Screen object, so it simply represents an image of the screen itself. + + Image: + The image is an individual sprite in a spritesheet. + + Frame Index: + The frame index is an int that is used to determine which sprite to use from the active_sheet. For example, + say the active_sheet is the first row in the ``ExampleSpritesheet.png``. If the frame_index is 1, the first + image will be used where the head is centered. If the frame_index is 3, the sprite used will be where the + head off to the right. + + Config: + This is an object reference to the ``config.py`` file. It's used to access the fixed values that are only + accessed in the configurations of the file. + + Update Function: + The update function is a method that is assigned during instantiation of the Bytesprite. That function is + used to update which active_sheet is used depending on what is implemented in Bytesprite classes. + + Examine the ``exampleBS.py`` file. In that implementation of the update method, it selects the active_sheet + based on a chain of if-statements. Then, in the ``create_bytesprite`` method, that implemented ``update`` + method is passed into the returned Bytesprite object. + + Now, in the Bytesprite object's update method, it will set the active_sheet to be based on what is returned + from the BytespriteFactory's method. + + To recap, first, a Bytesprite's update function depends on the BytespriteFactory's implementation. Then, the + BytespriteFactory's implementation will return which sprite_sheet is supposed to be used. Finally, the + bytesprite's update function will take what is returned from the BytespriteFactory's method and assign + the active_sheet to be what is returned. The two work in tandem. + """ + active_sheet: list[pyg.Surface] # The current spritesheet being used. spritesheets: list[list[pyg.Surface]] object_type: int - layer: int rect: pyg.Rect screen: pyg.Surface image: pyg.Surface @@ -23,7 +105,7 @@ class ByteSprite(pyg.sprite.Sprite): # make sure that all inherited classes constructors only take screen as a parameter def __init__(self, screen: pyg.Surface, filename: str, num_of_states: int, object_type: int, update_function: Callable[[dict, int, Vector, list[list[pyg.Surface]]], list[pyg.Surface]], - colorkey: pyg.Color | None = None, layer: int = 0, top_left: Vector = Vector(0, 0)): + colorkey: pyg.Color | None = None, top_left: Vector = Vector(0, 0)): # Add implementation here for selecting the sprite sheet to use super().__init__() self.spritesheet_parser: SpriteSheet = SpriteSheet(filename) @@ -43,7 +125,6 @@ def __init__(self, screen: pyg.Surface, filename: str, num_of_states: int, objec self.active_sheet: list[pyg.Surface] = self.spritesheets[0] self.object_type: int = object_type self.screen: pyg.Surface = screen - self.layer: int = layer @property def active_sheet(self) -> list[pyg.Surface]: @@ -56,11 +137,6 @@ def spritesheets(self) -> list[list[pyg.Surface]]: @property def object_type(self) -> int: return self.__object_type - - @property - def layer(self) -> int: - return self.__layer - @property def rect(self) -> pyg.Rect: return self.__rect @@ -99,15 +175,6 @@ def object_type(self, object_type: int) -> None: raise ValueError(f'{self.__class__.__name__}.object_type can\'t be negative.') self.__object_type = object_type - @layer.setter - def layer(self, layer: int) -> None: - if layer is None or not isinstance(layer, int): - raise ValueError(f'{self.__class__.__name__}.layer must be an int.') - - if layer < 0: - raise ValueError(f'{self.__class__.__name__}.layer can\'t be negative.') - self.__layer = layer - @rect.setter def rect(self, rect: pyg.Rect) -> None: if rect is None or not isinstance(rect, pyg.Rect): @@ -128,19 +195,30 @@ def update_function(self, update_function: Callable[[dict, int, Vector, list[lis # Inherit this method to implement sprite logic def update(self, data: dict, layer: int, pos: Vector) -> None: + """ + This method will start an animation based on the currently set active_sheet. Then, it will reassign the + active_sheet based on what the BytespriteFactory's update method will return. Lastly, the + ``set_image_and_render`` method is then called to then display the new sprites in the active_sheet. + :param data: + :param layer: + :param pos: + :return: None + """ self.__frame_index = 0 # Starts the new spritesheet at the beginning self.rect.topleft = ( pos.x * self.__config.TILE_SIZE * self.__config.SCALE + self.__config.GAME_BOARD_MARGIN_LEFT, pos.y * self.__config.TILE_SIZE * self.__config.SCALE + self.__config.GAME_BOARD_MARGIN_TOP) - self.update_function(data, layer, pos, self.spritesheets) + self.active_sheet = self.update_function(data, layer, pos, self.spritesheets) self.set_image_and_render() # Call this method at the end of the implemented logic and for each frame def set_image_and_render(self): + """ + This method will take a single image from the current active_sheet and then display it on the screen. + :return: + """ self.image = self.active_sheet[self.__frame_index] self.__frame_index = (self.__frame_index + 1) % self.__config.NUMBER_OF_FRAMES_PER_TURN self.screen.blit(self.image, self.rect) - - diff --git a/visualizer/bytesprites/bytesprite_factory.py b/visualizer/bytesprites/bytesprite_factory.py index 7ddf5c1..627d64b 100644 --- a/visualizer/bytesprites/bytesprite_factory.py +++ b/visualizer/bytesprites/bytesprite_factory.py @@ -7,8 +7,23 @@ class ByteSpriteFactory: @staticmethod def update(data: dict, layer: int, pos: Vector, spritesheets: list[list[pyg.Surface]]) -> list[pyg.Surface]: + """ + This is a method that **must** be implemented in every ByteSpriteFactory class. Look at the example files + to see how this *could* be implemented. Implementation may vary. + :param data: + :param layer: + :param pos: + :param spritesheets: + :return: list[pyg.Surface] + """ ... @staticmethod def create_bytesprite(screen: pyg.Surface) -> ByteSprite: + """ + This is a method that **must** be implemented in every ByteSpriteFactory class. Look at the example files + to see how this can be implemented. + :param screen: + :return: + """ ... diff --git a/visualizer/bytesprites/exampleBS.py b/visualizer/bytesprites/exampleBS.py index 50fe93e..d5efeae 100644 --- a/visualizer/bytesprites/exampleBS.py +++ b/visualizer/bytesprites/exampleBS.py @@ -9,8 +9,23 @@ class AvatarBytespriteFactoryExample(ByteSpriteFactory): + """ + `Avatar Bytesprite Factory Example Notes`: + + This is a factory class that will produce Bytesprite objects of the Avatar. + """ @staticmethod def update(data: dict, layer: int, pos: Vector, spritesheets: list[list[pyg.Surface]]) -> list[pyg.Surface]: + """ + This method will select which spritesheet to select from the ``ExampleSpritesheet.png`` file. For example, + the first if statement will return the second row of sprites in the image if conditions are met. + :param data: + :param layer: + :param pos: + :param spritesheets: + :return: list[pyg.Surface] + """ + # Logic for selecting active animation if data['inventory'][data['held_index']] is not None: return spritesheets[1] @@ -23,5 +38,10 @@ def update(data: dict, layer: int, pos: Vector, spritesheets: list[list[pyg.Surf @staticmethod def create_bytesprite(screen: pyg.Surface) -> ByteSprite: + """ + This file will return a new ByteSprite object that is to be displayed on the screen. + :param screen: ByteSprite + :return: + """ return ByteSprite(screen, os.path.join(os.getcwd(), 'visualizer/spritesheets/ExampleSpritesheet.png'), 4, 4, AvatarBytespriteFactoryExample.update, pyg.Color("#FBBBAD")) diff --git a/visualizer/bytesprites/exampleTileBS.py b/visualizer/bytesprites/exampleTileBS.py index d823449..5f4aef8 100644 --- a/visualizer/bytesprites/exampleTileBS.py +++ b/visualizer/bytesprites/exampleTileBS.py @@ -8,8 +8,29 @@ class TileBytespriteFactoryExample(ByteSpriteFactory): + """ + This class is used to demonstrate an example of the Tile Bytesprite. It demonstrates how any class inheriting + from ByteSpriteFactory must implement the `update()` and `create_bytesprite()` static methods. These methods may + have unique implementations based on how the sprites are meant to look and interact with other objects in the game. + """ @staticmethod def update(data: dict, layer: int, pos: Vector, spritesheets: list[list[pyg.Surface]]) -> list[pyg.Surface]: + """ + This implementation of the update method is different from the exampleWallBS.py file. In this method, the + data dictionary is used. The `data` is a dict representing a Tile object in JSON notation. + + For this unique implementation, an if statement is used to check if something is occupying the Tile object. + If true, the second spritesheet is used. If false, the first spritesheet is used. + + Examining the ExampleTileSS.png, it is apparent that the first spritesheet shows a Tile with an animation with + only the pink color. However, the second spritesheet (the one used if something occupies that tile) has a unique + animation that is blue instead. + :param data: + :param layer: + :param pos: + :param spritesheets: + :return: + """ if data['occupied_by'] is not None: return spritesheets[1] else: @@ -17,5 +38,11 @@ def update(data: dict, layer: int, pos: Vector, spritesheets: list[list[pyg.Surf @staticmethod def create_bytesprite(screen: pyg.Surface) -> ByteSprite: + """ + This method takes a screen from Pygame.Surface. That screen is then passed in as a parameter into the + returned Bytesprite object. + :param screen: + :return: + """ return ByteSprite(screen, os.path.join(os.getcwd(), 'visualizer/spritesheets/ExampleTileSS.png'), 2, 7, TileBytespriteFactoryExample.update) diff --git a/visualizer/bytesprites/exampleWallBS.py b/visualizer/bytesprites/exampleWallBS.py index 8989841..659cd1a 100644 --- a/visualizer/bytesprites/exampleWallBS.py +++ b/visualizer/bytesprites/exampleWallBS.py @@ -8,11 +8,32 @@ class WallBytespriteFactoryExample(ByteSpriteFactory): + """ + This class is used to demonstrate an example of the Wall Bytesprite. It demonstrates how any class inheriting + from ByteSpriteFactory must implement the `update()` and `create_bytesprite()` static methods. These methods may + have unique implementations based on how the sprites are meant to look and interact with other objects in the game. + """ @staticmethod def update(data: dict, layer: int, pos: Vector, spritesheets: list[list[pyg.Surface]]) -> list[pyg.Surface]: + """ + This method implementation simply returns the first spritesheet in the list of given spritesheets. Examining the + `ExampleWallSS.png` file, it is clear that there is only one spritesheet, so that is all this method needs to + do. + :param data: + :param layer: + :param pos: + :param spritesheets: + :return: + """ return spritesheets[0] @staticmethod def create_bytesprite(screen: pyg.Surface) -> ByteSprite: + """ + This method takes a screen from Pygame.Surface. That screen is then passed in as a parameter into the + returned Bytesprite object. + :param screen: + :return: a ByteSprite object + """ return ByteSprite(screen, os.path.join(os.getcwd(), 'visualizer/spritesheets/ExampleWallSS.png'), 1, 8, WallBytespriteFactoryExample.update) diff --git a/visualizer/utils/sidebars.py b/visualizer/utils/sidebars.py deleted file mode 100644 index 5053beb..0000000 --- a/visualizer/utils/sidebars.py +++ /dev/null @@ -1,29 +0,0 @@ -import pygame as pyg -from visualizer.config import Config -from game.utils.vector import Vector - - -class Sidebars: - - def __init__(self): - self.__config = Config() - self.top: pyg.Surface = pyg.Surface(self.__config.SIDEBAR_TOP_DIMENSIONS.as_tuple()) - self.top_rect: pyg.Rect = self.top.get_rect() - self.top_rect.midtop = Vector(x=self.__config.SCREEN_SIZE.x // 2, - y=self.__config.SIDEBAR_TOP_PADDING).as_tuple() # Returns (x: int, y: int) - - self.bottom: pyg.Surface = pyg.Surface(self.__config.SIDEBAR_BOTTOM_DIMENSIONS.as_tuple()) - self.bottom_rect: pyg.Rect = self.bottom.get_rect() - self.bottom_rect.midbottom = Vector(x=self.__config.SCREEN_SIZE.x // 2, - y=self.__config.SCREEN_SIZE.y - self.__config.SIDEBAR_BOTTOM_PADDING).as_tuple() # handling the correct padding since it will be on the bottom # Returns (x: int, y: int) - - self.left: pyg.Surface = pyg.Surface(self.__config.SIDEBAR_LEFT_DIMENSIONS.as_tuple()) - self.left_rect: pyg.Rect = self.left.get_rect() - self.left_rect.midleft = Vector(x=self.__config.SIDEBAR_LEFT_PADDING, - y=self.__config.SCREEN_SIZE.y // 2).as_tuple() # Returns (x: int, y: int) - - self.right: pyg.Surface = pyg.Surface(self.__config.SIDEBAR_RIGHT_DIMENSIONS.as_tuple()) - self.right_rect: pyg.Rect = self.right.get_rect() - self.right_rect.midright = Vector(x=self.__config.SCREEN_SIZE.x - self.__config.SIDEBAR_RIGHT_PADDING, - y=self.__config.SCREEN_SIZE.y // 2).as_tuple() # Returns (x: int, y: int) - From c9ed7c28e7c810832b6a176fe936e9492a4e7297 Mon Sep 17 00:00:00 2001 From: Ian <114031398+KingPhilip14@users.noreply.github.com> Date: Fri, 6 Oct 2023 20:46:16 -0500 Subject: [PATCH 4/4] Made requested changes (#110) * Documented 2 examples Documented 2 example files to explain the new architecture of the BytespriteFactory implementations. * Documented ByteSprite Documented the ByteSprite file * Finished folder docs Finished the folder documentation for bytesprite. * Removed duplicates Removed duplicate requirements from the requirements.txt * Removed sidebars Removed sidebars that are no longer used. * Removed comment Removed the comment related to sidebars * Made changes Made requests changes. --- visualizer/bytesprites/bytesprite.py | 29 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/visualizer/bytesprites/bytesprite.py b/visualizer/bytesprites/bytesprite.py index 6cf75aa..35974b8 100644 --- a/visualizer/bytesprites/bytesprite.py +++ b/visualizer/bytesprites/bytesprite.py @@ -45,8 +45,13 @@ class ByteSprite(pyg.sprite.Sprite): Example: - On the left, the Rect's offset is depicted as being at (0, 0), meaning there is no offset. On the right, - the Rect is seen further to the right, showing its offset from the left corner of the screen. + On the left, the Rect's offset is depicted as being at (0, 0), meaning there is no offset. A Rect object + has parameters that will determine the offset by passing in (x, y). The 'x' is the offset from the left + side, and the 'y' is the offset from the top. Therefore, passing in an (x, y) of (3, 2) in a Rect object + will move the object 3 units to the right, and 2 units down. + + In the visual below, the left side shows a Rect at (0, 0) (i.e., no offset). The image on the right depicts + the Rect object further to the right, showing its offset from the left corner of the screen. Rect Example: :: @@ -60,7 +65,7 @@ class ByteSprite(pyg.sprite.Sprite): ----------------------- ----------------------- Screen: - The screen also a PyGame.Screen object, so it simply represents an image of the screen itself. + The screen is also a PyGame.Screen object, so it simply represents an image of the screen itself. Image: The image is an individual sprite in a spritesheet. @@ -68,27 +73,27 @@ class ByteSprite(pyg.sprite.Sprite): Frame Index: The frame index is an int that is used to determine which sprite to use from the active_sheet. For example, say the active_sheet is the first row in the ``ExampleSpritesheet.png``. If the frame_index is 1, the first - image will be used where the head is centered. If the frame_index is 3, the sprite used will be where the - head off to the right. + image will be used where the head is centered. If the frame_index is 3, the sprite will now have the head of + the Avatar in a different position than in frame_index 1. Config: This is an object reference to the ``config.py`` file. It's used to access the fixed values that are only accessed in the configurations of the file. Update Function: - The update function is a method that is assigned during instantiation of the Bytesprite. That function is - used to update which active_sheet is used depending on what is implemented in Bytesprite classes. + The update function is a method that is assigned during instantiation of the ByteSprite. That function is + used to update what the active_sheet is depending on what is implemented in ByteSprite classes. Examine the ``exampleBS.py`` file. In that implementation of the update method, it selects the active_sheet - based on a chain of if-statements. Then, in the ``create_bytesprite`` method, that implemented ``update`` - method is passed into the returned Bytesprite object. + based on a chain of if statements. Next, in the ``create_bytesprite`` method, the implemented ``update`` + method is passed into the returned ByteSprite object. - Now, in the Bytesprite object's update method, it will set the active_sheet to be based on what is returned + Now, in the ByteSprite object's update method, it will set the active_sheet to be based on what is returned from the BytespriteFactory's method. - To recap, first, a Bytesprite's update function depends on the BytespriteFactory's implementation. Then, the + To recap, first, a ByteSprite's update function depends on the BytespriteFactory's implementation. Then, the BytespriteFactory's implementation will return which sprite_sheet is supposed to be used. Finally, the - bytesprite's update function will take what is returned from the BytespriteFactory's method and assign + ByteSprite's update function will take what is returned from the BytespriteFactory's method and assign the active_sheet to be what is returned. The two work in tandem. """