Перейти к содержанию

Основной игровой класс

pygine.game

Главный игровой класс, управляющий игровым циклом и окном.

Game

Главный игровой класс, который управляет окном, игровым циклом и базовой функциональностью.

Класс предоставляет простой интерфейс для создания игр с автоматическим управлением игровым циклом, обработкой событий и контролем частоты кадров.

Аргументы

width: Ширина окна в пикселях height: Высота окна в пикселях title: Заголовок окна fps: Целевая частота кадров background_color: Цвет фона в формате (R, G, B) background_image: Путь к изображению фона (опционально) *, create_display: bool = True,

Пример

game = Game(800, 600, "Моя Игра") player = AnimatedSprite("player.png", (32, 32))

def update(): ... player.update() ... def draw(): ... player.draw(game.screen) ... game.run(update, draw)

Пример с фоновым изображением

game = Game(800, 600, "Игра с фоном", background_image="background.png") game.run(update, draw)

Source code in pygine/game.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
class Game:
    """
    Главный игровой класс, который управляет окном, игровым циклом
    и базовой функциональностью.

    Класс предоставляет простой интерфейс для создания игр с автоматическим
    управлением игровым циклом, обработкой событий и контролем частоты кадров.

    Аргументы:
        width: Ширина окна в пикселях
        height: Высота окна в пикселях
        title: Заголовок окна
        fps: Целевая частота кадров
        background_color: Цвет фона в формате (R, G, B)
        background_image: Путь к изображению фона (опционально)
        *,
        create_display: bool = True,

    Пример:
        >>> game = Game(800, 600, "Моя Игра")
        >>> player = AnimatedSprite("player.png", (32, 32))
        >>>
        >>> def update():
        ...     player.update()
        ...
        >>> def draw():
        ...     player.draw(game.screen)
        ...
        >>> game.run(update, draw)

    Пример с фоновым изображением:
        >>> game = Game(800, 600, "Игра с фоном", background_image="background.png")
        >>> game.run(update, draw)
    """

    def __init__(
        self,
        width: int = 800,
        height: int = 600,
        title: str = "Pygame Easy Game",
        fps: int = 60,
        background_color: Tuple[int, int, int] = (50, 50, 50),
        background_image: Optional[str] = None,
        *,
        create_display: bool = True,
    ):
        # Инициализируем pygame
        if not pygame.get_init():
            pygame.init()

        # Свойства окна
        self.width = width
        self.height = height
        self.title = title
        self.fps = fps
        self.background_color = background_color

        # Фоновое изображение (инициализируем переменные)
        self.background_image_path = background_image
        self.background_image = None
        self.background_surface = None

        # Создаём окно, только если об этом явно не попросили отказаться.
        if create_display:
            self.screen = pygame.display.set_mode((width, height))
            pygame.display.set_caption(title)
        else:
            # Если пользователь уже создал окно – забираем его.
            existing = pygame.display.get_surface()
            if existing is not None:
                self.screen = existing
            else:
                # Fallback: создаём временную поверхность (off-screen).
                self.screen = pygame.Surface((width, height))

        # Загружаем фоновое изображение после создания окна
        if background_image:
            self._load_background_image(background_image)

        # Параметры игрового цикла
        self.clock = pygame.time.Clock()
        self.running = False
        self.paused = False

        # Отслеживание дельта-времени
        self.dt = 0.0
        self.last_time = 0.0

        # Колбэки событий
        self.update_callback: Optional[Callable] = None
        self.draw_callback: Optional[Callable] = None
        self.event_callbacks: List[Callable] = []

        # Группа спрайтов для автоматического управления
        self.all_sprites = pygame.sprite.Group()

        # Отладочная информация
        self.show_fps = False
        self.font = None

    def _load_background_image(self, image_path: str) -> None:
        """
        Загрузить и масштабировать фоновое изображение.

        Аргументы:
            image_path: Путь к файлу изображения
        """
        try:
            # Загружаем оригинальное изображение
            self.background_image = pygame.image.load(image_path).convert()

            # Масштабируем изображение под размеры окна
            self.background_surface = pygame.transform.scale(
                self.background_image, (self.width, self.height)
            )

        except pygame.error as e:
            print(f"Предупреждение: Не удалось загрузить фоновое изображение '{image_path}': {e}")
            print("Будет использован цветовой фон.")
            self.background_image = None
            self.background_surface = None

    def run(
        self,
        update_func: Optional[Callable] = None,
        draw_func: Optional[Callable] = None,
    ) -> None:
        """
        Запустить основной игровой цикл.

        Аргументы:
            update_func: Функция, вызываемая каждый кадр для логики игры
            draw_func: Функция, вызываемая каждый кадр для отрисовки

        Пример:
            >>> def update():
            ...     # Логика игры
            ...     pass
            ...
            >>> def draw():
            ...     # Отрисовка
            ...     pass
            ...
            >>> game.run(update, draw)
        """
        self.update_callback = update_func
        self.draw_callback = draw_func
        self.running = True

        try:
            self._game_loop()
        except KeyboardInterrupt:
            pass
        finally:
            self.quit()

    def _game_loop(self) -> None:
        """Реализация основного игрового цикла."""
        while self.running:
            # Calculate delta time
            current_time = pygame.time.get_ticks() / 1000.0
            if self.last_time > 0:
                self.dt = current_time - self.last_time
            else:
                self.dt = 1.0 / self.fps
            self.last_time = current_time

            # Handle events
            self._handle_events()

            # Update input state
            update_input_state()

            if not self.paused:
                # Update game logic
                self._update()

            # Draw everything
            self._draw()

            # Maintain frame rate
            self.clock.tick(self.fps)

    def _handle_events(self) -> None:
        """Обработка событий pygame."""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_F1:
                    self.toggle_fps_display()
                elif event.key == pygame.K_PAUSE or event.key == pygame.K_p:
                    self.toggle_pause()

            # Call custom event callbacks
            for callback in self.event_callbacks:
                callback(event)

    def _update(self) -> None:
        """Обновить игровую логику."""
        # Update all sprites in the group
        self.all_sprites.update(self.dt)

        # Call custom update function
        if self.update_callback:
            self.update_callback()

    def _draw(self) -> None:
        """Отрисовать всё на экран."""
        # Получаем смещение тряски экрана
        shake_offset = get_screen_shake_offset()

        # Создаём временную поверхность для применения тряски
        if shake_offset != (0.0, 0.0):
            # Создаём временный экран для отрисовки
            temp_surface = pygame.Surface((self.width, self.height))
            original_screen = self.screen
            self.screen = temp_surface

        # Отрисовка фона
        if self.background_surface is not None:
            # Используем фоновое изображение
            self.screen.blit(self.background_surface, (0, 0))
        else:
            # Используем цветовой фон
            self.screen.fill(self.background_color)

        # Draw all sprites
        self.all_sprites.draw(self.screen)

        # Call custom draw function
        if self.draw_callback:
            self.draw_callback()

        # Draw debug information
        if self.show_fps:
            self._draw_fps()

        # Применяем тряску, если активна
        if shake_offset != (0.0, 0.0):
            # Восстанавливаем оригинальный экран
            self.screen = original_screen
            # Очищаем экран и рисуем с смещением
            self.screen.fill((0, 0, 0))  # Чёрный фон для границ при тряске
            self.screen.blit(temp_surface, (int(shake_offset[0]), int(shake_offset[1])))

        # Обновляем только если инициализировано окно отображения
        if pygame.display.get_init() and pygame.display.get_surface() is not None:
            pygame.display.flip()

    def _draw_fps(self) -> None:
        """Отрисовать счётчик FPS."""
        if not self.font:
            self.font = pygame.font.Font(None, 36)

        fps_text = f"FPS: {int(self.clock.get_fps())}"
        fps_surface = self.font.render(fps_text, True, (255, 255, 255))
        self.screen.blit(fps_surface, (10, 10))

    def add_sprite(self, sprite: pygame.sprite.Sprite) -> None:
        """
        Добавить спрайт в систему автоматического обновления и отрисовки.

        Аргументы:
            sprite: Спрайт, который нужно добавить
        """
        self.all_sprites.add(sprite)

    def remove_sprite(self, sprite: pygame.sprite.Sprite) -> None:
        """
        Удалить спрайт из системы автоматического управления.

        Аргументы:
            sprite: Спрайт, который нужно удалить
        """
        self.all_sprites.remove(sprite)

    def add_event_callback(self, callback: Callable) -> None:
        """
        Добавить пользовательский обработчик событий.

        Аргументы:
            callback: Функция, принимающая объект события pygame
        """
        self.event_callbacks.append(callback)

    def set_background_color(self, color: Tuple[int, int, int]) -> None:
        """
        Установить цвет фона.

        Аргументы:
            color: RGB-кортеж цвета (0–255)
        """
        self.background_color = color
        # Если установлен цвет фона, убираем фоновое изображение
        self.background_surface = None

    def set_background_image(self, image_path: Optional[str]) -> None:
        """
        Установить фоновое изображение.

        Изображение автоматически масштабируется под размеры окна.
        Передача None отключает фоновое изображение.

        Аргументы:
            image_path: Путь к файлу изображения или None для отключения
        """
        if image_path is None:
            self.background_image_path = None
            self.background_image = None
            self.background_surface = None
        else:
            self.background_image_path = image_path
            self._load_background_image(image_path)

    def has_background_image(self) -> bool:
        """
        Проверить, установлено ли фоновое изображение.

        Возвращает:
            True, если фоновое изображение загружено и готово к использованию
        """
        return self.background_surface is not None

    def set_title(self, title: str) -> None:
        """
        Изменить заголовок окна.

        Аргументы:
            title: Новый заголовок окна
        """
        self.title = title
        pygame.display.set_caption(title)

    def set_fps(self, fps: int) -> None:
        """
        Задать целевую частоту кадров.

        Аргументы:
            fps: Кадров в секунду
        """
        self.fps = max(1, fps)

    def toggle_fps_display(self) -> None:
        """Переключить отображение счётчика FPS."""
        self.show_fps = not self.show_fps

    def toggle_pause(self) -> None:
        """Переключить состояние паузы игры."""
        self.paused = not self.paused

    def pause(self) -> None:
        """Поставить игру на паузу."""
        self.paused = True

    def resume(self) -> None:
        """Возобновить игру."""
        self.paused = False

    def quit(self) -> None:
        """
        Завершить игру и очистить ресурсы.
        """
        self.running = False
        pygame.quit()
        sys.exit()

    def get_screen_rect(self) -> pygame.Rect:
        """
        Получить прямоугольник экрана для проверки границ.

        Возвращает:
            Объект Rect, представляющий границы экрана
        """
        return pygame.Rect(0, 0, self.width, self.height)

    def get_center(self) -> Tuple[int, int]:
        """
        Получить центр экрана.

        Возвращает:
            Координаты центра (x, y)
        """
        return (self.width // 2, self.height // 2)

    def is_point_on_screen(self, x: int, y: int) -> bool:
        """
        Проверить, находится ли точка внутри границ экрана.

        Аргументы:
            x: Координата X
            y: Координата Y

        Возвращает:
            True — если точка на экране
        """
        return 0 <= x < self.width and 0 <= y < self.height

    def screenshot(self, filename: str = "screenshot.png") -> None:
        """
        Сохранить скриншот текущего экрана.

        Аргументы:
            filename: Путь к файлу скриншота
        """
        pygame.image.save(self.screen, filename)

    def get_delta_time(self) -> float:
        """
        Получить дельта-время (время с прошлого кадра) в секундах.

        Возвращает:
            Дельта-время в секундах
        """
        return self.dt

    def get_fps(self) -> float:
        """
        Получить текущую частоту кадров.

        Возвращает:
            Текущее значение FPS
        """
        return self.clock.get_fps()

    def debug_info(self) -> dict:
        """
        Получить отладочную информацию о состоянии игры.

        Возвращает:
            Словарь с отладочной информацией
        """
        return {
            "fps": self.get_fps(),
            "dt": self.dt,
            "running": self.running,
            "paused": self.paused,
            "sprite_count": len(self.all_sprites),
            "screen_size": (self.width, self.height),
            "background_color": self.background_color,
            "background_image": self.background_image_path,
            "has_background_image": self.has_background_image(),
        }

add_event_callback(callback)

Добавить пользовательский обработчик событий.

Аргументы

callback: Функция, принимающая объект события pygame

Source code in pygine/game.py
def add_event_callback(self, callback: Callable) -> None:
    """
    Добавить пользовательский обработчик событий.

    Аргументы:
        callback: Функция, принимающая объект события pygame
    """
    self.event_callbacks.append(callback)

add_sprite(sprite)

Добавить спрайт в систему автоматического обновления и отрисовки.

Аргументы

sprite: Спрайт, который нужно добавить

Source code in pygine/game.py
def add_sprite(self, sprite: pygame.sprite.Sprite) -> None:
    """
    Добавить спрайт в систему автоматического обновления и отрисовки.

    Аргументы:
        sprite: Спрайт, который нужно добавить
    """
    self.all_sprites.add(sprite)

debug_info()

Получить отладочную информацию о состоянии игры.

Возвращает

Словарь с отладочной информацией

Source code in pygine/game.py
def debug_info(self) -> dict:
    """
    Получить отладочную информацию о состоянии игры.

    Возвращает:
        Словарь с отладочной информацией
    """
    return {
        "fps": self.get_fps(),
        "dt": self.dt,
        "running": self.running,
        "paused": self.paused,
        "sprite_count": len(self.all_sprites),
        "screen_size": (self.width, self.height),
        "background_color": self.background_color,
        "background_image": self.background_image_path,
        "has_background_image": self.has_background_image(),
    }

get_center()

Получить центр экрана.

Возвращает

Координаты центра (x, y)

Source code in pygine/game.py
def get_center(self) -> Tuple[int, int]:
    """
    Получить центр экрана.

    Возвращает:
        Координаты центра (x, y)
    """
    return (self.width // 2, self.height // 2)

get_delta_time()

Получить дельта-время (время с прошлого кадра) в секундах.

Возвращает

Дельта-время в секундах

Source code in pygine/game.py
def get_delta_time(self) -> float:
    """
    Получить дельта-время (время с прошлого кадра) в секундах.

    Возвращает:
        Дельта-время в секундах
    """
    return self.dt

get_fps()

Получить текущую частоту кадров.

Возвращает

Текущее значение FPS

Source code in pygine/game.py
def get_fps(self) -> float:
    """
    Получить текущую частоту кадров.

    Возвращает:
        Текущее значение FPS
    """
    return self.clock.get_fps()

get_screen_rect()

Получить прямоугольник экрана для проверки границ.

Возвращает

Объект Rect, представляющий границы экрана

Source code in pygine/game.py
def get_screen_rect(self) -> pygame.Rect:
    """
    Получить прямоугольник экрана для проверки границ.

    Возвращает:
        Объект Rect, представляющий границы экрана
    """
    return pygame.Rect(0, 0, self.width, self.height)

has_background_image()

Проверить, установлено ли фоновое изображение.

Возвращает

True, если фоновое изображение загружено и готово к использованию

Source code in pygine/game.py
def has_background_image(self) -> bool:
    """
    Проверить, установлено ли фоновое изображение.

    Возвращает:
        True, если фоновое изображение загружено и готово к использованию
    """
    return self.background_surface is not None

is_point_on_screen(x, y)

Проверить, находится ли точка внутри границ экрана.

Аргументы

x: Координата X y: Координата Y

Возвращает

True — если точка на экране

Source code in pygine/game.py
def is_point_on_screen(self, x: int, y: int) -> bool:
    """
    Проверить, находится ли точка внутри границ экрана.

    Аргументы:
        x: Координата X
        y: Координата Y

    Возвращает:
        True — если точка на экране
    """
    return 0 <= x < self.width and 0 <= y < self.height

pause()

Поставить игру на паузу.

Source code in pygine/game.py
def pause(self) -> None:
    """Поставить игру на паузу."""
    self.paused = True

quit()

Завершить игру и очистить ресурсы.

Source code in pygine/game.py
def quit(self) -> None:
    """
    Завершить игру и очистить ресурсы.
    """
    self.running = False
    pygame.quit()
    sys.exit()

remove_sprite(sprite)

Удалить спрайт из системы автоматического управления.

Аргументы

sprite: Спрайт, который нужно удалить

Source code in pygine/game.py
def remove_sprite(self, sprite: pygame.sprite.Sprite) -> None:
    """
    Удалить спрайт из системы автоматического управления.

    Аргументы:
        sprite: Спрайт, который нужно удалить
    """
    self.all_sprites.remove(sprite)

resume()

Возобновить игру.

Source code in pygine/game.py
def resume(self) -> None:
    """Возобновить игру."""
    self.paused = False

run(update_func=None, draw_func=None)

Запустить основной игровой цикл.

Аргументы

update_func: Функция, вызываемая каждый кадр для логики игры draw_func: Функция, вызываемая каждый кадр для отрисовки

Пример

def update(): ... # Логика игры ... pass ... def draw(): ... # Отрисовка ... pass ... game.run(update, draw)

Source code in pygine/game.py
def run(
    self,
    update_func: Optional[Callable] = None,
    draw_func: Optional[Callable] = None,
) -> None:
    """
    Запустить основной игровой цикл.

    Аргументы:
        update_func: Функция, вызываемая каждый кадр для логики игры
        draw_func: Функция, вызываемая каждый кадр для отрисовки

    Пример:
        >>> def update():
        ...     # Логика игры
        ...     pass
        ...
        >>> def draw():
        ...     # Отрисовка
        ...     pass
        ...
        >>> game.run(update, draw)
    """
    self.update_callback = update_func
    self.draw_callback = draw_func
    self.running = True

    try:
        self._game_loop()
    except KeyboardInterrupt:
        pass
    finally:
        self.quit()

screenshot(filename='screenshot.png')

Сохранить скриншот текущего экрана.

Аргументы

filename: Путь к файлу скриншота

Source code in pygine/game.py
def screenshot(self, filename: str = "screenshot.png") -> None:
    """
    Сохранить скриншот текущего экрана.

    Аргументы:
        filename: Путь к файлу скриншота
    """
    pygame.image.save(self.screen, filename)

set_background_color(color)

Установить цвет фона.

Аргументы

color: RGB-кортеж цвета (0–255)

Source code in pygine/game.py
def set_background_color(self, color: Tuple[int, int, int]) -> None:
    """
    Установить цвет фона.

    Аргументы:
        color: RGB-кортеж цвета (0–255)
    """
    self.background_color = color
    # Если установлен цвет фона, убираем фоновое изображение
    self.background_surface = None

set_background_image(image_path)

Установить фоновое изображение.

Изображение автоматически масштабируется под размеры окна. Передача None отключает фоновое изображение.

Аргументы

image_path: Путь к файлу изображения или None для отключения

Source code in pygine/game.py
def set_background_image(self, image_path: Optional[str]) -> None:
    """
    Установить фоновое изображение.

    Изображение автоматически масштабируется под размеры окна.
    Передача None отключает фоновое изображение.

    Аргументы:
        image_path: Путь к файлу изображения или None для отключения
    """
    if image_path is None:
        self.background_image_path = None
        self.background_image = None
        self.background_surface = None
    else:
        self.background_image_path = image_path
        self._load_background_image(image_path)

set_fps(fps)

Задать целевую частоту кадров.

Аргументы

fps: Кадров в секунду

Source code in pygine/game.py
def set_fps(self, fps: int) -> None:
    """
    Задать целевую частоту кадров.

    Аргументы:
        fps: Кадров в секунду
    """
    self.fps = max(1, fps)

set_title(title)

Изменить заголовок окна.

Аргументы

title: Новый заголовок окна

Source code in pygine/game.py
def set_title(self, title: str) -> None:
    """
    Изменить заголовок окна.

    Аргументы:
        title: Новый заголовок окна
    """
    self.title = title
    pygame.display.set_caption(title)

toggle_fps_display()

Переключить отображение счётчика FPS.

Source code in pygine/game.py
def toggle_fps_display(self) -> None:
    """Переключить отображение счётчика FPS."""
    self.show_fps = not self.show_fps

toggle_pause()

Переключить состояние паузы игры.

Source code in pygine/game.py
def toggle_pause(self) -> None:
    """Переключить состояние паузы игры."""
    self.paused = not self.paused