Библиотека Pygame / Часть 1. Введение
Это первая часть серии руководств « Разработка игр с помощью Pygame ». Она предназначена для программистов начального и среднего уровней, которые заинтересованы в создании игр и улучшении собственных навыков кодирования на Python.
Что такое Pygame?
Pygame — это «игровая библиотека», набор инструментов, помогающих программистам создавать игры. К ним относятся:
- Графика и анимация
- Звук (включая музыку)
- Управление (мышь, клавиатура, геймпад и так далее)
Игровой цикл
В сердце каждой игры лежит цикл, который принято называть «игровым циклом». Он запускается снова и снова, делая все, чтобы работала игра. Каждый цикл в игре называется кадром.
В каждом кадре происходит масса вещей, но их можно разбить на три категории:
1.Обработка ввода (события)
Речь идет обо всем, что происходит вне игры — тех событиях, на которые она должна реагировать. Это могут быть нажатия клавиш на клавиатуре, клики мышью и так далее.
2.Обновление игры
Изменение всего, что должно измениться в течение одного кадра. Если персонаж в воздухе, гравитация должна потянуть его вниз. Если два объекта встречаются на большой скорости, они должны взорваться.
3.Рендеринг (прорисовка)
В этом шаге все выводится на экран: фоны, персонажи, меню. Все, что игрок должен видеть, появляется на экране в нужном месте.
Время
Еще один важный аспект игрового цикла — скорость его работы. Многие наверняка знакомы с термином FPS, который расшифровывается как Frames Per Second (или кадры в секунду). Он указывает на то, сколько раз цикл должен повториться за одну секунду. Это важно, чтобы игра не была слишком медленной или быстрой. Важно и то, чтобы игра не работала с разной скоростью на разных ПК. Если персонажу необходимо 10 секунд на то, чтобы пересечь экран, эти 10 секунд должны быть неизменными для всех компьютеров.
Создание шаблона Pygame
Теперь, зная из каких элементов состоит игра, можно переходить к процессу написания кода. Начать стоит с создания простейшей программы pygame, которая всего лишь открывает окно и запускает игровой цикл. Это отправная точка для любого проекта pygame.
В начале программы нужно импортировать необходимые библиотеки и задать базовые переменные настроек игры:
# Pygame шаблон - скелет для нового проекта Pygame import pygame import random WIDTH = 360 # ширина игрового окна HEIGHT = 480 # высота игрового окна FPS = 30 # частота кадров в секунду
Дальше необходимо открыть окно игры:
# создаем игру и окно pygame.init() pygame.mixer.init() # для звука screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("My Game") clock = pygame.time.Clock()
pygame.init() — это команда, которая запускает pygame. screen — окно программы, которое создается, когда мы задаем его размер в настройках. Дальше необходимо создать clock , чтобы убедиться, что игра работает с заданной частотой кадров.
Теперь необходимо создать игровой цикл:
# Цикл игры running = True while running: # Ввод процесса (события) # Обновление # Визуализация (сборка)
Игровой цикл — это цикл while , контролируемый переменной running . Если нужно завершить игру, необходимо всего лишь поменять значение running на False . В результате цикл завершится. Теперь можно заполнить каждый раздел базовым кодом.
Раздел рендеринга (отрисовки)
Начнем с раздела отрисовки. Персонажей пока нет, поэтому экран можно заполнить сплошным цветом. Чтобы сделать это, нужно разобраться, как компьютер обрабатывает цвета.
Экраны компьютеров сделаны из пикселей, каждый из которых содержит 3 элемента: красный, зеленый и синий. Цвет пикселя определяется тем, как горит каждый из элементов:
Каждый из трех основных цветов может иметь значение от 0 (выключен) до 255 (включен на 100%), так что для каждого элемента есть 256 вариантов.
Узнать общее количество отображаемых компьютером цветов можно, умножив:
>>> 256 * 256 * 256 16,777,216
Теперь, зная, как работают цвета, можно задать их в начале программ:
# Цвета (R, G, B) BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255)
А после этого — заполнить весь экран.
# Рендеринг screen.fill(BLACK)
Но этого недостаточно. Дисплей компьютера работает не так. Изменить пиксель — значит передать команду видеокарте, чтобы она передала соответствующую команду экрану. По компьютерным меркам это очень медленный процесс. Если нужно нарисовать на экране много всего, это займет много времени. Исправить это можно оригинальным способом, который называется — двойная буферизация. Звучит необычно, но вот что это такое.
Представьте, что у вас есть двусторонняя доска, которую можно поворачивать, показывая то одну, то вторую сторону. Одна будет дисплеем (то, что видит игрок), а вторая — оставаться скрытой, ее сможет «видеть» только компьютер. С каждым кадром рендеринг будет происходить на задней части доски. Когда отрисовка завершается, доска поворачивается и ее содержимое демонстрируется игроку.
А это значит, что процесс отрисовки происходит один раз за кадр, а не при добавлении каждого элемента.
В pygame это происходит автоматически. Нужно всего лишь сказать доске, чтобы она перевернулась, когда отрисовка завершена. Эта команда называется flip() :
# Рендеринг screen.fill(BLACK) # после отрисовки всего, переворачиваем экран pygame.display.flip()
Главное — сделать так, чтобы функция flip() была в конце. Если попытаться отрисовать что-то после поворота, это содержимое не отобразится на экране.
Раздел ввода (событий)
Игры еще нет, поэтому пока сложно сказать, какие кнопки или другие элементы управления понадобятся. Но нужно настроить одно важное событие. Если попытаться запустить программу сейчас, то станет понятно, что нет возможности закрыть окно. Нажать на крестик в верхнем углу недостаточно. Это тоже событие, и необходимо сообщить программе, чтобы она считала его и, соответственно, закрыла игру.
События происходят постоянно. Что, если игрок нажимает кнопку прыжка во время отрисовки? Это нельзя игнорировать, иначе игрок будет разочарован. Для этого pygame сохраняет все события, произошедшие с момента последнего кадра. Даже если игрок будет лупить по кнопкам, вы не пропустите ни одну из них. Создается список, и с помощью цикла for можно пройтись по всем из них.
for event in pygame.event.get(): # проверить закрытие окна if event.type == pygame.QUIT: running = False
В pygame много событий, на которые он способен реагировать. pygame.QUIT — событие, которое стартует после нажатия крестика и передает значение False переменной running , в результате чего игровой цикл заканчивается.
Контроль FPS
Пока что нечего поместить в раздел Update (обновление), но нужно убедиться, что настройка FPS контролирует скорость игры. Это можно сделать следующим образом:
while running: # держим цикл на правильной скорости clock.tick(FPS)
Команда tick() просит pygame определить, сколько занимает цикл, а затем сделать паузу, чтобы цикл (целый кадр) длился нужно время. Если задать значение FPS 30, это значит, что длина одного кадра — 1/30, то есть 0,03 секунды. Если цикл кода (обновление, рендеринг и прочее) занимает 0,01 секунды, тогда pygame сделает паузу на 0,02 секунды.
Итог
Наконец, нужно убедиться, что когда игровой цикл завершается, окно игры закрывается. Для этого нужно поместить функцию pygame.quit() в конце кода. Финальный шаблон pygame будет выглядеть вот так:
# Pygame шаблон - скелет для нового проекта Pygame import pygame import random WIDTH = 360 HEIGHT = 480 FPS = 30 # Задаем цвета WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) # Создаем игру и окно pygame.init() pygame.mixer.init() screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("My Game") clock = pygame.time.Clock() # Цикл игры running = True while running: # Держим цикл на правильной скорости clock.tick(FPS) # Ввод процесса (события) for event in pygame.event.get(): # check for closing window if event.type == pygame.QUIT: running = False # Обновление # Рендеринг screen.fill(BLACK) # После отрисовки всего, переворачиваем экран pygame.display.flip() pygame.quit()
Ура! У вас есть рабочий шаблон Pygame. Сохраните его в файле с понятным названием, например, pygame_template.py , чтобы можно было использовать его каждый раз при создании нового проекта pygame.
В следующем руководстве этот шаблон будет использован как отправная точка для изучения процесса отрисовки объектов на экране и их движения.
Pygame и разработка игр
Pygame – это библиотека модулей для языка Python, созданная для разработки 2D игр. Также Pygame могут называть фреймворком. В программировании понятия «библиотека» и «фреймворк» несколько разные. Но когда дело касается классификации конкретного инструмента, не все так однозначно.
В любом случае, фреймворк является более мощным по-сравнению с библиотекой, он накладывает свою специфику на особенности программирования и сферу использования продукта. С точки зрения специфики Pygame – это фреймворк. Однако его сложно назвать «мощным инструментом». По своему объему и функционалу это скорее библиотека.
Также существует понятие «игрового движка» как программной среды для разработки игр. По своему назначению Pygame можно считать игровым движком. В то же время, с точки зрения классификации программного обеспечения, Pygame является API для Питона к API библиотеки SDL.
API – это интерфейс (в основном набор функций и классов) для прикладного (часто более высокоуровневого) программирования, который предоставляет, например, та или иная библиотека. SDL – это библиотека, которая работает с мультимедийными устройствами компьютера.
В этом смысле Pygame можно сравнить с Tkinter, который через свои функции и классы предоставляет Питону доступ к графической библиотеке Tk.
Официальный сайт: https://www.pygame.org (на август 2023 года сайт недоступен с российских IP)
Особенности разработки компьютерных игр
Игры событийно-ориентированны, также как любое приложение с графическим интерфейсом пользователя. Поэтому какие-никакие, но игры можно было бы писать с помощью Tkinter, в частности на его экземплярах холста. Но поскольку основное назначение библиотеки графического пользовательского интерфейса совсем другое, то пришлось бы изобретать велосипеды. В то время как библиотека, специально предназначенная для написания игр, уже содержит необходимые объекты, что упрощает разработку.
Например, чтобы определить, столкнулись ли два объекта, надо написать код, проверяющий совпадение координат. Это может быть непростой задачей, так как надо учесть области перекрытия, форму объектов и др. В то же время игровой движок может включать готовую функцию проверки коллизии (столкновения) с необходимыми опциями настройки.
При всем этом Pygame достаточно низкоуровневый игровой движок, если его можно так называть. Это значит, что многое в нем не остается за кадром, а дается программисту на доработку, вынуждает его понимать, как работают «шестеренки». Так в Pygame отсутствует эмуляция физических явлений. Если вам надо смоделировать движение с ускорением или по дуге, программируйте это сами, предварительно взяв из курса физики соответствующую формулу.
Игры относятся к мультимедийным приложениям. Однако, в отличие от других приложений этой группы, для них характерна сложная программная логика и нередко много математики, хотя достаточно простой, плюс эмуляция физических явлений. В играх программируется подобие искусственного интеллекта. В многопользовательской игре, хотя пользователи играют друг с другом, а не с ИИ, создаются виртуальные миры, существующие по законам, заложенным разработчиками.
В программном коде игры выделяют три основных логических блока:
- Отслеживание событий, производимых пользователем и не только им.
- Изменение состояний объектов, согласно произошедшим событиям.
- Отображение объектов на экране, согласно их текущим состояниям.
Эти три этапа повторяются в цикле бесчисленное количество раз, пока игра запущена.
Место Pygame среди инструментов разработки игр
Популярна ли библиотека pygame, пишут ли на ней сложные игры? Хотя на Pygame есть востребованные игры, в подавляющем случае – нет. Для программирования под андроид и десктоп существуют более функциональные игровые движки.
Для создания двумерных браузерных игр инди-разработчики (от слова independent – независимый, здесь понимается как «одиночка», «не работающий в команде или на фирму») часто используют JavaScript и его игровые библиотеки, так как JS родной для веба язык. Хотя существуют проекты перевода с Python на JavaScript, а также запуска python-приложений на Android.
В чем тогда преимущество Pygame? Оно в легком обучении и быстрой разработке. Pygame – небольшая библиотека. Сам Python позволяет писать короткий и ясный код. Так что это хорошее начало, чтобы познакомиться с особенностями разработки игр. Более опытными программистами Pygame может использоваться для быстрого создания прототипа игры, чтобы посмотреть, как все будет работать. После этого программа переписывается на другом языке.
После Pygame жизнь разработчика игр на Питоне не заканчивается. Следует посмотреть в сторону Kivy (https://kivy.org). Это уже полноценный фреймворк, позволяющий писать на Python не только игровые приложения. В большей степени ориентирован для разработки под мобильные платформы.
Как установить Pygame
Pygame не входит в стандартную библиотеку Python, то есть не поставляется с установочным пакетом, а требует отдельной установки. В Ubuntu и родственных дистрибутивах его можно установить с помощью pip :
python3 -m pip install -U pygame --user
Если pip не установлен, предварительно выполняем команду:
sudo apt install python3-pip
py -m pip install -U pygame --user
Дополнительную информацию по установке смотрите здесь: https://www.pygame.org/wiki/GettingStarted
Проверить, что все установилось нормально, можно так:
python3 -m pygame.examples.aliens
Для Windows вместо python3 надо писать py . Произойдет запуск игры aliens , включенной в модуль examples (примеры) библиотеки pygame .
Курс с примерами решений практических работ:
pdf-версия
X Скрыть Наверх
Pygame. Введение в разработку игр на Python
Using Generics in Python
If you are using type hints in Python and static analysis tools such as mypy, then you have probably used the Any type quite often to get around typing functions and methods where the type of an argument can vary at runtime. While Any itself is a very convenient tool that enables gradual typing in Python code (the ability to add type hints to your code little by little over time), it doesn’t add any real value in terms of portraying knowledge to the reader. This is where generics come in. In this article I go over the basics of generics and how they can be used in Python to better document and communicate the intentions of your code, and in some cases even enforce correctness.
Why use type hints?
Before we start we should cover why we would use type hints in Python at all.
If you come from a background of dynamic languages, or maybe you are just starting out coding and have only worked on smaller projects, you may wonder what the value of adding type hints to Python code is. Isn’t the point of Python to be dynamic, rely on duck typing and enable the engineer to create something quickly without barriers? Well, yes, it is. But, if you work on a large codebase, or with other engineers, or even on a pet project that you only work on occasionally, you’ll find that the more information you can add to the code the easier it is to work on.
I’ve worked on some pretty large Python monoliths in the past where the only way to actually understand what was going on was to either get it running on your machine (often harder than it sounds) or to write a test that fires the code you want to understand, throw in some debugs and start stepping through it. Type hints add value as they give you more information about what is happening in your code so you can better reason about it without extra effort. These type hints also enable you to use tools like mypy that can check for errors that might have only been seen at runtime, as the type hints add a contract to your code that these tools can verify.
Type hints increase readability, code quality and allow you to find bugs and issues before your customers do.
What are generics?
The aim of generics are to:
- Allow functions, methods and classes to work with arguments of any type whilst maintaining the information on the relationships between things, such as arguments and return values.
- Better define how types can mix
We achieve these points by using generic types instead of concrete or parent types when defining what type is to be used in a given situation.
That may or may not have made any sense. The best way to explain this properly is with some simple examples. The next section will guide us through some examples of generics and how to use them in our Python code.
Using generics in Python
All of the following examples are written using Python 3.8 and therefore use the typing module.
Let’s say we have a function, and that function takes a list of things, and returns the first thing in the list.
The first() function defined here will work with a list of any type, even a list of mixed types. But, what happens when we decide to better define our lists? If we decide to limit the types of the elements in these lists, by adding type hints to our code and using mypy to enforce those types, you may end up with something like this;
This is a common sight when adding type hints. We better define the containers but we have just used Any on the first() function itself. This doesn’t add any value! In this very basic example, where we can see what is happening in the code at a glance, this isn’t too bad. However, even with this example, let alone a larger real world example working with user defined types, these type hints don’t capture something that we know to be true. We know that our method will be used with lists of a single type. We also know that the return type will match the type of the items in the list, so let’s capture these things in the code.
Here we have added a generic type named T . We did this by using the TypeVar factory, giving the name of the type we want to create and then capturing that in a variable named T . This is then used the same way as any other type is used in Python type hints. T and U are commonly used names in generics (T standing for Type and U standing for…. nothing. It’s just the next letter in the alphabet) similar to how i and x are used as iteration variables.
Using this generic type T , the first() function now states that the container parameter is a list of a “generic type”. We don’t care about the actual type of the argument, but we do care that the return value is the same type as the items in the list. Using this we capture the relationship between the argument and return value in code. It has the added bonus of allowing mypy to detect if we try to return something that is not from the container argument. Let’s see what happens if we return a value of the same type as the contents of container , but is not actually from container …
In the above example even though the only container argument passed to the function has elements of type str , and we return a str , mypy raises an “Incompatible return value type” error, as it was expecting a return value of generic type T .We only define T as the content type for the container parameter in this function, so the return value must come from the container.
Using generics in the first() function was a small change, but we now better communicate to the reader the relationship between the argument and the return value, and use that information to allow static analysis tools to check our code is correct.
Let’s use a few more simple examples to demonstrate everything we have just learnt to show us how useful this is. In these examples we use K and V as our generic types, as they represent the types of the keys and values of a dictionary.
Here, get_item() doesn’t care what types the keys of the dictionary are, or what type the values of the dictionary are, but it captures that the key argument we send, has to be of the same type as the keys in the container argument we send, and that the return value will be a dictionary value and not a dictionary key. We get all of this information just by looking at the signature of the function, and again this can be tested for correctness.
Let’s look at one final example:
Above, we use a poor name for our function but the generic types still explain it’s intent of returning the first key, not the first value. If we then switch the implementation of this to return the first value…
mypy raises the same “incompatible return value type” as we saw before, explaining what we have done wrong.
In these simple examples we have used generic types that can represent any type. However you can limit the types that can be represented by a generic type in Python. This can be done by listing the types that are allowed
Or by setting an “upper bound” type
This then limits what this generic type can represent to the upper bound type and subtypes of the given type, in this case int and it’s subtype bool .
Generic Types
Generics are not just used for function and method parameters. They can also be used to define classes that can contain, or work with, multiple types. These “generic types” allow us to state what type, or types, we want to work with for each instance when we instantiate the class.
Let’s look back at this earlier listing as an example:
Here we updated our code to use fixed type lists. We used the construct List[str] and List[int] to define that the list will contain only string and integer values respectively. This works because List is a generic type. When we instantiate an instance of a list, we tell it what type its values will be. If we did not define a type on instantiation, then it assumes Any . That means that my_list: List = [‘a’] and my_list: List[Any] = [‘a’] are the same.
Most container types in the Typing module are generic, as they allow you to define what type the contents of the container will be on instantiation. In the case of Dict we can state the type of the key and value. Callable is another example of a generic type, as it allows us to define the types of the parameters as well as the return type.
To better understand the concept of generic types, let’s look at building one of our own
User defined generic types
In the following example we have made a registry class. The type of the contents of the registry is generic. This type is stated when we instantiate this class. After instantiation that instance will only accept arguments of that type.
Here we have created the generic class Registry . This is done by extending the Generic base class, and by defining the generic types we want to be valid within this class. In this case we define T (line 5) which is then used within methods of the Registry class.
When we instantiate family_name_reg , we state that it will only hold values of type string (by using Registry[str]) , and the family_age_reg instance will only hold values of type integer (by using Registry[int] ).
Generics have allowed us to create a class that can be used with multiple types, and then enforce (through the use of tools such as mypy) that only arguments of the specified type are sent to the methods of the instance.
Using our example above, if we try to set a string value in the age registry, we can see that mypy will raise this as an error
Generics are very powerful and help you to better reason about what you are doing and why. They also communicate that information to others, or your future self, that are reading your code and add a contract that static analysis tools can use to check your code is correct. In Python we don’t need to use them, but only in the same way we don’t need to add type hints. Much for the same reasons why we try to make code readable by writing small functions and using naming conventions that are meaningful, using type hints and generics just makes things easier to reason about, which means less time trying to understand what is going on and more time progressing your project.
In this article we have covered what generics are, why using them is a good thing and how to use them in Python. The examples were brief and simple, but I hope they portrayed how much value using generics adds to your codebase, empowers you to dive deeper into the subject and to start using them in your code!
Python 3.12.0
![]()
Python is a dynamic object-oriented programming language that can be used for many kinds of software development. It offers strong support for integration with other languages and tools, comes with extensive standard libraries, and can be learned in a few days. Many Python programmers report substantial productivity gains and feel the language encourages the development of higher quality, more maintainable code.
Overview
Python is a Open Source software in the category Development developed by Python Software Foundation.
It was checked for updates 15,425 times by the users of our client application UpdateStar during the last month.
The latest version of Python is 3.12.0, released on 10/04/2023. It was initially added to our database on 08/24/2007.
Python runs on the following operating systems: Android/Windows/Mac. The download file has a size of 25.3MB.
Users of Python gave it a rating of 4 out of 5 stars.
FAQ
What is Python?
Python is a high-level, interpreted programming language that emphasizes code readability and ease of use. It is utilized in a broad range of applications, including web development, scientific computing, data analysis, artificial intelligence, and more. Python is considered to be an excellent first language for beginners, but it is also used by seasoned professionals.
Who developed Python?
Python was created in the late 1980s by Guido van Rossum, a Dutch programmer. Today, it is maintained by the Python Software Foundation, a non-profit organization dedicated to supporting and promoting Python.
Is Python free?
Yes, Python is free and open-source software. This means that anyone can download and use it for free, and the source code for Python is available for anyone to inspect and modify.
What platforms does Python run on?
Python runs on many operating systems, including Windows, macOS, Linux, and Unix. It can also run on mobile devices using specialized versions of Python like Kivy or Pygame.
What are the basic data types in Python?
Some of the basic data types in Python include integers, floating-point numbers (decimal numbers), strings (text), lists (ordered collections of elements), tuples (ordered and immutable collections of elements), and dictionaries (unordered collections of key-value pairs).
What are Python modules?
Python modules are files that contain Python code, typically organized around a particular functionality or set of related functionalities. Modules can be imported and used in other Python programs to provide functionality without needing to rewrite or copy the entire code.
Can Python be used for web development?
Yes, Python has several frameworks that make it well-suited for web development, including Django, Flask, Pyramid, and Bottle. These frameworks provide developers with tools and libraries to build web applications more efficiently.
Is Python suitable for data analysis and scientific computing?
Yes, Python has a number of libraries specifically designed for data analysis and scientific computing, including NumPy, Pandas, SciPy, and Matplotlib. Many data scientists and researchers use Python as their primary language for exploring data and building models.
Is it easy to learn Python?
Yes, Python is often cited as one of the easiest programming languages to learn. Its syntax is straightforward and employs a natural language approach, making it easy to read and write. Additionally, it has an active community that provides resources and support to learners of all levels.
Are there any downsides to using Python?
One potential downside of using Python is its performance; as an interpreted language, it can be slower than compiled languages like C or C++. However, there are ways to maximize Python’s performance, such as using specialized tools and libraries or writing specific performance-optimized code.
