Mango-Creators / pygess

Pygame Essentials. Provides for template classes to make pygame easier to use
https://pypi.org/project/pygess-py/
10 stars 1 forks source link

title: 'Guide: How to use PyGess'

Introduction

This document will walk you through the implementation of the PyGess library.

PyGess is a physics engine designed to manage game objects, their interactions, and their updates within a game world. It provides classes and functions to handle entities, game objects, and worlds, along with utility functions to manage these components.

We will cover:

  1. The GameObjects class and its methods.
  2. The World class and its methods.
  3. The entity and MovingEntity classes.
  4. Utility functions for managing worlds and entities.
  5. A test script to demonstrate usage.

GameObjects class

The GameObjects class is responsible for storing and updating a list of entities. It acts like a sprite group but is tailored for entities.

Initialization

--- The constructor initializes the list of entities. ```` class GameObjects: ''' # Description A class which stores list of Entities. Also updates them. Basically a sprite group for entities instead. # Functions ## Init ``` def __init__(self, entities:list) -> None: self.entities = entities ``` Initializes entity list. ```` ---

Update method

--- The `update` method ensures all entities in the list are unique and calls their `update` method. ```` ## update ``` def update(self): from . import entity as ent self.entities = list(set(self.entities)) for entity in self.entities: if isinstance(entity, (ent.Entity)): entity.update() ``` Updates all entities in list. ''' def __init__(self, entities:list) -> None: self.entities = entities def update(self): from . import entity as ent self.entities = list(set(self.entities)) for entity in self.entities: if isinstance(entity, (ent.Entity)): entity.update() def append(self, *entity): for e in entity: if e != None: self.entities = list(self.entities) self.entities.append(e) ```` ---

Append method

--- The `append` method adds new entities to the list. ```` ## update ``` def update(self): from . import entity as ent self.entities = list(set(self.entities)) for entity in self.entities: if isinstance(entity, (ent.Entity)): entity.update() ``` Updates all entities in list. ''' def __init__(self, entities:list) -> None: self.entities = entities def update(self): from . import entity as ent self.entities = list(set(self.entities)) for entity in self.entities: if isinstance(entity, (ent.Entity)): entity.update() def append(self, *entity): for e in entity: if e != None: self.entities = list(self.entities) self.entities.append(e) ```` ---

World class

The World class manages the game world, including gravity, time, and game objects.

Initialization

--- The constructor initializes the world with gravity, game objects, and other necessary attributes. ``` class World: def __init__(self, grav: tuple, *game_objects) -> None: self.gravity = pyg.math.Vector2(grav) self.dt = 0 self.prev_time = time.time() self.is_active = False self.__counter = 0 self.__counter2 = 0 self.__objects = GameObjects(game_objects) self.runtime_obj = copy.deepcopy(self.__objects) self.__base_state = self.runtime_obj worlds.append(self) def update(self): if not self.is_active: self.__counter2 = 0 return if self.__counter2 == 0: self.__reset() if self.__counter == 0: self.__set_runtime() ``` ---

Update method

--- The `update` method handles the world's update logic, including resetting and setting runtime states. ``` self.runtime_obj.update() self.__counter += 1 self.__counter2 += 1 def add_gameobj(self, *gameobj): for obj in gameobj: self.__objects.append(obj) def load_new_object(self, *gameobj): for obj in gameobj: self.runtime_obj.append(obj) def update_delta_time(self): ``` ---

Add and load game objects

--- The `add_gameobj` and `load_new_object` methods add game objects to the world and runtime objects, respectively. ``` self.runtime_obj.update() self.__counter += 1 self.__counter2 += 1 def add_gameobj(self, *gameobj): for obj in gameobj: self.__objects.append(obj) def load_new_object(self, *gameobj): for obj in gameobj: self.runtime_obj.append(obj) def update_delta_time(self): ``` ---

Update delta time

--- The `update_delta_time` method updates the delta time for the world. ``` if self.is_active: self.dt = min(time.time() - self.prev_time, 0.1) self.prev_time = time.time() return self.dt = get_active_world().dt self.dt = min(self.dt, 0.1) def __set_runtime(self): ent = [] for e in self.__objects.entities: if e.rect not in data.all_rects: data.all_rects.append(e.rect) en = copy.deepcopy(e) en.id = uuid.uuid4() en.parent = e ent.append(copy.deepcopy(en)) self.runtime_obj = GameObjects(ent) self.__base_state = GameObjects(copy.deepcopy(ent)) def __reset(self): ent = [] for e in self.__base_state.entities: if e.rect not in data.all_rects: data.all_rects.append(e.rect) ent.append(copy.deepcopy(e)) self.runtime_obj = GameObjects(ent) ``` ---

Set runtime and reset methods

--- The `__set_runtime` and `__reset` methods manage the runtime state of the world. ``` if self.is_active: self.dt = min(time.time() - self.prev_time, 0.1) self.prev_time = time.time() return self.dt = get_active_world().dt self.dt = min(self.dt, 0.1) def __set_runtime(self): ent = [] for e in self.__objects.entities: if e.rect not in data.all_rects: data.all_rects.append(e.rect) en = copy.deepcopy(e) en.id = uuid.uuid4() en.parent = e ent.append(copy.deepcopy(en)) self.runtime_obj = GameObjects(ent) self.__base_state = GameObjects(copy.deepcopy(ent)) def __reset(self): ent = [] for e in self.__base_state.entities: if e.rect not in data.all_rects: data.all_rects.append(e.rect) ent.append(copy.deepcopy(e)) self.runtime_obj = GameObjects(ent) ``` ---

Deactivate method

--- The `_deactivate` method deactivates the world. ``` def _deactivate(self): self.is_active = False ``` ---

Utility functions

Get active world

--- The `get_active_world` function returns the currently active world. ``` def get_active_world() -> World: for world in worlds: if world.is_active: return world ``` ---

Debug no active worlds

--- The `_debug_no_active_worlds` function counts the number of active worlds. ``` def _debug_no_active_worlds(): counter = 0 for world in worlds: if world.is_active: counter += 1 return counter ``` ---

Set active world

--- The `set_active_world` function sets a given world as active and deactivates the current active world if any. ``` def set_active_world(world:World): if _debug_no_active_worlds() > 0: get_active_world()._deactivate() world.is_active = True ``` ---

Get all worlds

--- The `get_all_worlds` function returns all worlds. ``` def get_all_worlds(): return worlds ``` ---

Update delta time for all worlds

--- The `update_delta_time` function updates the delta time for all worlds. ``` def update_delta_time(): for w in get_all_worlds(): w.update_delta_time() ``` ---

Update all worlds

--- The `update_worlds` function updates all worlds. ``` def update_worlds(): for w in get_all_worlds(): w.update() ``` ---

Prefab instance functions

--- The `first_instance_of_prefab_in_world` and `all_instances_of_prefab_in_world` functions find instances of a prefab entity in a world. ``` def first_instance_of_prefab_in_world(world, prefab_entity) -> 'entity.Entity': for e in world.runtime_obj.entities: if e.parent.id == prefab_entity.id: return e ``` --- --- ``` def all_instances_of_prefab_in_world(world, prefab_entity) -> list: instances = [] for e in world.runtime_obj.entities: if e.parent.id == prefab_entity.id: instances.append(e) return instances ``` ---

Entity class

The entity class represents a game entity with position, dimensions, and optional color or image.

Initialization

--- The constructor initializes the entity with position, dimensions, and optional color or image. ``` class Entity(pyg.sprite.DirtySprite): def __init__(self, position: tuple, dimensions: tuple, color=None, image_path=None) -> None: pyg.sprite.DirtySprite.__init__(self) self.pos = pyg.math.Vector2(position) self.dimensions = dimensions self.color = color self.parent = None self.id = uuid.uuid4() ``` ---

Update method

--- The `update` method updates the entity's position, checks for collisions, and updates its sprite group. ``` def get_all_obj_colliding_with(self): return self._colliding_objects def update(self): self.active_world = physics.get_active_world() self.update_rect() self.check_collisions() if self in self.spr_group: self.spr_group.remove(self) self.spr_group.update() self.spr_group.add(self) else: self.spr_group.update() self.spr_group.draw(pyg.display.get_surface()) ``` ---

Collision methods

--- The `update_rect`, `check_collisions`, `is_colliding_with`, and `get_all_obj_colliding_with` methods handle collision detection and response. ``` self._colliding_objects = [] data.all_rects.append(self.rect) def update_rect(self): index = data.all_rects.index(self.rect) data.all_rects.remove(self.rect) self.rect.topleft = self.pos self.rect.size = self.dimensions data.all_rects.insert(index, self.rect) def check_collisions(self): self._colliding_objects = [r for r in data.all_rects if r != self.rect and self.rect.colliderect(r)] def is_colliding_with(self, rect): return rect in self._colliding_objects def get_all_obj_colliding_with(self): return self._colliding_objects def update(self): self.active_world = physics.get_active_world() self.update_rect() self.check_collisions() if self in self.spr_group: self.spr_group.remove(self) self.spr_group.update() self.spr_group.add(self) else: self.spr_group.update() self.spr_group.draw(pyg.display.get_surface()) ``` ---

MovingEntity class

The MovingEntity class extends entity to include velocity and gravity effects.

Initialization

--- The constructor initializes the moving entity with position, dimensions, velocity, and optional color or image. ``` class MovingEntity(Entity): def __init__(self, position: tuple, dimensions: tuple, velocity: tuple, color:tuple=None, image_path=None) -> None: super().__init__(position, dimensions, color=color, image_path=image_path) self.velocity = pyg.math.Vector2(velocity) self.orignal_vel = pyg.math.Vector2(velocity) self.is_affected_by_gravity = False def update(self): self.active_world = physics.get_active_world() ``` ---

Update method

--- The `update` method updates the entity's position, checks for collisions, and moves the entity. ``` self.update_rect() self.check_collisions() self.move() if self in self.spr_group: self.spr_group.remove(self) self.spr_group.update() self.spr_group.add(self) else: self.spr_group.update() self.spr_group.draw(pyg.display.get_surface()) def move(self): self.pos += self.velocity * self.active_world.dt if not self.is_affected_by_gravity: self.velocity.y = 0 return self.velocity += self.active_world.gravity def set_gravitified(self, bool:bool): self.is_affected_by_gravity = bool ``` ---

Move method

--- The `move` method updates the entity's position based on its velocity and gravity. ``` self.update_rect() self.check_collisions() self.move() if self in self.spr_group: self.spr_group.remove(self) self.spr_group.update() self.spr_group.add(self) else: self.spr_group.update() self.spr_group.draw(pyg.display.get_surface()) def move(self): self.pos += self.velocity * self.active_world.dt if not self.is_affected_by_gravity: self.velocity.y = 0 return self.velocity += self.active_world.gravity def set_gravitified(self, bool:bool): self.is_affected_by_gravity = bool ``` ---

Set gravity method

--- The `set_gravitified` method enables or disables gravity for the entity. ``` self.update_rect() self.check_collisions() self.move() if self in self.spr_group: self.spr_group.remove(self) self.spr_group.update() self.spr_group.add(self) else: self.spr_group.update() self.spr_group.draw(pyg.display.get_surface()) def move(self): self.pos += self.velocity * self.active_world.dt if not self.is_affected_by_gravity: self.velocity.y = 0 return self.velocity += self.active_world.gravity def set_gravitified(self, bool:bool): self.is_affected_by_gravity = bool ``` ---

Data and colors

Data module

--- The `data` module contains a list of all rectangles for collision detection. ``` all_rects = [] ``` ---

Colors module

--- The `colors` module defines some basic colors. ``` WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) ``` ---

Initialization

Init module

--- The `__init__` module imports necessary components and sets the version. ``` from . import entity from . import data from . import colors from . import physics from . import entity import time __version__ = '1.0.1' ``` ---

Test script

Here is a test script to demonstrate the usage of PyGess:

import pygess
import time

# Create entities
entity1 = pygess.entity.Entity((50, 50), (10, 10), color=pygess.colors.RED)
entity2 = pygess.entity.MovingEntity((100, 100), (10, 10), (1, 1), color=pygess.colors.BLUE)

# Create a world with gravity
world = pygess.physics.World((0, 9.8), entity1, entity2)

# Set the world as active
pygess.physics.set_active_world(world)

# Main loop
while True:
    pygess.physics.update_worlds()
    time.sleep(0.016)  # Simulate 60 FPS

This script creates a world with two entities, sets the world as active, and continuously updates the world in a loop.

Powered by [Swimm](https://app.swimm.io/)