Создание интерактивных меню в Python-ботах: пошаговое руководство
Для кого эта статья:
- Разработчики, изучающие создание чат-ботов на Python
- Студенты, проходящие курсы программирования и желающие улучшить навыки в разработке ботов
Профессионалы, стремящиеся повысить юзабилити и функциональность своих программных решений
Создание чат-бота без продуманного интерфейса — всё равно что построить дом без дверей. Пользователи быстро запутаются и уйдут, не разобравшись в командах. Интерактивные меню решают эту проблему, превращая взаимодействие с ботом в интуитивно понятный процесс. В этом руководстве вы научитесь создавать удобные навигационные системы для ваших Python-ботов, от простых кнопок до многоуровневых меню с обработкой колбэков. Готовы превратить своего бота из примитивного текстового интерфейса в полноценное интерактивное приложение? 🚀
Хотите быстро освоить создание ботов с интерактивными меню и другие востребованные навыки Python-разработчика? Обучение Python-разработке от Skypro поможет вам перейти от теории к реальным проектам за 9 месяцев. Наши студенты создают функциональных ботов с продвинутыми интерфейсами уже на втором месяце обучения, а к выпуску формируют портфолио из 5+ проектов, с которыми успешно трудоустраиваются.
Интерактивное меню в Python-ботах: основы и преимущества
Интерактивное меню в боте — это система навигации, позволяющая пользователю взаимодействовать с ботом через предопределенные элементы интерфейса вместо ввода текстовых команд. По сути, это кнопки, которые пользователь может нажимать для выполнения действий или перехода между разделами бота.
Когда я только начинал создавать своего первого бота для управления проектами, пользователи постоянно жаловались на необходимость запоминать команды. Внедрение интерактивного меню увеличило удержание на 67% и сократило количество ошибок пользователей почти в 3 раза.
Александр Соколов, Python-разработчик
Мой первый бот для Telegram был простым конвертером валют. Пользователи писали команду типа "/convert 100 USD to EUR", и бот выдавал результат. Звучит просто, но на практике 80% сообщений содержали ошибки: опечатки в названиях валют, неправильный формат запроса, забытые пробелы. Логи были заполнены сообщениями об ошибках.
После внедрения интерактивного меню с кнопками выбора валют и суммы ситуация кардинально изменилась. Количество успешных конвертаций выросло с 20% до 95%. Пользователи просто выбирали из списка валюту, вводили сумму и нажимали кнопку "Конвертировать". Бот стали использовать в 3 раза чаще, а количество жалоб сократилось почти до нуля.
Давайте рассмотрим основные преимущества интерактивных меню в ботах:
- Повышение юзабилити — пользователи видят доступные опции, что снижает когнитивную нагрузку
- Снижение ошибок — структурированный ввод минимизирует возможность опечаток или неверных команд
- Направленное взаимодействие — вы контролируете пользовательский сценарий, предлагая только релевантные опции
- Визуальная привлекательность — кнопки и меню делают интерфейс более современным и привычным для пользователей
- Статистика использования — легче отслеживать, какие функции бота используются чаще
| Тип взаимодействия | Текстовые команды | Интерактивное меню |
|---|---|---|
| Скорость освоения | Низкая (требуется запоминание команд) | Высокая (интуитивно понятный интерфейс) |
| Процент ошибок пользователя | 30-40% | 5-10% |
| Удовлетворенность пользователей | Средняя | Высокая |
| Сложность реализации | Низкая | Средняя |
Современные библиотеки для создания ботов, такие как aiogram и pyTelegramBotAPI, предоставляют разработчикам мощные инструменты для создания интерактивных меню. Вы можете использовать два основных типа клавиатур:
- Reply-клавиатуры — физически отображаются под полем ввода сообщения
- Inline-клавиатуры — отображаются прямо в сообщении бота и позволяют создавать более сложные интерфейсы
Теперь, когда мы понимаем ценность интерактивных меню, давайте разберемся, как настроить проект для их создания. 🛠️

Настройка проекта и выбор библиотек для создания бота
Перед тем как приступить к созданию интерактивного меню, необходимо настроить проект и выбрать подходящую библиотеку. В экосистеме Python существует несколько популярных библиотек для разработки ботов, каждая со своими преимуществами.
Дмитрий Волков, Lead Python Developer
Однажды наша команда получила заказ на разработку бота-помощника для крупной компании. Требования были стандартными: отвечать на часто задаваемые вопросы, предоставлять информацию о продуктах и обрабатывать простые запросы. Мы начали разработку с использованием pyTelegramBotAPI, так как это казалось самым простым решением.
Однако, когда клиент начал добавлять новые требования — многоуровневые меню, более сложные сценарии взаимодействия, асинхронную обработку запросов — мы столкнулись с ограничениями выбранной библиотеки. Перепись проекта на aiogram заняла почти неделю, но результат стоил усилий. Производительность бота выросла на 40%, а код стал гораздо чище и поддерживаемым.
Главный урок, который мы извлекли: выбирайте библиотеку не только для текущих задач, но и с учетом потенциального масштабирования проекта.
Давайте сравним наиболее популярные библиотеки для создания ботов в Python:
| Библиотека | Преимущества | Недостатки | Подходит для |
|---|---|---|---|
| aiogram (3.x) | Асинхронная, масштабируемая, современный API | Более сложная для начинающих | Сложных ботов с высокой нагрузкой |
| pyTelegramBotAPI | Простота использования, понятная документация | Ограниченная масштабируемость | Простых ботов и быстрых прототипов |
| python-telegram-bot | Полная имплементация Telegram Bot API | Более обширный API требует изучения | Средних и сложных проектов |
| discord.py | Отлично подходит для Discord-ботов | Только для платформы Discord | Сообществ и игровых серверов Discord |
Для нашего руководства я выбрал aiogram, так как он предоставляет наиболее гибкие инструменты для создания интерактивных меню и хорошо масштабируется для сложных проектов.
Вот базовые шаги для настройки проекта с aiogram:
- Создайте виртуальное окружение Python:
python -m venv venv
source venv/bin/activate # На Windows: venv\Scripts\activate
- Установите необходимые пакеты:
pip install aiogram==3.0.0b7
- Создайте базовую структуру проекта:
my_bot/
├── bot.py # Основной файл бота
├── config.py # Конфигурация (токены, настройки)
├── keyboards.py # Модуль для клавиатур и меню
└── handlers.py # Обработчики сообщений и колбэков
Теперь давайте создадим базовый код для нашего бота в файле bot.py:
from aiogram import Bot, Dispatcher, types
from aiogram.filters import CommandStart
import asyncio
import logging
from config import BOT_TOKEN
# Настраиваем логирование
logging.basicConfig(level=logging.INFO)
# Инициализируем бота и диспетчер
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher()
# Обработчик команды /start
@dp.message(CommandStart())
async def cmd_start(message: types.Message):
await message.answer(
"Привет! Я бот с интерактивным меню.",
reply_markup=types.ReplyKeyboardRemove()
)
# Запуск бота
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
А в файле config.py необходимо создать переменную с токеном:
BOT_TOKEN = "ваш_токен_от_BotFather"
Это базовая настройка проекта. Теперь мы готовы перейти к созданию структуры интерактивного меню. 🧩
Структура интерактивного меню и типы кнопок в aiogram
Aiogram предлагает два основных типа клавиатур для создания интерактивных меню: reply-клавиатуры и inline-клавиатуры. Каждый тип имеет свои особенности и сценарии использования. Давайте разберем их подробнее и научимся создавать различные структуры меню.
Reply-клавиатуры отображаются под полем ввода сообщения и заменяют стандартную клавиатуру. Они идеальны для основного меню и частых действий. В aiogram 3.x создание reply-клавиатуры выглядит так:
from aiogram import types
from aiogram.utils.keyboard import ReplyKeyboardBuilder
def get_main_menu():
builder = ReplyKeyboardBuilder()
# Добавляем кнопки в клавиатуру
builder.row(
types.KeyboardButton(text="📊 Статистика"),
types.KeyboardButton(text="⚙️ Настройки")
)
builder.row(
types.KeyboardButton(text="❓ Помощь"),
types.KeyboardButton(text="👤 Профиль")
)
# Формируем клавиатуру с параметрами
return builder.as_markup(
resize_keyboard=True, # Адаптивный размер
one_time_keyboard=False # Клавиатура не скрывается после нажатия
)
# Использование клавиатуры в обработчике
@dp.message(CommandStart())
async def cmd_start(message: types.Message):
await message.answer(
"Выберите действие:",
reply_markup=get_main_menu()
)
Inline-клавиатуры встраиваются непосредственно в сообщение и позволяют создавать более сложные интерфейсы. Они идеально подходят для многоуровневых меню, пагинации и действий, требующих дополнительных данных:
from aiogram import types
from aiogram.utils.keyboard import InlineKeyboardBuilder
def get_products_menu():
builder = InlineKeyboardBuilder()
# Добавляем кнопки с callback_data
builder.row(
types.InlineKeyboardButton(
text="🍕 Пицца",
callback_data="category:pizza"
),
types.InlineKeyboardButton(
text="🍔 Бургеры",
callback_data="category:burgers"
)
)
builder.row(
types.InlineKeyboardButton(
text="🥤 Напитки",
callback_data="category:drinks"
),
types.InlineKeyboardButton(
text="🍰 Десерты",
callback_data="category:desserts"
)
)
builder.row(
types.InlineKeyboardButton(
text="🔙 Назад",
callback_data="back:main"
)
)
return builder.as_markup()
Кроме стандартных кнопок, inline-клавиатура поддерживает несколько специальных типов:
- URL-кнопки — открывают указанную ссылку
- Callback-кнопки — отправляют данные боту для обработки
- Web App кнопки — открывают веб-приложения внутри Telegram
- Login кнопки — предоставляют аутентификацию через Telegram
- Switch Inline кнопки — переключают бота в режим inline-запросов
Для создания более сложных структур меню, таких как сетки или столбцы, можно использовать различные методы построения клавиатуры:
def get_grid_menu():
builder = InlineKeyboardBuilder()
# Создаем сетку 3x3 с цифрами
buttons = [
types.InlineKeyboardButton(
text=str(i),
callback_data=f"number:{i}"
) for i in range(1, 10)
]
# Добавляем кнопки сеткой 3x3
builder.add(*buttons)
builder.adjust(3) # 3 кнопки в строке
return builder.as_markup()
При создании структуры меню важно продумать навигацию. Хорошей практикой является создание кнопок "Назад" и "Главное меню" для многоуровневых структур:
def get_submenu(category_id):
builder = InlineKeyboardBuilder()
# Динамическое создание кнопок на основе категории
if category_id == "pizza":
items = ["Маргарита", "Пепперони", "Гавайская", "Четыре сыра"]
elif category_id == "drinks":
items = ["Кола", "Спрайт", "Сок", "Вода"]
else:
items = ["Товар 1", "Товар 2", "Товар 3"]
# Добавляем кнопки с товарами
for item in items:
builder.row(
types.InlineKeyboardButton(
text=item,
callback_data=f"product:{category_id}:{item}"
)
)
# Навигационные кнопки
builder.row(
types.InlineKeyboardButton(
text="🔙 Назад к категориям",
callback_data="back:categories"
),
types.InlineKeyboardButton(
text="🏠 Главное меню",
callback_data="menu:main"
)
)
return builder.as_markup()
Структура меню должна быть интуитивно понятной и соответствовать ментальной модели пользователя. При разработке сложных ботов полезно сначала нарисовать карту интерфейса, чтобы визуализировать все переходы между экранами. 📱
Реализация inline keyboard и обработка колбэков в Python
Одним из ключевых аспектов создания интерактивного меню является правильная обработка колбэков (callback queries) — данных, отправляемых боту при нажатии на кнопки inline-клавиатуры. В этом разделе мы рассмотрим, как эффективно организовать этот процесс.
В aiogram 3.x обработка колбэков происходит через регистрацию обработчиков с соответствующими фильтрами. Рекомендуется использовать структурированный формат callback_data для упрощения обработки:
from aiogram import Router, F
from aiogram.types import CallbackQuery
router = Router()
# Обработчик колбэков категорий
@router.callback_query(F.data.startswith("category:"))
async def process_category_selection(callback: CallbackQuery):
# Извлекаем идентификатор категории из callback_data
category_id = callback.data.split(":")[1]
# Отвечаем пользователю и обновляем клавиатуру
await callback.message.edit_text(
f"Вы выбрали категорию: {category_id}",
reply_markup=get_submenu(category_id)
)
# Обязательно отвечаем на колбэк, чтобы убрать индикатор загрузки
await callback.answer()
# Обработчик колбэков навигации
@router.callback_query(F.data.startswith("back:"))
async def process_back_button(callback: CallbackQuery):
destination = callback.data.split(":")[1]
if destination == "main":
await callback.message.edit_text(
"Главное меню:",
reply_markup=get_main_menu_inline()
)
elif destination == "categories":
await callback.message.edit_text(
"Выберите категорию:",
reply_markup=get_products_menu()
)
await callback.answer()
Для более сложной обработки колбэков можно использовать фабричный подход с использованием CallbackData. В aiogram 3.x для этого есть специальный класс:
from aiogram.filters.callback_data import CallbackData
class ProductCallback(CallbackData, prefix="product"):
category_id: str
product_id: str
action: str = "view" # Действие по умолчанию
# Создание кнопки с использованием фабрики
button = types.InlineKeyboardButton(
text="Пепперони",
callback_data=ProductCallback(
category_id="pizza",
product_id="pepperoni"
).pack()
)
# Обработчик с использованием фабрики
@router.callback_query(ProductCallback.filter())
async def process_product_action(
callback: CallbackQuery,
callback_data: ProductCallback
):
# Извлекаем данные из структурированного объекта
category = callback_data.category_id
product = callback_data.product_id
action = callback_data.action
if action == "view":
# Показываем информацию о товаре
await callback.message.edit_text(
f"Товар: {product} из категории {category}\n\n"
f"Здесь будет описание товара...",
reply_markup=get_product_menu(category, product)
)
elif action == "add":
# Добавляем товар в корзину
await callback.answer(f"Товар {product} добавлен в корзину!", show_alert=True)
await callback.answer()
При работе с колбэками важно помнить о некоторых ограничениях:
- Размер callback_data — Telegram ограничивает размер callback_data до 64 байт
- Время ответа — на колбэк необходимо ответить в течение 30 секунд
- Частота обновлений — существуют ограничения на частоту обновления сообщений
Вот примеры различных паттернов взаимодействия с inline-клавиатурой:
| Паттерн взаимодействия | Описание | Пример использования |
|---|---|---|
| Изменение текущего меню | Обновление текста и клавиатуры текущего сообщения | Навигация по категориям |
| Пагинация | Переключение между страницами результатов | Список товаров, новостная лента |
| Одноразовые уведомления | Всплывающие сообщения без изменения интерфейса | Подтверждение действий, ошибки |
| Многошаговые формы | Последовательный сбор информации от пользователя | Регистрация, заказ товаров |
Для обработки колбэков с пагинацией можно использовать следующий подход:
class PaginationCallback(CallbackData, prefix="page"):
action: str # next, prev, current
page: int
def get_paginated_menu(items, page=0, items_per_page=5):
builder = InlineKeyboardBuilder()
# Определяем начальный и конечный индексы для текущей страницы
start_idx = page * items_per_page
end_idx = min(start_idx + items_per_page, len(items))
# Добавляем кнопки для текущих элементов
for item in items[start_idx:end_idx]:
builder.row(
types.InlineKeyboardButton(
text=item,
callback_data=f"select:{item}"
)
)
# Кнопки навигации
navigation = []
# Кнопка "Назад", если не первая страница
if page > 0:
navigation.append(
types.InlineKeyboardButton(
text="◀️ Назад",
callback_data=PaginationCallback(
action="prev",
page=page-1
).pack()
)
)
# Информация о текущей странице
navigation.append(
types.InlineKeyboardButton(
text=f"{page+1}/{(len(items)+items_per_page-1)//items_per_page}",
callback_data=PaginationCallback(
action="current",
page=page
).pack()
)
)
# Кнопка "Вперед", если не последняя страница
if end_idx < len(items):
navigation.append(
types.InlineKeyboardButton(
text="Вперед ▶️",
callback_data=PaginationCallback(
action="next",
page=page+1
).pack()
)
)
builder.row(*navigation)
return builder.as_markup()
@router.callback_query(PaginationCallback.filter())
async def process_pagination(callback: CallbackQuery, callback_data: PaginationCallback):
# Здесь items должны быть доступны или извлечены из БД/состояния
items = ["Товар 1", "Товар 2", "Товар 3", "Товар 4", "Товар 5",
"Товар 6", "Товар 7", "Товар 8", "Товар 9", "Товар 10"]
action = callback_data.action
current_page = callback_data.page
if action == "current":
# Просто обновляем сообщение колбэка
await callback.answer(f"Текущая страница: {current_page+1}")
return
# Обновляем меню с новой страницей
await callback.message.edit_reply_markup(
reply_markup=get_paginated_menu(items, page=current_page)
)
await callback.answer()
Такая организация обработки колбэков позволяет создавать сложные интерактивные интерфейсы, поддерживая при этом читаемость и поддерживаемость кода. 🧠
Расширение функционала: многоуровневые меню и диспетчеры
Для создания по-настоящему мощных ботов с разветвленной навигацией необходимо выйти за рамки простых меню и реализовать многоуровневую структуру с грамотным управлением состоянием. В этом разделе мы рассмотрим продвинутые техники организации сложных меню и использование диспетчеров для управления навигацией.
Одна из ключевых концепций при построении сложных меню — FSM (Finite State Machine, или конечный автомат). В aiogram 3.x для этого используется встроенная система состояний:
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.context import FSMContext
# Определяем состояния для нашего меню
class MenuStates(StatesGroup):
main = State() # Главное меню
catalog = State() # Каталог товаров
category = State() # Выбранная категория
product = State() # Просмотр товара
cart = State() # Корзина
checkout = State() # Оформление заказа
# Обработчик перехода в главное меню
@router.message(Command("menu"))
@router.callback_query(F.data == "menu:main")
async def show_main_menu(event: Union[Message, CallbackQuery], state: FSMContext):
# Устанавливаем состояние
await state.set_state(MenuStates.main)
# Сохраняем дополнительные данные в состоянии
await state.update_data(last_visited=datetime.now().isoformat())
# Отправляем меню в зависимости от типа события
if isinstance(event, CallbackQuery):
await event.message.edit_text(
"🏠 Главное меню",
reply_markup=get_main_menu_inline()
)
await event.answer()
else: # Message
await event.answer(
"🏠 Главное меню",
reply_markup=get_main_menu_inline()
)
# Обработчик выбора категории
@router.callback_query(MenuStates.main, F.data.startswith("category:"))
async def show_category(callback: CallbackQuery, state: FSMContext):
category_id = callback.data.split(":")[1]
# Устанавливаем состояние категории
await state.set_state(MenuStates.category)
# Сохраняем данные категории в состоянии
await state.update_data(current_category=category_id)
# Загружаем продукты для категории (здесь могла бы быть загрузка из БД)
products = get_products_for_category(category_id)
await callback.message.edit_text(
f"Категория: {category_id.capitalize()}\n"
f"Доступно товаров: {len(products)}",
reply_markup=get_category_menu(category_id, products)
)
await callback.answer()
Для более сложных сценариев и многоуровневых меню полезно реализовать диспетчер меню, который централизованно управляет навигацией:
class MenuDispatcher:
def __init__(self):
self.menus = {}
self.default_menu = None
def register_menu(self, menu_id, get_content_func, get_markup_func):
"""Регистрирует новое меню"""
self.menus[menu_id] = {
'get_content': get_content_func,
'get_markup': get_markup_func
}
def set_default(self, menu_id):
"""Устанавливает меню по умолчанию"""
if menu_id in self.menus:
self.default_menu = menu_id
async def show_menu(self, menu_id, event, state=None, **kwargs):
"""Показывает указанное меню"""
if menu_id not in self.menus:
if self.default_menu:
menu_id = self.default_menu
else:
raise ValueError(f"Menu {menu_id} not found and no default menu set")
menu = self.menus[menu_id]
content = await menu['get_content'](**kwargs)
markup = await menu['get_markup'](**kwargs)
# Сохраняем текущее меню в состоянии, если оно предоставлено
if state:
await state.update_data(current_menu=menu_id, menu_params=kwargs)
# Показываем меню в зависимости от типа события
if isinstance(event, CallbackQuery):
await event.message.edit_text(content, reply_markup=markup)
await event.answer()
else: # Message
await event.answer(content, reply_markup=markup)
return menu_id
# Использование диспетчера меню
menu_dispatcher = MenuDispatcher()
# Регистрация меню
menu_dispatcher.register_menu(
'main',
lambda **kw: "🏠 Главное меню",
lambda **kw: get_main_menu_inline()
)
menu_dispatcher.register_menu(
'category',
lambda category_id, **kw: f"Категория: {category_id.capitalize()}",
lambda category_id, **kw: get_category_menu(category_id)
)
menu_dispatcher.set_default('main')
# Обработчик для использования диспетчера
@router.callback_query(F.data.startswith("menu:"))
async def process_menu_navigation(callback: CallbackQuery, state: FSMContext):
menu_id = callback.data.split(":")[1]
# Извлекаем параметры из callback_data или состояния
params = {}
if len(callback.data.split(":")) > 2:
# Парсим дополнительные параметры из callback_data
params_str = callback.data.split(":", 2)[2]
for param in params_str.split(","):
if "=" in param:
key, value = param.split("=")
params[key] = value
# Если параметров нет в callback_data, пробуем взять из состояния
if not params:
data = await state.get_data()
params = data.get("menu_params", {})
# Показываем меню через диспетчер
await menu_dispatcher.show_menu(menu_id, callback, state, **params)
Для удобной организации сложных интерфейсов можно создать систему "слоев" меню:
- Основное меню — первый уровень навигации (Каталог, Корзина, Профиль)
- Контекстное меню — зависит от текущего контекста (например, действия с товаром)
- Динамическое меню — генерируется на основе данных (список товаров категории)
- Сервисное меню — доступно всегда (Помощь, Назад, Главное меню)
При создании многоуровневых меню важно следить за UX и предусматривать возможность быстрой навигации. Вот некоторые рекомендации:
| UX-рекомендация | Реализация |
|---|---|
| Возможность вернуться на шаг назад | Кнопка "Назад" с сохранением истории навигации в состоянии |
| Быстрый доступ к главному меню | Постоянная кнопка "Главное меню" в нижней части клавиатуры |
| Визуальная иерархия | Группировка кнопок по функциональности, использование эмодзи для визуальных акцентов |
| Индикация текущего положения | Заголовки с указанием текущего раздела, хлебные крошки для глубоких структур |
| Ограничение глубины меню | Не более 3-4 уровней вложенности для предотвращения потери контекста |
Для трекинга поведения пользователей в сложных меню можно реализовать аналитику навигации:
async def track_menu_navigation(user_id, from_menu, to_menu, context=None):
"""Записывает переход между меню для аналитики"""
logging.info(f"Navigation: {user_id} moved from {from_menu} to {to_menu}")
# Здесь может быть код для записи в БД или отправки в аналитическую систему
# Пример структуры события навигации
navigation_event = {
"user_id": user_id,
"timestamp": datetime.now().isoformat(),
"from_menu": from_menu,
"to_menu": to_menu,
"context": context or {},
"session_id": generate_session_id(user_id)
}
# Сохранение события (например, в MongoDB)
# await navigation_collection.insert_one(navigation_event)
Использование этих техник позволит вам создавать сложные и гибкие интерфейсы для ботов, которые останутся понятными для пользователей и поддерживаемыми для разработчиков. 🎮
Работа с интерактивными меню в Python-ботах — это не просто набор технических приемов, а целое искусство создания удобных интерфейсов. Изучив основы и продвинутые техники из этого руководства, вы сможете создавать ботов, которые будут по-настоящему полезны вашей аудитории. Помните, что каждая кнопка в меню — это возможность направить пользователя к нужной информации или действию без лишних слов и объяснений. Именно в этом и заключается главная ценность хорошо спроектированного интерактивного меню.
Читайте также
- Визуальное программирование: как создавать код без написания
- Разработка приложений без кода: как реализовать идею без программирования
- Игры в презентациях: как увеличить запоминаемость на 70%
- PHP разработка: создание первого приложения от основ до публикации
- Топ-5 инструментов для разработки приложений: выбор технологий
- Веб, мобильные или десктопные приложения: что выбрать для проекта
- ТОП-10 программ для разработки Windows-приложений: обзор и выбор
- Создание визуальной новеллы в Godot: полное руководство для начинающих
- От идеи до релиза: полное руководство по разработке программы
- 5 простых 3D-движков для новичков: от GameMaker до Unity