## Pigframe
![Pigframe](docs/images/pigframe-logo-rectangle-200x99.jpg)
[![Downloads](https://static.pepy.tech/badge/pigframe)](https://pepy.tech/project/pigframe)
<b>[日本語版 README](docs/README-ja.md)</b>
<b>Pigframe</b> is a minimum ECS (Entity Component System) library for any Python-based game dev project. While I think it's quite rare to want to adopt ECS for game development in Python, I created this library because there wasn't an open-source library (at the time I started development) that provided both ECS and state management as a single package.
### Key Features:
- <b>Component-Based Architecture</b>: Pigframe adopts a component-based approach, allowing for modular and scalable game development. This architecture facilitates easy addition, modification, and management of game elements.
- <b>Intuitive Scene Management</b>: Manage game scenes seamlessly with Pigframe's intuitive scene transition and control system. This feature allows for smooth transitions and efficient scene organization.
- <b>Efficient Entity-Component System</b>: At the heart of Pigframe is an efficient entity-component system (ECS), which promotes a clean separation of concerns and enhances performance.
- <b>Pythonic Simplicity</b>: Designed with Python's philosophy of simplicity and readability, Pigframe is ideal for those learning game development or individual developers seeking an accessible yet powerful tool.
- <b>Versatile Integration</b>: Pigframe is optimized to work seamlessly with popular Python game libraries like Pyxel and Pygame, making it a perfect choice for diverse and creative game development projects.
### Getting Started:
To get started with Pigframe, simply install the `pigframe` from PyPI.
Pigframe has no dependencies.
```bash
pip install pigframe # pigframe has no dependencies.
```
### How to use:
- import module
```python
from pigframe import World, System, Event, Screen, Component
```
- create your own world class which manage entities, components, systems, events and screens. It is the start of your game scripts.
```python
# Implement World class for your own project.
# Example
class App(World):
def __init__(self):
super().__init__()
self.init() # write initial process which is unique to the game engine and the game you develop.
... # other game engine unique methods.
app = App()
```
- create and remove entity
```python
# Create entity to world.
entity = app.create_entity() # -> int: entity ID
# Remove entity from world.
app.remove_entity(entity) # deletes from entites list
```
- add/remove components to entity
- add components to entity
```python
# Add component to entity ID.
# Components are recorded as values where entity ID is the key inside dict.
# Component instance are created automatically.
app.add_component_to_entity(entity, ComponentA, **component_args) # ComponentA is not an instance of Component but type.
app.add_component_to_entity(entity, ComponentB(**component_args)) # This is wrong way of use.
# getter
app.get_component(ComponentA) # Returns the list of tuple: entity id which has ComponentA, component object. -> list((int, ComponentA object))
app.get_components(ComponentA, ComponentB) # Returns the list of tuple: entity id which has ComponentA and ComponentB, component objects. -> list((int, (ComponentA object, ComponentB object)))
```
- remove components from entity
```python
app.add_component_to_entity(ent, ComponentA, **component_argsA)
app.add_component_to_entity(ent, ComponentB, **component_argsB)
app.remove_component_from_entity(ent, ComponentA) # remove single component instance from entity
app.add_component_to_entity(ent, ComponentC, **component_argsC)
app.remove_components_from_entity(ent, ComponentB, ComponentC) # remove components instances from entity
```
- use component values inside system, event and screen
```python
# Example of using get_components() method.
class SystemA(System):
def process(self):
for ent, (pos, vel) in self.world.get_components(Position, Velocity):
"""
Update positions by velocity
"""
pos.x += vel.x
pos.y += vel.x
```
- use entity
```python
# Example of using entity object
class EventA(Event):
def __process(self):
player = self.world.get_entity_object(0) # 0 is the entity ID
"""
This method returns a dict
-----------
dict: entity object
key: component type
value: component
"""
```
- add scenes to world
```python
# Add scenes to world.
app.add_scenes(["launch", "game", "result", "settings"])
add.add_scene("game_over")
# scenes getter
app.sceneces # -> [["launch", "game", "result", "settings", "game_over"]
```
- add/remove system to/from world
```python
# Add screen to a scene of world. Be sure you have added scenes before adding systems.
# System instance are created automatically.
app.add_system_to_scenes(SystemA, "launch", priority = 0, **system_args)
# system with its lower priority than the other systems is executed in advance., by default 0.
# World calls System A then System B.
app.add_system_to_scenes(SystemA, "game", priority = 0, **system_args)
app.add_system_to_scenes(SystemB, "launch", priority = 1)
# Remove system from scene.
app.remove_system_from_scene(SystemA, ["launch", "game"])
```
- add/remove screens to/from world
```python
# Add screen to a scene of world. Be sure you have added scenes before adding screens.
# Screen instance are created automatically.
app.add_screen_to_scenes(ScreenA, "launch", priority = 0)
app.add_screen_to_scenes(ScreenB, "launch", priority = 0)
app.add_screen_to_scenes(ScreenC, "game", priority = 0, screen_args)
# Remove screen from scene.
app.remove_screen_from_scene(ScreenB, "launch")
```
- add/remove event to/from world
```python
# Add an event, event triger to a scene of world. Be sure you have added scenes before adding events.
# Event instance are created automatically.
app.add_event_to_scene(EventA, "game", callable_triger, priority = 0)
# Remove event from scene.
app.remove_event_from_scene(EventA, "game")
```
- add scene transitions settings
```python
app.add_scene_transition(scene_from = "launch", scene_to = "game", triger = callable_triger)
# triger has to be callable.
```
- execute systems, events and draw screens
```python
# Example with Pyxel (Python retro game engine)
class App(World):
...
def run(self):
pyxel.run(self.update, self.draw)
def update(self):
self.process() # World class has process method.
# process method calls these internal methods below.
# 1. process_systems()
# 1. process_events()
# 1. scene_manager.process()
def draw(self):
self.process_screens()
```
In `update()` method, of course, you can customize execution order as well.
```python
def update(self):
self.process_user_actions()
self.process_systems()
self.proces_events()
self.scene_manager.process() # Pigframe implements scene listener and World class use this class to manage scenes.
```
```python
# Pygame Example
class App(World):
...
def run(self):
while self.running:
self.update()
self.draw()
def update(self):
self.process()
def draw(self):
self.process_screens()
```
when some components' parameters are entity_id and you want to load saved data which had been created by the previous game, you can put entity_id to create_entity method and use set_next_entity_id method of World class to ensure the same entity_id represents the same game object between the previous game and the current game sessions.
```python
## session1
a = world.create_entity() # -> 0
b = world.create_entity() # -> 1
c = world.create_entity() # -> 2
world.add_components_to_entity(c, Relation, friedns=[b])
## remove a
world.remove_entity(a)
```
```python
## session2
max_entity_id = 0
for entity_id, data in loaded_data:
world.create_entity(entity_id=entity_id) # ensure the same entity_id represents the same game object between sessions.
for component_name, component_data in data["components"].items():
component_class = globals()[component_name]
world.add_component_to_entity(entity_id, component_class, **component_data)
max_entity_id = max(max_entity_id, entity_id)
... # after loading
world.set_next_entity_id(max_entity_id + 1) # prevent entity_id conflict
```
If you want to know the examples of real game project, please check micro projects listed below.
#### Examples
| game engine | example | contents |
| ---- | ----| ---- |
| Pyxel | [2D shooting game](https://github.com/passive-radio/pigframe/tree/main/src/pigframe/examples/pyxel_2d_shooting) | examples of system, event, component, entity and world implementations. |
| Pygame | [control a ball](https://github.com/passive-radio/pigframe/tree/main/src/pigframe/examples/pygame_control_a_ball) | examples of system, event, component, entity and world implementations. |
| Pyxel | [control a ball](https://github.com/passive-radio/pigframe/tree/main/src/pigframe/examples/pyxel_control_a_ball) | examples of system, event, component, entity and world implementations. |
### Contributing:
Contributions to Pigframe are welcome! Whether it's bug reports, feature requests or code contributions, any inputs are valuable in making Pigframe better for everyone.
Raw data
{
"_id": null,
"home_page": "https://github.com/passive-radio/pigframe",
"name": "pigframe",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "python, game, framework",
"author": "passive-radio, Yudai Okubo",
"author_email": "srccreator@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/6b/ad/2b177866c706d38a2afe42be23f694118a68a7fe7da63598d04cf8b2e543/pigframe-0.2.1.tar.gz",
"platform": null,
"description": "## Pigframe\n![Pigframe](docs/images/pigframe-logo-rectangle-200x99.jpg)\n\n[![Downloads](https://static.pepy.tech/badge/pigframe)](https://pepy.tech/project/pigframe)\n\n<b>[\u65e5\u672c\u8a9e\u7248 README](docs/README-ja.md)</b>\n\n<b>Pigframe</b> is a minimum ECS (Entity Component System) library for any Python-based game dev project. While I think it's quite rare to want to adopt ECS for game development in Python, I created this library because there wasn't an open-source library (at the time I started development) that provided both ECS and state management as a single package.\n\n### Key Features:\n- <b>Component-Based Architecture</b>: Pigframe adopts a component-based approach, allowing for modular and scalable game development. This architecture facilitates easy addition, modification, and management of game elements.\n\n- <b>Intuitive Scene Management</b>: Manage game scenes seamlessly with Pigframe's intuitive scene transition and control system. This feature allows for smooth transitions and efficient scene organization.\n\n- <b>Efficient Entity-Component System</b>: At the heart of Pigframe is an efficient entity-component system (ECS), which promotes a clean separation of concerns and enhances performance.\n\n- <b>Pythonic Simplicity</b>: Designed with Python's philosophy of simplicity and readability, Pigframe is ideal for those learning game development or individual developers seeking an accessible yet powerful tool.\n\n- <b>Versatile Integration</b>: Pigframe is optimized to work seamlessly with popular Python game libraries like Pyxel and Pygame, making it a perfect choice for diverse and creative game development projects.\n\n### Getting Started:\nTo get started with Pigframe, simply install the `pigframe` from PyPI.\nPigframe has no dependencies.\n\n```bash\npip install pigframe # pigframe has no dependencies.\n```\n\n### How to use:\n\n- import module\n ```python\n from pigframe import World, System, Event, Screen, Component\n ```\n\n- create your own world class which manage entities, components, systems, events and screens. It is the start of your game scripts.\n ```python\n # Implement World class for your own project.\n # Example \n class App(World):\n def __init__(self):\n super().__init__()\n self.init() # write initial process which is unique to the game engine and the game you develop.\n \n ... # other game engine unique methods.\n \n app = App()\n ```\n\n- create and remove entity\n ```python\n # Create entity to world.\n entity = app.create_entity() # -> int: entity ID\n # Remove entity from world.\n app.remove_entity(entity) # deletes from entites list\n ```\n\n- add/remove components to entity\n - add components to entity\n ```python\n # Add component to entity ID.\n # Components are recorded as values where entity ID is the key inside dict.\n # Component instance are created automatically.\n app.add_component_to_entity(entity, ComponentA, **component_args) # ComponentA is not an instance of Component but type.\n app.add_component_to_entity(entity, ComponentB(**component_args)) # This is wrong way of use.\n # getter\n app.get_component(ComponentA) # Returns the list of tuple: entity id which has ComponentA, component object. -> list((int, ComponentA object))\n app.get_components(ComponentA, ComponentB) # Returns the list of tuple: entity id which has ComponentA and ComponentB, component objects. -> list((int, (ComponentA object, ComponentB object)))\n ```\n\n - remove components from entity\n ```python\n app.add_component_to_entity(ent, ComponentA, **component_argsA)\n app.add_component_to_entity(ent, ComponentB, **component_argsB)\n app.remove_component_from_entity(ent, ComponentA) # remove single component instance from entity\n\n app.add_component_to_entity(ent, ComponentC, **component_argsC)\n app.remove_components_from_entity(ent, ComponentB, ComponentC) # remove components instances from entity\n ```\n\n- use component values inside system, event and screen\n ```python\n # Example of using get_components() method.\n class SystemA(System):\n def process(self):\n for ent, (pos, vel) in self.world.get_components(Position, Velocity):\n \"\"\"\n Update positions by velocity\n \"\"\"\n pos.x += vel.x\n pos.y += vel.x\n ```\n\n- use entity\n ```python\n # Example of using entity object\n class EventA(Event):\n def __process(self):\n player = self.world.get_entity_object(0) # 0 is the entity ID\n \"\"\"\n This method returns a dict\n -----------\n dict: entity object\n key: component type\n value: component\n \"\"\"\n ```\n\n- add scenes to world\n ```python\n # Add scenes to world.\n app.add_scenes([\"launch\", \"game\", \"result\", \"settings\"])\n add.add_scene(\"game_over\")\n # scenes getter\n app.sceneces # -> [[\"launch\", \"game\", \"result\", \"settings\", \"game_over\"]\n ```\n\n- add/remove system to/from world\n ```python\n # Add screen to a scene of world. Be sure you have added scenes before adding systems.\n # System instance are created automatically.\n app.add_system_to_scenes(SystemA, \"launch\", priority = 0, **system_args)\n # system with its lower priority than the other systems is executed in advance., by default 0.\n # World calls System A then System B.\n app.add_system_to_scenes(SystemA, \"game\", priority = 0, **system_args)\n app.add_system_to_scenes(SystemB, \"launch\", priority = 1)\n # Remove system from scene.\n app.remove_system_from_scene(SystemA, [\"launch\", \"game\"])\n ```\n\n- add/remove screens to/from world\n ```python\n # Add screen to a scene of world. Be sure you have added scenes before adding screens.\n # Screen instance are created automatically.\n app.add_screen_to_scenes(ScreenA, \"launch\", priority = 0)\n app.add_screen_to_scenes(ScreenB, \"launch\", priority = 0)\n app.add_screen_to_scenes(ScreenC, \"game\", priority = 0, screen_args)\n # Remove screen from scene.\n app.remove_screen_from_scene(ScreenB, \"launch\")\n ```\n\n- add/remove event to/from world\n ```python\n # Add an event, event triger to a scene of world. Be sure you have added scenes before adding events.\n # Event instance are created automatically.\n app.add_event_to_scene(EventA, \"game\", callable_triger, priority = 0)\n # Remove event from scene.\n app.remove_event_from_scene(EventA, \"game\")\n ```\n\n- add scene transitions settings\n ```python\n app.add_scene_transition(scene_from = \"launch\", scene_to = \"game\", triger = callable_triger)\n # triger has to be callable.\n ```\n\n- execute systems, events and draw screens\n ```python\n # Example with Pyxel (Python retro game engine)\n class App(World):\n ...\n\n def run(self):\n pyxel.run(self.update, self.draw)\n\n def update(self):\n self.process() # World class has process method.\n # process method calls these internal methods below.\n # 1. process_systems()\n # 1. process_events()\n # 1. scene_manager.process()\n\n def draw(self):\n self.process_screens()\n ```\n\n In `update()` method, of course, you can customize execution order as well.\n ```python\n def update(self):\n self.process_user_actions()\n self.process_systems()\n self.proces_events()\n self.scene_manager.process() # Pigframe implements scene listener and World class use this class to manage scenes.\n ```\n\n ```python\n # Pygame Example\n class App(World):\n ...\n \n def run(self):\n while self.running:\n self.update()\n self.draw()\n \n def update(self):\n self.process()\n \n def draw(self):\n self.process_screens()\n ```\n\nwhen some components' parameters are entity_id and you want to load saved data which had been created by the previous game, you can put entity_id to create_entity method and use set_next_entity_id method of World class to ensure the same entity_id represents the same game object between the previous game and the current game sessions.\n\n```python\n## session1\na = world.create_entity() # -> 0\nb = world.create_entity() # -> 1\nc = world.create_entity() # -> 2\nworld.add_components_to_entity(c, Relation, friedns=[b])\n## remove a\nworld.remove_entity(a)\n```\n\n```python\n## session2\nmax_entity_id = 0\nfor entity_id, data in loaded_data:\n world.create_entity(entity_id=entity_id) # ensure the same entity_id represents the same game object between sessions.\n for component_name, component_data in data[\"components\"].items():\n component_class = globals()[component_name]\n world.add_component_to_entity(entity_id, component_class, **component_data)\n max_entity_id = max(max_entity_id, entity_id)\n... # after loading\nworld.set_next_entity_id(max_entity_id + 1) # prevent entity_id conflict\n```\n\nIf you want to know the examples of real game project, please check micro projects listed below.\n\n#### Examples\n| game engine | example | contents |\n| ---- | ----| ---- |\n| Pyxel | [2D shooting game](https://github.com/passive-radio/pigframe/tree/main/src/pigframe/examples/pyxel_2d_shooting) | examples of system, event, component, entity and world implementations. |\n| Pygame | [control a ball](https://github.com/passive-radio/pigframe/tree/main/src/pigframe/examples/pygame_control_a_ball) | examples of system, event, component, entity and world implementations. |\n| Pyxel | [control a ball](https://github.com/passive-radio/pigframe/tree/main/src/pigframe/examples/pyxel_control_a_ball) | examples of system, event, component, entity and world implementations. |\n\n### Contributing:\nContributions to Pigframe are welcome! Whether it's bug reports, feature requests or code contributions, any inputs are valuable in making Pigframe better for everyone.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A minimum ECS (Entity Component System) library in Python, designed to simplify and streamline the development process of game application.",
"version": "0.2.1",
"project_urls": {
"Documentation": "https://github.com/passive-radio/pigframe",
"Homepage": "https://github.com/passive-radio/pigframe",
"Source": "https://github.com/passive-radio/pigframe"
},
"split_keywords": [
"python",
" game",
" framework"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "862386922319bc027ea1893926852f70e220a62aadea6d62a0cf08da0e46464a",
"md5": "032a62d2de9ee2fa761b5b1399906ee4",
"sha256": "c9b1d7c4a132ed8b6056a9f7778d70feae9608a3507f56d84e02033010d0f929"
},
"downloads": -1,
"filename": "pigframe-0.2.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "032a62d2de9ee2fa761b5b1399906ee4",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 27999,
"upload_time": "2024-11-22T19:19:57",
"upload_time_iso_8601": "2024-11-22T19:19:57.047159Z",
"url": "https://files.pythonhosted.org/packages/86/23/86922319bc027ea1893926852f70e220a62aadea6d62a0cf08da0e46464a/pigframe-0.2.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "6bad2b177866c706d38a2afe42be23f694118a68a7fe7da63598d04cf8b2e543",
"md5": "61196b37c4f54d6ff6ba8d3d76ffa159",
"sha256": "094573fc9726510eb6e6aa908ff05b8b47fd0be249a4f96d0e9c2bf4f12bbf48"
},
"downloads": -1,
"filename": "pigframe-0.2.1.tar.gz",
"has_sig": false,
"md5_digest": "61196b37c4f54d6ff6ba8d3d76ffa159",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 418201,
"upload_time": "2024-11-22T19:19:58",
"upload_time_iso_8601": "2024-11-22T19:19:58.724491Z",
"url": "https://files.pythonhosted.org/packages/6b/ad/2b177866c706d38a2afe42be23f694118a68a7fe7da63598d04cf8b2e543/pigframe-0.2.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-22 19:19:58",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "passive-radio",
"github_project": "pigframe",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "pigframe"
}