Hopefully improved the sound handling for looping and for Y positioning. Should make it easier to have things falling or approaching from the Y axis.
This commit is contained in:
parent
3b01662d98
commit
d513927d52
552
README.md
552
README.md
@ -7,7 +7,7 @@ A Python library to make creating audio games easier.
|
||||
|
||||
`libstormgames` provides a comprehensive set of tools for developing accessible games with audio-first design. It handles common game development tasks including:
|
||||
|
||||
- Sound and music playback with positional audio
|
||||
- Sound and music playback with positional audio and 3D spatialization
|
||||
- Text-to-speech integration
|
||||
- Configuration management
|
||||
- Score tracking and high score tables
|
||||
@ -28,6 +28,7 @@ A Python library to make creating audio games easier.
|
||||
- numpy>=1.19.0
|
||||
- wxpython
|
||||
|
||||
|
||||
#### Speech Providers (one required)
|
||||
|
||||
- Linux/Unix: `python-speechd` or `accessible-output2>=0.14`
|
||||
@ -76,14 +77,16 @@ def main():
|
||||
|
||||
# Define menu options
|
||||
while True:
|
||||
choice = sg.game_menu(sounds)
|
||||
if choice == "play":
|
||||
play_game()
|
||||
choice = sg.game_menu(sounds, play_game)
|
||||
|
||||
# The game_menu function already includes standard options like
|
||||
# high scores, instructions, credits, and exit
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
|
||||
### Modern Class-Based Approach
|
||||
|
||||
```python
|
||||
@ -107,9 +110,10 @@ def main():
|
||||
|
||||
# Define menu options
|
||||
while True:
|
||||
choice = sg.game_menu(game.sound.get_sounds())
|
||||
if choice == "play":
|
||||
play_game()
|
||||
choice = sg.game_menu(game.sound.sounds, play_game)
|
||||
|
||||
# The game_menu function already includes standard options like
|
||||
# high scores, instructions, credits, and exit
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -122,7 +126,7 @@ The library is organized into modules, each with a specific focus:
|
||||
|
||||
- **config**: Configuration management
|
||||
- **services**: Core services that replace global variables
|
||||
- **sound**: Sound and music playback
|
||||
- **sound**: Sound and music playback with 3D positional audio
|
||||
- **speech**: Text-to-speech functionality
|
||||
- **scoreboard**: High score tracking
|
||||
- **input**: Input handling and dialogs
|
||||
@ -149,11 +153,9 @@ game.scoreboard.increase_score(10)
|
||||
game.sound.play_random("explosion")
|
||||
|
||||
# Display text
|
||||
|
||||
game.display_text(["Line 1", "Line 2"])
|
||||
|
||||
# Clean exit
|
||||
|
||||
game.exit()
|
||||
```
|
||||
|
||||
@ -205,444 +207,85 @@ Configuration files are automatically stored in standard system locations:
|
||||
|
||||
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.
|
||||
|
||||
```python
|
||||
# Create a sound manager
|
||||
sound_system = sg.Sound("sounds/")
|
||||
|
||||
# Get the dictionary of loaded sounds
|
||||
sounds = sound_system.get_sounds()
|
||||
sounds = sound_system.sounds
|
||||
|
||||
# Play a sound
|
||||
sg.play_sound(sounds["explosion"])
|
||||
channel = sound_system.play_sound("explosion")
|
||||
|
||||
# Play a sound with positional audio (player at x=5, object at x=10)
|
||||
channel = sg.obj_play(sounds, "footsteps", 5, 10)
|
||||
# Play a looping sound
|
||||
channel = sound_system.play_sound("ambient", loop=True)
|
||||
|
||||
# Play a sound with 3D positional audio (horizontal and vertical positioning)
|
||||
channel = sound_system.obj_play("footsteps",
|
||||
playerPos=5, objPos=10,
|
||||
playerY=0, objY=5,
|
||||
loop=True)
|
||||
|
||||
# Update sound position as player or object moves
|
||||
channel = sg.obj_update(channel, 6, 10) # Player moved to x=6
|
||||
channel = sound_system.obj_update(channel, 6, 10, 0, 5) # Player moved to x=6
|
||||
|
||||
# Stop the sound
|
||||
channel = sound_system.obj_stop(channel)
|
||||
|
||||
# Update all active looping sounds with a single call
|
||||
updated_count = sound_system.update_all_active_loops(player_x=6, player_y=0)
|
||||
|
||||
# Global function equivalents
|
||||
channel = sg.obj_play(sounds, "footsteps", 5, 10, 0, 5, loop=True)
|
||||
channel = sg.obj_update(channel, 6, 10, 0, 5)
|
||||
channel = sg.obj_stop(channel)
|
||||
|
||||
# Play background music
|
||||
sg.play_bgm("sounds/background.ogg")
|
||||
sg.play_bgm("sounds/background.ogg", loop=True)
|
||||
|
||||
# Adjust volume
|
||||
sg.adjust_master_volume(0.1) # Increase master volume
|
||||
sg.adjust_bgm_volume(-0.1) # Decrease background music volume
|
||||
sg.adjust_sfx_volume(0.1) # Increase sound effects volume
|
||||
```
|
||||
|
||||
# Create a sound manager
|
||||
sound_system = sg.Sound("sounds/")
|
||||
|
||||
### Speech
|
||||
# Get the dictionary of loaded sounds
|
||||
sounds = sound_system.sounds
|
||||
|
||||
Provides text-to-speech functionality using available speech providers.
|
||||
# Play a sound
|
||||
channel = sound_system.play_sound("explosion")
|
||||
|
||||
```python
|
||||
# Create a speech manager (usually you'll use the global instance)
|
||||
speech = sg.Speech()
|
||||
# Play a looping sound
|
||||
channel = sound_system.play_sound("ambient", loop=True)
|
||||
|
||||
# Speak text
|
||||
speech.speak("Hello, world!")
|
||||
# Play a sound with 3D positional audio (horizontal and vertical positioning)
|
||||
channel = sound_system.obj_play("footsteps",
|
||||
playerPos=5, objPos=10,
|
||||
playerY=0, objY=5,
|
||||
loop=True)
|
||||
|
||||
# Or use the global function for convenience
|
||||
sg.speak("Hello, world!")
|
||||
# Update sound position as player or object moves
|
||||
channel = sound_system.obj_update(channel, 6, 10, 0, 5) # Player moved to x=6
|
||||
|
||||
# Speak without interrupting previous speech
|
||||
sg.speak("This won't interrupt", interrupt=False)
|
||||
# Stop the sound
|
||||
channel = sound_system.obj_stop(channel)
|
||||
|
||||
# Clean up when done
|
||||
exit_game() or as the class, game.exit_game()
|
||||
```
|
||||
# Update all active looping sounds with a single call
|
||||
updated_count = sound_system.update_all_active_loops(player_x=6, player_y=0)
|
||||
|
||||
# Global function equivalents
|
||||
channel = sg.obj_play(sounds, "footsteps", 5, 10, 0, 5, loop=True)
|
||||
channel = sg.obj_update(channel, 6, 10, 0, 5)
|
||||
channel = sg.obj_stop(channel)
|
||||
|
||||
### Scoreboard
|
||||
|
||||
Tracks scores and manages high score tables.
|
||||
|
||||
```python
|
||||
# Create a scoreboard
|
||||
scoreboard = sg.Scoreboard()
|
||||
|
||||
# Manipulate score
|
||||
scoreboard.increase_score(10)
|
||||
scoreboard.decrease_score(5)
|
||||
current_score = scoreboard.get_score()
|
||||
|
||||
# Check for high score
|
||||
position = scoreboard.check_high_score()
|
||||
if position:
|
||||
print(f"You're in position {position}!")
|
||||
|
||||
# Add high score (prompts for player name)
|
||||
scoreboard.add_high_score()
|
||||
|
||||
# Get all high scores
|
||||
high_scores = scoreboard.get_high_scores()
|
||||
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
|
||||
|
||||
```python
|
||||
# Initialize game systems (traditional approach)
|
||||
sounds = sg.initialize_gui("Game Title")
|
||||
|
||||
# Or use the Game class (modern approach)
|
||||
game = sg.Game("Game Title").initialize()
|
||||
|
||||
# Pause the game (freezes until user presses backspace)
|
||||
sg.pause_game()
|
||||
|
||||
# Check if user wants to exit
|
||||
if sg.check_for_exit():
|
||||
sg.exit_game()
|
||||
|
||||
# Exit the game properly
|
||||
sg.exit_game()
|
||||
```
|
||||
|
||||
### Menu System
|
||||
|
||||
```python
|
||||
# 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...
|
||||
|
||||
# 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)
|
||||
|
||||
# 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
|
||||
# Display text with navigation
|
||||
sg.display_text([
|
||||
"Line 1 of instructions",
|
||||
"Line 2 of instructions",
|
||||
"Line 3 of instructions"
|
||||
])
|
||||
|
||||
# Display a simple message box
|
||||
sg.messagebox("Game Over! You scored 100 points.")
|
||||
|
||||
# Get text input from user
|
||||
name = sg.get_input("Enter your name:", "Player")
|
||||
```
|
||||
|
||||
|
||||
### Sound Effects
|
||||
|
||||
```python
|
||||
# Play a random variation of a sound
|
||||
sg.play_random(sounds, "explosion")
|
||||
|
||||
# Play positional sound with distance-based volume
|
||||
sg.play_random_positional(sounds, "footsteps", player_x=5, object_x=10)
|
||||
|
||||
# Play directional sound (simplified left/right positioning)
|
||||
sg.play_directional_sound(sounds, "voice", player_x=5, object_x=10)
|
||||
|
||||
# Play a sound as a cutscene (interrupts other sounds, waits until complete)
|
||||
sg.cut_scene(sounds, "intro_speech")
|
||||
|
||||
# Play or update a falling sound
|
||||
channel = sg.play_random_falling(sounds, "rock", player_x=5, object_x=8, start_y=10, currentY=5)
|
||||
```
|
||||
|
||||
### Utility Functions
|
||||
|
||||
```python
|
||||
# Check for game updates
|
||||
update_info = sg.check_for_updates("1.0.0", "My Game", "https://example.com/version.json")
|
||||
|
||||
# Check compatibility with library version
|
||||
is_compatible = sg.check_compatibility("1.0.0", "1.2.3")
|
||||
|
||||
# Sanitize a filename for any OS
|
||||
safe_name = sg.sanitize_filename("User's File.txt")
|
||||
|
||||
# Calculate distance between points
|
||||
distance = sg.distance_2d(x1=5, y1=10, x2=8, y2=15)
|
||||
|
||||
# Interpolation functions
|
||||
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)
|
||||
|
||||
```python
|
||||
import libstormgames as sg
|
||||
import pygame
|
||||
import random
|
||||
|
||||
def main():
|
||||
# Create and initialize the game
|
||||
game = sg.Game("My Advanced Game").initialize()
|
||||
|
||||
# Set up game environment
|
||||
game.play_bgm("sounds/background.ogg")
|
||||
|
||||
# Main game loop
|
||||
running = True
|
||||
player_x = 5
|
||||
|
||||
while running:
|
||||
# Process events
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
running = False
|
||||
elif event.key == pygame.K_SPACE:
|
||||
# Score points
|
||||
game.scoreboard.increase_score(5)
|
||||
game.speak(f"Score: {game.scoreboard.get_score()}")
|
||||
|
||||
# Update game state
|
||||
player_x += random.uniform(-0.2, 0.2)
|
||||
|
||||
# Add random ambient sounds
|
||||
if random.random() < 0.05:
|
||||
sounds = game.sound.get_sounds()
|
||||
if "ambient" in sounds:
|
||||
sg.play_random_positional(sounds, "ambient", player_x,
|
||||
player_x + random.uniform(-5, 5))
|
||||
|
||||
pygame.time.delay(50)
|
||||
|
||||
# Game over and cleanup
|
||||
game.speak("Game over!")
|
||||
game.exit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
|
||||
### Complex Sound Environment
|
||||
|
||||
```python
|
||||
import libstormgames as sg
|
||||
import time
|
||||
import random
|
||||
|
||||
def create_sound_environment(player_x, player_y):
|
||||
sounds = sg.initialize_gui("Sound Environment Demo")
|
||||
|
||||
# Place sound sources
|
||||
water_x, water_y = 10, 5
|
||||
fire_x, fire_y = 15, 8
|
||||
wind_x, wind_y = 3, 12
|
||||
|
||||
# Play ambient sounds
|
||||
water_channel = sg.obj_play(sounds, "water", player_x, water_x)
|
||||
fire_channel = sg.obj_play(sounds, "fire", player_x, fire_x)
|
||||
wind_channel = sg.obj_play(sounds, "wind", player_x, wind_x)
|
||||
|
||||
# Main loop
|
||||
running = True
|
||||
while running:
|
||||
# Simulate player movement
|
||||
player_x += random.uniform(-0.5, 0.5)
|
||||
player_y += random.uniform(-0.5, 0.5)
|
||||
|
||||
# Update sound positions
|
||||
water_channel = sg.obj_update(water_channel, player_x, water_x)
|
||||
fire_channel = sg.obj_update(fire_channel, player_x, fire_x)
|
||||
wind_channel = sg.obj_update(wind_channel, player_x, wind_x)
|
||||
|
||||
# Occasionally play random sound
|
||||
if random.random() < 0.1:
|
||||
sg.play_random_positional(sounds, "creature", player_x,
|
||||
player_x + random.uniform(-5, 5))
|
||||
|
||||
# Check for exit
|
||||
if sg.check_for_exit():
|
||||
running = False
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
# Clean up
|
||||
sg.obj_stop(water_channel)
|
||||
sg.obj_stop(fire_channel)
|
||||
sg.obj_stop(wind_channel)
|
||||
sg.exit_game()
|
||||
```
|
||||
# Play background music (now with loop parameter)
|
||||
sg.play_bgm("sounds/background.ogg", loop=True)
|
||||
|
||||
# Adjust volume
|
||||
sg.adjust_master_volume(0.1) # Increase master volume
|
||||
sg.adjust_bgm_volume(-0.1) # Decrease background music volume
|
||||
sg.adjust_sfx_volume(0.1) # Increase sound effects volume
|
||||
|
||||
### Complete Game Structure with Class-Based Architecture
|
||||
|
||||
@ -658,7 +301,7 @@ class MyGame:
|
||||
|
||||
# Game state
|
||||
self.player_x = 5
|
||||
self.player_y = 5
|
||||
self.player_y = 0
|
||||
self.difficulty = "normal"
|
||||
|
||||
# Load settings
|
||||
@ -675,28 +318,53 @@ class MyGame:
|
||||
|
||||
# Game loop
|
||||
running = True
|
||||
|
||||
# Create enemies with positional audio
|
||||
enemies = []
|
||||
for i in range(3):
|
||||
enemy = {
|
||||
'x': random.uniform(10, 30),
|
||||
'y': random.uniform(-5, 5),
|
||||
'channel': None
|
||||
}
|
||||
# Start looping sound for each enemy
|
||||
enemy['channel'] = self.game.sound.play_random_positional(
|
||||
"enemy", self.player_x, enemy['x'], self.player_y, enemy['y'], loop=True
|
||||
)
|
||||
enemies.append(enemy)
|
||||
|
||||
while running:
|
||||
# Update game state
|
||||
self.player_x += random.uniform(-0.2, 0.2)
|
||||
for enemy in enemies:
|
||||
# Move enemies toward player
|
||||
if enemy['x'] > self.player_x:
|
||||
enemy['x'] -= 0.1
|
||||
else:
|
||||
enemy['x'] += 0.1
|
||||
|
||||
# Handle input
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
running = False
|
||||
elif event.key == pygame.K_LEFT:
|
||||
self.player_x -= 1
|
||||
elif event.key == pygame.K_RIGHT:
|
||||
self.player_x += 1
|
||||
elif event.key == pygame.K_SPACE:
|
||||
self.game.scoreboard.increase_score()
|
||||
self.game.speak(f"Score: {self.game.scoreboard.get_score()}")
|
||||
|
||||
# Add some random sounds
|
||||
if random.random() < 0.05:
|
||||
sounds = self.game.sound.get_sounds()
|
||||
if "ambient" in sounds:
|
||||
sg.play_random_positional(sounds, "ambient",
|
||||
self.player_x, self.player_x + random.uniform(-10, 10))
|
||||
# Update all sound positions with one call
|
||||
self.game.sound.update_all_active_loops(self.player_x, self.player_y)
|
||||
|
||||
pygame.time.delay(50)
|
||||
|
||||
# Stop all enemy sounds
|
||||
for enemy in enemies:
|
||||
if enemy['channel']:
|
||||
self.game.sound.obj_stop(enemy['channel'])
|
||||
|
||||
# Game over
|
||||
position = self.game.scoreboard.check_high_score()
|
||||
if position:
|
||||
@ -733,20 +401,12 @@ class MyGame:
|
||||
def run(self):
|
||||
# Main menu loop
|
||||
while True:
|
||||
sounds = self.game.sound.get_sounds()
|
||||
choice = sg.game_menu(sounds, self.play_game, "settings",
|
||||
"instructions", "credits", "donate", "exit_game")
|
||||
# Use playCallback for play option and add custom "settings" option
|
||||
# Note: standard options like instructions, high_scores, etc. are handled automatically
|
||||
choice = sg.game_menu(self.game.sound.sounds, self.play_game, "settings")
|
||||
|
||||
if choice == "settings":
|
||||
self.settings()
|
||||
elif choice == "instructions":
|
||||
sg.instructions()
|
||||
elif choice == "credits":
|
||||
sg.credits()
|
||||
elif choice == "donate":
|
||||
sg.donate()
|
||||
elif choice == "exit_game":
|
||||
self.game.exit()
|
||||
|
||||
# Run the game
|
||||
if __name__ == "__main__":
|
||||
@ -757,24 +417,29 @@ if __name__ == "__main__":
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Modern vs Traditional Approach**:
|
||||
1. **Sound Looping and Positioning**:
|
||||
- Use the `loop=True` parameter for sounds that need to loop
|
||||
- For moving sound sources, use `update_all_active_loops()` to efficiently update all sounds at once
|
||||
- Include vertical positioning (Y-axis) if you need 3D audio
|
||||
|
||||
2. **Modern vs Traditional Approach**:
|
||||
- New projects: Use the Game class for better organization
|
||||
- Existing projects: Continue with global functions for compatibility
|
||||
- Both approaches are fully supported
|
||||
|
||||
2. **Always clean up resources**:
|
||||
3. **Always clean up resources**:
|
||||
- Use `exit_game()` or `game.exit()` when exiting to ensure proper cleanup
|
||||
- Stop sounds that are no longer needed
|
||||
- Stop sounds that are no longer needed with `obj_stop()`
|
||||
|
||||
3. **Volume control**:
|
||||
4. **Volume control**:
|
||||
- Implement the Alt+key volume controls in your game
|
||||
- Use volume services for better control
|
||||
|
||||
4. **Configuration**:
|
||||
5. **Configuration**:
|
||||
- Save user preferences using the Config class
|
||||
- Load settings at startup
|
||||
|
||||
5. **Path initialization**:
|
||||
6. **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
|
||||
@ -799,6 +464,7 @@ if __name__ == "__main__":
|
||||
|
||||
- Ensure pygame is properly handling events
|
||||
- Check event loop for proper event handling
|
||||
- Remember to use pygame.event.pump() especially in the game loop
|
||||
|
||||
### Scoreboard/Configuration Issues
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user