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. A Python library to make creating audio games easier.
## Overview ## Overview
`libstormgames` provides a comprehensive set of tools for developing accessible games with audio-first design. It handles common game development tasks including: `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 - Menu systems and text display
- GUI initialization - GUI initialization
## Installation ## Installation
### Requirements ### Requirements
@ -31,18 +33,23 @@ A Python library to make creating audio games easier.
- Linux/Unix: `python-speechd` or `accessible-output2>=0.14` - Linux/Unix: `python-speechd` or `accessible-output2>=0.14`
- Windows/macOS: `accessible-output2>=0.14` - Windows/macOS: `accessible-output2>=0.14`
### Install from source ### Install from source
If you are on Linux, check your package manager first to see if the packages in requirements.txt are available.
```bash ```bash
git clone https://git.stormux.org/storm/libstormgames git clone https://git.stormux.org/storm/libstormgames
cd libstormgames cd libstormgames
pip install -e . pip install -e .
``` ```
## Getting Started ## Getting Started
You can use libstormgames in two ways: the traditional function-based approach or the new class-based approach. You can use libstormgames in two ways: the traditional function-based approach or the new class-based approach.
### Traditional Function-Based Approach ### Traditional Function-Based Approach
```python ```python
@ -69,8 +76,8 @@ def main():
# Define menu options # Define menu options
while True: while True:
choice = sg.game_menu(sounds, "play_game", "instructions", "credits", "donate", "exit_game") choice = sg.game_menu(sounds)
if choice == "play_game": if choice == "play":
play_game() play_game()
if __name__ == "__main__": if __name__ == "__main__":
@ -87,7 +94,7 @@ def main():
# Create and initialize a game # Create and initialize a game
game = sg.Game("My First Audio Game").initialize() game = sg.Game("My First Audio Game").initialize()
# Welcome message (using fluent API) # Welcome message
game.speak("Welcome to My First Audio Game!") game.speak("Welcome to My First Audio Game!")
# Main game loop # Main game loop
@ -100,14 +107,15 @@ def main():
# Define menu options # Define menu options
while True: while True:
choice = sg.game_menu(game.sound.get_sounds(), "play_game", "instructions", "credits", "donate", "exit_game") choice = sg.game_menu(game.sound.get_sounds())
if choice == "play_game": if choice == "play":
play_game() play_game()
if __name__ == "__main__": if __name__ == "__main__":
main() main()
``` ```
## Library Structure ## Library Structure
The library is organized into modules, each with a specific focus: 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 - **menu**: Menu systems
- **utils**: Utility functions and Game class - **utils**: Utility functions and Game class
## Core Classes ## Core Classes
### Game ### Game
@ -133,19 +142,27 @@ The Game class provides a central way to manage all game systems:
game = sg.Game("My Game").initialize() game = sg.Game("My Game").initialize()
# Use fluent API for chaining commands # Use fluent API for chaining commands
game.speak("Hello").play_bgm("music/theme.ogg") game.speak("Hello").play_bgm("music/theme")
# Access components directly # Access components directly
game.scoreboard.increase_score(10) game.scoreboard.increase_score(10)
game.sound.play_random("explosion") game.sound.play_random("explosion")
# Display text # Display text
game.display_text(["Line 1", "Line 2"]) game.display_text(["Line 1", "Line 2"])
# Clean exit # Clean exit
game.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 ### Services
The library includes several service classes that replace global variables: 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") 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 ### Sound
Manages sound loading and playback with positional audio support. 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 sg.adjust_sfx_volume(0.1) # Increase sound effects volume
``` ```
### Speech ### Speech
Provides text-to-speech functionality using available speech providers. 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) sg.speak("This won't interrupt", interrupt=False)
# Clean up when done # Clean up when done
speech.close() exit_game() or as the class, game.exit_game()
``` ```
### Scoreboard ### Scoreboard
Tracks scores and manages high score tables. Tracks scores and manages high score tables.
@ -261,6 +288,15 @@ for entry in high_scores:
print(f"{entry['name']}: {entry['score']}") 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 ## Key Functions
### Game Initialization and Control ### Game Initialization and Control
@ -286,22 +322,167 @@ sg.exit_game()
### Menu System ### Menu System
```python ```python
# Display a menu with options (functions should exist with these names) # Basic menu example (returns option string)
choice = sg.game_menu(sounds, "play_game", "high_scores", "instructions", "credits", "exit_game") choice = sg.game_menu(sounds)
if choice == "play":
# Start the game
play_game()
elif choice == "instructions":
sg.instructions()
# etc...
# Display built-in instructions # Menu with play callback (callback is executed directly)
sg.instructions() def play_callback():
sg.speak("Game started with callback!")
# Game code goes here
sg.game_menu(sounds, play_callback)
# Display built-in credits # Menu with custom options
sg.credits() choice = sg.game_menu(sounds, None, "practice_mode")
if choice == "practice_mode":
# Open donation page # Handle practice mode
sg.donate() practice_game()
elif choice == "difficulty":
# Interactive menu to learn available sounds # Handle difficulty settings
sg.learn_sounds(sounds) 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 ### Text Display
```python ```python
@ -319,6 +500,7 @@ sg.messagebox("Game Over! You scored 100 points.")
name = sg.get_input("Enter your name:", "Player") name = sg.get_input("Enter your name:", "Player")
``` ```
### Sound Effects ### Sound Effects
```python ```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 smooth_value = sg.smooth_step(edge0=0, edge1=10, x=5) # Smooth transition
``` ```
## Advanced Examples ## Advanced Examples
### Using the Game Class (Modern Approach) ### Using the Game Class (Modern Approach)
@ -409,6 +592,7 @@ if __name__ == "__main__":
main() main()
``` ```
### Complex Sound Environment ### Complex Sound Environment
```python ```python
@ -459,6 +643,7 @@ def create_sound_environment(player_x, player_y):
sg.exit_game() sg.exit_game()
``` ```
### Complete Game Structure with Class-Based Architecture ### Complete Game Structure with Class-Based Architecture
```python ```python
@ -549,12 +734,10 @@ class MyGame:
# Main menu loop # Main menu loop
while True: while True:
sounds = self.game.sound.get_sounds() 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") "instructions", "credits", "donate", "exit_game")
if choice == "play_game": if choice == "settings":
self.play_game()
elif choice == "settings":
self.settings() self.settings()
elif choice == "instructions": elif choice == "instructions":
sg.instructions() sg.instructions()
@ -571,6 +754,7 @@ if __name__ == "__main__":
game.run() game.run()
``` ```
## Best Practices ## Best Practices
1. **Modern vs Traditional Approach**: 1. **Modern vs Traditional Approach**:
@ -586,18 +770,16 @@ if __name__ == "__main__":
- Implement the Alt+key volume controls in your game - Implement the Alt+key volume controls in your game
- Use volume services for better control - Use volume services for better control
4. **Speech feedback**: 4. **Configuration**:
- 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**:
- Save user preferences using the Config class - Save user preferences using the Config class
- Load settings at startup - 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 ## Troubleshooting
### No Sound ### No Sound
@ -618,6 +800,26 @@ if __name__ == "__main__":
- Ensure pygame is properly handling events - Ensure pygame is properly handling events
- Check event loop for proper event handling - 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 ## Contributing
Contributions are welcome! Please feel free to submit a Pull Request. Contributions are welcome! Please feel free to submit a Pull Request.