Pygame To Mini Game Source section: `https://appsudo.com/doc#pygame-to-mini-game` Core idea - Appsudo can host pygame-style mini-games. - Most existing pygame logic stays the same. - The main migration is structural: wrap the game in an `App`, subclass `GameWindow`, and render through `Canvas`. What stays the same - `import pygame` stays unchanged. - Game logic, sprites, physics, collisions, and particles are mostly portable. - Some unsupported calls no-op instead of failing, which helps incremental ports. Main migration steps - Add imports for `App`, `Canvas`, and `GameWindow`. - Replace standalone `main()` loops with `class MyGame(GameWindow): def on_draw(self, canvas): ...` - Replace module globals with instance attributes where practical. - Render the game via a `Canvas` owned by the app wrapper. - End gameplay by calling `self.set_running(False)` instead of only toggling a local loop variable. Wrapper pattern ```python class MyGameApp(App): def __init__(self, application_path): App.__init__(self, "MyGame", application_path) self.setup() def setup(self): self.ui_view = Canvas() self.game = MyGame(self.ui_view) def run(self): App.run(self) self.render(self.ui_view.render()) ``` Input model - Mobile uses an overlay controller. - Desktop runner also forwards keyboard input during development. - Common mappings: `K_DPAD_UP`, `K_DPAD_DOWN`, `K_DPAD_LEFT`, `K_DPAD_RIGHT`, `K_BUTTON_START`, `K_BUTTON_SELECT`. - `InputManager` is the abstraction for simulating input and haptics. Other guidance - `pygame.mixer.init()` is safe but unnecessary. - `DataStore` is presented as the Appsudo idiom for persistence such as high scores. - `get_shareable()` only controls feed/DM sharing, not search/discoverability. - You can optionally compose normal Appsudo UI beside the canvas. Code Snippets From Docs - The blocks below were extracted from the corresponding section of `appsudo.com/doc`. Snippet 1 ``` import pygame import random import math ``` Snippet 2 ``` from appsudo import App from components.canvas import Canvas from components.extras.game.window import GameWindow import pygame import random import math ``` Snippet 3 ``` class MyGameApp(App): def __init__(self, application_path): App.__init__(self, "MyGame", application_path) self.setup() def setup(self): self.ui_view = Canvas() self.game = MyGame(self.ui_view) def play(self): self.render(self.ui_view.render()) def run(self): App.run(self) self.play() def get_shareable(self): return True # flag: allow this app on feed / DM (search discoverability is unaffected) def on_close(self): pass ``` Snippet 4 ``` screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) clock = pygame.time.Clock() def main(): running = True while running: for event in pygame.event.get(): ... screen.fill(COLOR_BACKGROUND) snake.draw(screen) pygame.display.flip() clock.tick(game_speed) ``` Snippet 5 ``` class MyGame(GameWindow): def __init__(self, canvas_view=None): super().__init__(canvas_view) self.GRID_SIZE = 20 self.SCREEN_WIDTH = 600 self.SCREEN_HEIGHT = 760 self.COLOR_BACKGROUND = (13, 17, 23) self.clock = pygame.time.Clock() self.screen = pygame.display.set_mode((self.SCREEN_WIDTH, self.SCREEN_HEIGHT)) self.game_speed = 10 self.reset_game() def on_draw(self, canvas): self.handle_input() self.update_game_logic() canvas.fill(self.COLOR_BACKGROUND) self.snake.draw(canvas, self.GRID_SIZE) pygame.display.flip() self.clock.tick(self.game_speed) ``` Snippet 6 ``` for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: running = False elif event.key == pygame.K_p: paused = not paused elif event.key in [pygame.K_UP, pygame.K_w]: snake.turn((0, -1)) ``` Snippet 7 ``` for event in pygame.event.get(): if event.type == pygame.QUIT: self.set_running(False) if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.set_running(False) elif event.key == pygame.K_BUTTON_START: self.toggle_pause() elif event.key in [pygame.K_DPAD_UP, pygame.K_w]: self.snake.turn((0, -1)) ``` Snippet 8 ``` def generate_sound(frequency=440, duration=0.1): sample_rate = pygame.mixer.get_init()[0] max_amplitude = 2 ** (pygame.mixer.get_init()[2] - 1) - 1 n_samples = int(round(duration * sample_rate)) buf = bytearray([0] * (n_samples * 2)) for i in range(n_samples): value = int(round(max_amplitude * math.sin(2 * math.pi * frequency * i / sample_rate))) buf[i*2] = value & 0xFF buf[i*2+1] = (value >> 8) & 0xFF return pygame.mixer.Sound(buf) sound_eat = generate_sound(660, 0.08) sound_eat.play() ``` Snippet 9 ``` try: with open("highscore.txt", "r") as f: self.high_score = int(f.read()) except (FileNotFoundError, ValueError): self.high_score = 0 # later with open("highscore.txt", "w") as f: f.write(str(self.high_score)) ``` Snippet 10 ``` self.high_score = DataStore.get("highscore", default=0) # later DataStore.put("highscore", self.high_score) ``` Detailed Coverage Map - The entries below were derived from the section structure in `appsudo.com/doc`. Articles - `pygame-to-mini-game-overview`: Overview - `pygame-to-mini-game-checklist`: Migration Checklist - `pygame-to-mini-game-imports`: 1. Imports - `pygame-to-mini-game-app-wrapper`: 2. Wrap the game in an App - `pygame-to-mini-game-gamewindow`: 3. Subclass GameWindow instead of writing main() - `pygame-to-mini-game-ui-alongside-canvas`: 4. Compose regular UI alongside the Canvas (optional) - `pygame-to-mini-game-input`: 5. Input: gamepad on mobile, keyboard for local dev - `pygame-to-mini-game-sound`: 6. Sound - `pygame-to-mini-game-persistence`: 7. Persistence - `pygame-to-mini-game-conventions`: 8. Conventions to follow