Updated the readme.

This commit is contained in:
Storm Dragon 2025-03-15 20:33:57 -04:00
parent 27765e62bc
commit fedb09be94

266
README.md
View File

@ -2,6 +2,7 @@
A Python library to make creating audio games easier.
## Overview
`libstormgames` provides a comprehensive set of tools for developing accessible games with audio-first design. It handles common game development tasks including:
@ -14,6 +15,7 @@ A Python library to make creating audio games easier.
- Menu systems and text display
- GUI initialization
## Installation
### Requirements
@ -31,18 +33,23 @@ A Python library to make creating audio games easier.
- Linux/Unix: `python-speechd` or `accessible-output2>=0.14`
- Windows/macOS: `accessible-output2>=0.14`
### Install from source
If you are on Linux, check your package manager first to see if the packages in requirements.txt are available.
```bash
git clone https://git.stormux.org/storm/libstormgames
cd libstormgames
pip install -e .
```
## Getting Started
You can use libstormgames in two ways: the traditional function-based approach or the new class-based approach.
### Traditional Function-Based Approach
```python
@ -69,8 +76,8 @@ def main():
# Define menu options
while True:
choice = sg.game_menu(sounds, "play_game", "instructions", "credits", "donate", "exit_game")
if choice == "play_game":
choice = sg.game_menu(sounds)
if choice == "play":
play_game()
if __name__ == "__main__":
@ -87,7 +94,7 @@ def main():
# Create and initialize a game
game = sg.Game("My First Audio Game").initialize()
# Welcome message (using fluent API)
# Welcome message
game.speak("Welcome to My First Audio Game!")
# Main game loop
@ -100,14 +107,15 @@ def main():
# Define menu options
while True:
choice = sg.game_menu(game.sound.get_sounds(), "play_game", "instructions", "credits", "donate", "exit_game")
if choice == "play_game":
choice = sg.game_menu(game.sound.get_sounds())
if choice == "play":
play_game()
if __name__ == "__main__":
main()
```
## Library Structure
The library is organized into modules, each with a specific focus:
@ -122,6 +130,7 @@ The library is organized into modules, each with a specific focus:
- **menu**: Menu systems
- **utils**: Utility functions and Game class
## Core Classes
### Game
@ -133,19 +142,27 @@ The Game class provides a central way to manage all game systems:
game = sg.Game("My Game").initialize()
# Use fluent API for chaining commands
game.speak("Hello").play_bgm("music/theme.ogg")
game.speak("Hello").play_bgm("music/theme")
# Access components directly
game.scoreboard.increase_score(10)
game.sound.play_random("explosion")
# Display text
game.display_text(["Line 1", "Line 2"])
# Clean exit
game.exit()
```
The initialization process sets up proper configuration paths:
- Creates game-specific directories if they don't exist
- Ensures all services have access to correct paths
- Connects all components for seamless operation
### Services
The library includes several service classes that replace global variables:
@ -181,6 +198,14 @@ config.read_local_config()
difficulty = config.local_config.get("settings", "difficulty")
```
Configuration files are automatically stored in standard system locations:
- Linux/Unix: `~/.config/storm-games/`
- Windows: `%APPDATA%\storm-games\`
- macOS: `~/Library/Application Support/storm-games/`
Each game has its own subdirectory based on the game name (lowercase with hyphens), for example, Wicked Quest becomes wicked-quest.
### Sound
Manages sound loading and playback with positional audio support.
@ -213,6 +238,7 @@ sg.adjust_bgm_volume(-0.1) # Decrease background music volume
sg.adjust_sfx_volume(0.1) # Increase sound effects volume
```
### Speech
Provides text-to-speech functionality using available speech providers.
@ -231,9 +257,10 @@ sg.speak("Hello, world!")
sg.speak("This won't interrupt", interrupt=False)
# Clean up when done
speech.close()
exit_game() or as the class, game.exit_game()
```
### Scoreboard
Tracks scores and manages high score tables.
@ -261,6 +288,15 @@ for entry in high_scores:
print(f"{entry['name']}: {entry['score']}")
```
The Scoreboard system automatically manages high score data in the game's configuration directory:
- Linux/Unix: `~/.config/storm-games/game-name/config.ini`
- Windows: `%APPDATA%\storm-games\game-name\config.ini`
- macOS: `~/Library/Application Support/storm-games/game-name/config.ini`
Where `game-name` is the lowercase, hyphenated version of your game title. For example,
"My Awesome Game" would use the directory `my-awesome-game`.
## Key Functions
### Game Initialization and Control
@ -286,22 +322,167 @@ sg.exit_game()
### Menu System
```python
# Display a menu with options (functions should exist with these names)
choice = sg.game_menu(sounds, "play_game", "high_scores", "instructions", "credits", "exit_game")
# Basic menu example (returns option string)
choice = sg.game_menu(sounds)
if choice == "play":
# Start the game
play_game()
elif choice == "instructions":
sg.instructions()
# etc...
# Display built-in instructions
sg.instructions()
# Menu with play callback (callback is executed directly)
def play_callback():
sg.speak("Game started with callback!")
# Game code goes here
sg.game_menu(sounds, play_callback)
# Display built-in credits
sg.credits()
# Open donation page
sg.donate()
# Interactive menu to learn available sounds
sg.learn_sounds(sounds)
# Menu with custom options
choice = sg.game_menu(sounds, None, "practice_mode")
if choice == "practice_mode":
# Handle practice mode
practice_game()
elif choice == "difficulty":
# Handle difficulty settings
set_difficulty()
elif choice == "options":
# Handle options screen
show_options()
# etc...
```
### Complete Menu Example
```python
import libstormgames as sg
import pygame
def main():
# Initialize the game
sounds = sg.initialize_gui("Menu Example Game")
# Play menu music
sg.play_bgm("sounds/music_menu.ogg")
# Define play function with callback
def play_game():
sg.speak("Starting game!")
# Game code here
sg.speak("Game over!")
return "menu" # Return to menu after game ends
# Define custom menu option handlers
def practice_mode():
sg.speak("Starting practice mode!")
# Practice mode code here
return "menu"
def difficulty_settings():
# Show difficulty options
options = ["easy", "normal", "hard", "back"]
current = 0
while True:
sg.speak(options[current])
event = pygame.event.wait()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP and current > 0:
current -= 1
elif event.key == pygame.K_DOWN and current < len(options) - 1:
current += 1
elif event.key == pygame.K_RETURN:
if options[current] == "back":
return "menu"
sg.speak(f"Difficulty set to {options[current]}")
return "menu"
elif event.key == pygame.K_ESCAPE:
return "menu"
# Main menu loop
while True:
# Display menu with play callback and custom options
choice = sg.game_menu(sounds, play_game, "practice_mode", "difficulty")
# Handle menu choices
if choice == "practice_mode":
practice_mode()
elif choice == "difficulty":
difficulty_settings()
# Other options are handled automatically by game_menu
if __name__ == "__main__":
main()
```
### Class-Based Menu Example
```python
import libstormgames as sg
import pygame
class MyGame:
def __init__(self):
# Create and initialize game
self.game = sg.Game("Class-Based Menu Example").initialize()
self.sounds = self.game.sound.get_sounds()
def play_game(self):
self.game.speak("Starting game!")
# Game code here
self.game.speak("Game over!")
return "menu"
def practice_mode(self):
self.game.speak("Starting practice mode!")
# Practice mode code here
return "menu"
def settings(self):
# A nested menu example
submenu_options = ["graphics", "audio", "controls", "back"]
current = 0
while True:
self.game.speak(submenu_options[current])
event = pygame.event.wait()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP and current > 0:
current -= 1
elif event.key == pygame.K_DOWN and current < len(submenu_options) - 1:
current += 1
elif event.key == pygame.K_RETURN:
if submenu_options[current] == "back":
return "menu"
self.game.speak(f"Selected {submenu_options[current]} settings")
return "menu"
elif event.key == pygame.K_ESCAPE:
return "menu"
def run(self):
# Main menu loop
while True:
# Use playCallback parameter to directly call play_game when "play" is selected
choice = sg.game_menu(self.sounds, None, "practice_mode",
"settings")
# Handle other menu options
if choice == "practice_mode":
self.practice_mode()
elif choice == "settings":
self.settings()
# Run the game
if __name__ == "__main__":
game = MyGame()
game.run()
```
### Text Display
```python
@ -319,6 +500,7 @@ sg.messagebox("Game Over! You scored 100 points.")
name = sg.get_input("Enter your name:", "Player")
```
### Sound Effects
```python
@ -358,6 +540,7 @@ mid_value = sg.lerp(start=0, end=10, factor=0.5) # Returns 5.0
smooth_value = sg.smooth_step(edge0=0, edge1=10, x=5) # Smooth transition
```
## Advanced Examples
### Using the Game Class (Modern Approach)
@ -409,6 +592,7 @@ if __name__ == "__main__":
main()
```
### Complex Sound Environment
```python
@ -459,6 +643,7 @@ def create_sound_environment(player_x, player_y):
sg.exit_game()
```
### Complete Game Structure with Class-Based Architecture
```python
@ -549,12 +734,10 @@ class MyGame:
# Main menu loop
while True:
sounds = self.game.sound.get_sounds()
choice = sg.game_menu(sounds, "play_game", "settings",
choice = sg.game_menu(sounds, self.play_game, "settings",
"instructions", "credits", "donate", "exit_game")
if choice == "play_game":
self.play_game()
elif choice == "settings":
if choice == "settings":
self.settings()
elif choice == "instructions":
sg.instructions()
@ -571,6 +754,7 @@ if __name__ == "__main__":
game.run()
```
## Best Practices
1. **Modern vs Traditional Approach**:
@ -586,18 +770,16 @@ if __name__ == "__main__":
- Implement the Alt+key volume controls in your game
- Use volume services for better control
4. **Speech feedback**:
- Provide clear speech feedback for all actions
- Use the `interrupt` parameter to control speech priority
5. **Sound positioning**:
- Use positional audio to create an immersive environment
- Update object positions as the game state changes
6. **Configuration**:
4. **Configuration**:
- Save user preferences using the Config class
- Load settings at startup
5. **Path initialization**:
- Always initialize the framework with a proper game title
- Game title is used to determine configuration directory paths
- Services are interconnected, so proper initialization ensures correct operation
## Troubleshooting
### No Sound
@ -618,6 +800,26 @@ if __name__ == "__main__":
- Ensure pygame is properly handling events
- Check event loop for proper event handling
### Scoreboard/Configuration Issues
- Check if path services are properly initialized with a game title
- Verify write permissions in the configuration directory
- For debugging, use the following code to view path configuration:
```python
from libstormgames.services import PathService, ConfigService
# Print path information
path_service = PathService.get_instance()
print(f"Game Name: {path_service.gameName}")
print(f"Game Path: {path_service.gamePath}")
# Check config service connection
config_service = ConfigService.get_instance()
print(f"Config connected to path: {hasattr(config_service, 'pathService')}")
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.