PhyreEngine от Sony: создание профессиональных игр с нуля

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Разработчики игр, интересующиеся использованием PhyreEngine
  • Новички в геймдеве, стремящиеся освоить игровые движки с нуля
  • Профессионалы, ищущие оптимизацию и высокую производительность для проектов на PlayStation

    PhyreEngine — один из тех игровых движков, которые незаслуженно остаются в тени гигантов вроде Unity и Unreal. А зря. Этот мощный инструмент от Sony Interactive Entertainment предоставляет разработчикам исключительные возможности для создания высококачественных игр с впечатляющей графикой и оптимизированной производительностью. В этой статье я проведу вас от абсолютных основ до создания полноценных игровых проектов на PhyreEngine, раскрыв секреты, которые превратят вас из новичка в уверенного разработчика. 🎮

Хотите создавать игры, но не знаете, с чего начать? Python — идеальный язык для старта в геймдеве! Изучив основы на курсе Обучение Python-разработке от Skypro, вы получите прочный фундамент для дальнейшего погружения в игровые движки, включая PhyreEngine. Python не только прост в освоении, но и широко используется в игровой индустрии для прототипирования, написания серверной логики и инструментов разработки. Начните с малого, чтобы достичь большего!

Что такое PhyreEngine: особенности и преимущества

PhyreEngine — это многоплатформенный игровой движок, разработанный Sony Interactive Entertainment для разработки игр на консолях PlayStation, а также на ПК. Созданный изначально для внутреннего использования, сегодня он доступен зарегистрированным разработчикам PlayStation и некоторым сторонним студиям.

Ключевое преимущество PhyreEngine — его оптимизация для работы с оборудованием PlayStation, что обеспечивает высочайшую производительность при минимальных усилиях со стороны разработчика. Графический конвейер движка спроектирован с учетом архитектуры консолей Sony, что позволяет извлекать максимум из их аппаратных возможностей.

Алексей Сергеев, технический директор игровой студии

Наша студия раньше использовала исключительно Unreal Engine, пока не столкнулась с проектом, требующим особой оптимизации для PlayStation 5. Переход на PhyreEngine дался непросто — документации меньше, сообщество разработчиков не такое большое. Но результат превзошел ожидания. Игра получила стабильные 60 FPS с полноценным рейтрейсингом, при этом мы сэкономили около 15% ресурсов процессора по сравнению с нашими предыдущими проектами. Критичным фактором стал прямой доступ к технической поддержке Sony, которая помогала решать сложные проблемы оптимизации в кратчайшие сроки.

Вот основные характеристики, делающие PhyreEngine привлекательным для разработчиков:

  • Высокопроизводительный графический движок с поддержкой передовых технологий освещения и рендеринга
  • Встроенная поддержка физики и систем частиц
  • Полная интеграция с инструментами разработки PlayStation
  • Оптимизированный конвейер ассетов
  • Мощная система анимации персонажей
  • Поддержка новейших технологий, включая рейтрейсинг и переменную частоту кадров
Характеристика PhyreEngine Unity Unreal Engine
Оптимизация для PlayStation Превосходная Хорошая Очень хорошая
Порог вхождения Высокий Низкий Средний
Доступность Ограниченная Широкая Широкая
Поддержка рейтрейсинга Нативная Через плагины Нативная
Лицензирование Специальное соглашение Бесплатно + % с дохода Бесплатно + % с дохода

Особенностью PhyreEngine является его компонентная архитектура, которая позволяет разработчикам использовать только те модули, которые им необходимы для конкретного проекта. Это уменьшает избыточность и позволяет создавать более компактные и эффективные игры.

В отличие от других движков, PhyreEngine предоставляет непосредственный доступ к низкоуровневым API консолей PlayStation, что открывает возможности для тонкой настройки производительности. Это особенно ценно для разработчиков AAA-игр, где каждый процент производительности имеет значение.

Пошаговый план для смены профессии

Установка и настройка PhyreEngine для разработки игр

Процесс установки PhyreEngine отличается от установки общедоступных движков, поскольку требует официальной регистрации в программе разработчиков PlayStation. После получения доступа к инструментарию, процесс установки становится более стандартным.

Марина Ковалёва, инди-разработчик

Моя первая попытка настроить PhyreEngine закончилась катастрофой. Я потратила неделю, пытаясь разобраться с конфликтующими зависимостями и непонятными ошибками компиляции. На грани отчаяния я обнаружила, что просто использовала несовместимую версию компилятора. После перехода на рекомендуемую версию Visual Studio все заработало буквально за пару часов. Самое забавное, что именно эта борьба с настройкой заставила меня детально изучить структуру движка, что впоследствии оказалось бесценным. Теперь я могу найти и исправить почти любую проблему в проекте, потому что знаю, "где искать тело" 😄. Совет новичкам: строго следуйте инструкции по установке и не пропускайте шаги, какими бы незначительными они ни казались.

Вот пошаговая инструкция для установки PhyreEngine:

  1. Зарегистрируйтесь в программе разработчиков PlayStation через официальный портал Sony Developer Network
  2. После одобрения заявки, получите доступ к загрузке SDK и PhyreEngine
  3. Скачайте актуальную версию PhyreEngine для вашей целевой платформы
  4. Установите соответствующую версию Visual Studio (обычно рекомендуется VS2019 или новее)
  5. Установите необходимые инструменты и зависимости, указанные в документации
  6. Распакуйте архив PhyreEngine в выбранную директорию
  7. Запустите скрипт инициализации, который настроит переменные окружения и пути к инструментам
  8. Проверьте установку, скомпилировав и запустив примеры из директории samples

Для эффективной работы с PhyreEngine необходимо настроить несколько критических компонентов:

  • Конфигурация проекта в соответствии с целевой платформой (PS4, PS5, ПК)
  • Настройка графического API (обычно выбор между DirectX и PlayStation Graphics API)
  • Установка и интеграция инструментов для работы с ассетами
  • Конфигурация сред разработки и отладки

Системные требования для разработки на PhyreEngine варьируются в зависимости от масштаба проекта и целевой платформы, но минимальная конфигурация выглядит следующим образом:

Компонент Минимальные требования Рекомендуемые требования
Процессор Intel Core i5 8-го поколения / AMD Ryzen 5 Intel Core i7 10-го поколения / AMD Ryzen 7
Оперативная память 16 ГБ 32 ГБ или больше
Видеокарта NVIDIA GTX 1060 / AMD RX 580 NVIDIA RTX 2070 или выше
Хранилище SSD 256 ГБ + HDD 1 ТБ SSD 1 ТБ + HDD 2 ТБ
ОС Windows 10 64-bit Windows 10/11 64-bit

После установки и настройки базовых компонентов рекомендуется потратить время на изучение структуры каталогов и организации проекта. Это значительно ускорит последующую разработку и поможет избежать типичных проблем с конфигурацией и зависимостями.

Основы работы с PhyreEngine: интерфейс и компоненты

PhyreEngine построен на принципах компонентно-ориентированной архитектуры, что позволяет создавать гибкие и модульные игровые системы. Работа с движком происходит преимущественно через написание кода с использованием C++, хотя существуют и высокоуровневые инструменты для определенных задач.

Основные компоненты PhyreEngine включают:

  • Core Framework — ядро движка, отвечающее за базовую функциональность и абстракцию платформ
  • Graphics System — система рендеринга с поддержкой различных графических API
  • Physics Engine — физический движок для симуляции твердых тел и тканей
  • Audio System — звуковая подсистема с поддержкой 3D-аудио
  • Animation System — система анимации персонажей и объектов
  • Asset Pipeline — конвейер обработки и оптимизации игровых ресурсов
  • Networking — сетевой стек для многопользовательских игр
  • UI Framework — система для создания пользовательского интерфейса

Взаимодействие с этими компонентами происходит через API движка. Вот пример базовой структуры проекта и использования основных систем:

cpp
Скопировать код
// Пример инициализации основных систем PhyreEngine
#include "PhyreEngine/Core.h"
#include "PhyreEngine/Graphics.h"
#include "PhyreEngine/Physics.h"

class MyGame : public phyre::Application {
public:
MyGame() : Application("My First PhyreEngine Game") {}

bool Initialize() override {
// Инициализация графики
graphics_system_ = std::make_unique<phyre::GraphicsSystem>();
if (!graphics_system_->Initialize(window_handle_)) {
return false;
}

// Инициализация физики
physics_system_ = std::make_unique<phyre::PhysicsSystem>();
if (!physics_system_->Initialize()) {
return false;
}

// Загрузка игровых ресурсов
resource_manager_ = std::make_unique<phyre::ResourceManager>();
if (!LoadGameResources()) {
return false;
}

return true;
}

void Update(float delta_time) override {
// Обновление игровой логики
physics_system_->Simulate(delta_time);
game_world_->Update(delta_time);
}

void Render() override {
graphics_system_->BeginFrame();
game_world_->Render();
graphics_system_->EndFrame();
}

private:
std::unique_ptr<phyre::GraphicsSystem> graphics_system_;
std::unique_ptr<phyre::PhysicsSystem> physics_system_;
std::unique_ptr<phyre::ResourceManager> resource_manager_;
std::unique_ptr<GameWorld> game_world_;
};

PHYRE_MAIN(MyGame)

В отличие от многих других движков, PhyreEngine не имеет визуального редактора уровней в традиционном понимании. Вместо этого он предоставляет инструменты командной строки и интеграцию с популярными пакетами 3D-моделирования, такими как Maya и Blender, через плагины экспорта.

Для эффективной работы с PhyreEngine необходимо освоить следующие концепции:

  • Entity-Component System — каждый игровой объект (Entity) состоит из набора компонентов, определяющих его поведение
  • Scene Graph — иерархическая структура, представляющая организацию объектов в игровом мире
  • Material System — система материалов, определяющая визуальные свойства объектов
  • Shader Framework — структура для написания и управления шейдерами
  • Event System — система событий для коммуникации между компонентами

Важным аспектом работы с PhyreEngine является понимание его подхода к управлению памятью и оптимизации. Движок предоставляет низкоуровневые инструменты для контроля над распределением ресурсов, что требует от разработчика большей ответственности, но и даёт больший контроль.

Создаем первую игру на PhyreEngine: пошаговое руководство

Создание даже простой игры на PhyreEngine требует последовательного подхода. В этом разделе мы рассмотрим процесс разработки простого 3D-платформера от концепции до готового продукта. 🚀

Начнем с определения базовой структуры проекта и основных игровых систем:

cpp
Скопировать код
// Game.h – основной класс игры
#pragma once
#include "PhyreEngine/Core.h"
#include "PhyreEngine/Graphics.h"
#include "PhyreEngine/Physics.h"
#include "PhyreEngine/Audio.h"
#include "Player.h"
#include "Level.h"

class PlatformerGame : public phyre::Application {
public:
PlatformerGame();
~PlatformerGame();

bool Initialize() override;
void Update(float delta_time) override;
void Render() override;

private:
bool LoadResources();
void HandleInput();

std::unique_ptr<phyre::GraphicsSystem> graphics_;
std::unique_ptr<phyre::PhysicsSystem> physics_;
std::unique_ptr<phyre::AudioSystem> audio_;
std::unique_ptr<Player> player_;
std::unique_ptr<Level> current_level_;
phyre::Camera main_camera_;
bool game_paused_ = false;
};

Основная игровая логика будет реализована в методе Update, который вызывается каждый кадр:

cpp
Скопировать код
void PlatformerGame::Update(float delta_time) {
if (game_paused_) return;

// Обработка ввода
HandleInput();

// Обновление физики
physics_->Simulate(delta_time);

// Обновление игровых объектов
player_->Update(delta_time);
current_level_->Update(delta_time);

// Обновление камеры (следование за игроком)
phyre::Vector3 player_position = player_->GetPosition();
main_camera_.SetPosition(player_position + phyre::Vector3(0.0f, 3.0f, -5.0f));
main_camera_.LookAt(player_position);

// Проверка условий завершения уровня
if (current_level_->IsCompleted()) {
LoadNextLevel();
}
}

Теперь рассмотрим создание персонажа игрока, который будет основным управляемым объектом:

cpp
Скопировать код
// Player.h
#pragma once
#include "PhyreEngine/Core.h"
#include "PhyreEngine/Physics.h"
#include "PhyreEngine/Graphics.h"

class Player {
public:
Player(phyre::PhysicsSystem* physics, phyre::ResourceManager* resources);

void Update(float delta_time);
void Render(phyre::GraphicsSystem* graphics);

void Jump();
void Move(const phyre::Vector3& direction);

phyre::Vector3 GetPosition() const;

private:
phyre::Model model_;
phyre::RigidBody* physics_body_;
phyre::Animation idle_animation_;
phyre::Animation run_animation_;
phyre::Animation jump_animation_;

float move_speed_ = 5.0f;
float jump_force_ = 10.0f;
bool is_grounded_ = false;

void UpdateAnimation(float delta_time);
void CheckGroundContact();
};

// Player.cpp (фрагмент)
void Player::Move(const phyre::Vector3& direction) {
if (!is_grounded_) {
// В воздухе движение ограничено
phyre::Vector3 current_velocity = physics_body_->GetLinearVelocity();
phyre::Vector3 air_control = direction * (move_speed_ * 0.3f);
physics_body_->SetLinearVelocity(phyre::Vector3(
air_control.x, 
current_velocity.y, 
air_control.z
));
} else {
// На земле полное управление
physics_body_->SetLinearVelocity(phyre::Vector3(
direction.x * move_speed_,
physics_body_->GetLinearVelocity().y,
direction.z * move_speed_
));
}
}

void Player::Jump() {
if (is_grounded_) {
physics_body_->ApplyImpulse(phyre::Vector3(0.0f, jump_force_, 0.0f));
is_grounded_ = false;
}
}

Для создания уровня мы используем систему тайлов и процедурную генерацию:

cpp
Скопировать код
// Level.h (фрагмент)
class Level {
public:
Level(phyre::PhysicsSystem* physics, phyre::ResourceManager* resources);

void LoadFromFile(const std::string& file_path);
void GenerateProcedural(int width, int length, int difficulty);

void Update(float delta_time);
void Render(phyre::GraphicsSystem* graphics);

bool IsCompleted() const;
phyre::Vector3 GetStartPosition() const;
phyre::Vector3 GetEndPosition() const;

private:
std::vector<std::vector<int>> tile_map_;
std::vector<phyre::Model> tile_models_;
std::vector<phyre::RigidBody*> collision_bodies_;

phyre::Vector3 start_position_;
phyre::Vector3 end_position_;
bool level_completed_ = false;

void CreatePhysicsForTile(int tile_type, const phyre::Vector3& position);
};

Реализация обработки пользовательского ввода позволяет управлять персонажем:

cpp
Скопировать код
void PlatformerGame::HandleInput() {
phyre::InputSystem* input = phyre::InputSystem::GetInstance();

// Векторы направления движения
phyre::Vector3 move_direction(0.0f, 0.0f, 0.0f);

// Обработка клавиш WASD
if (input->IsKeyPressed(phyre::KeyCode::W)) {
move_direction.z += 1.0f;
}
if (input->IsKeyPressed(phyre::KeyCode::S)) {
move_direction.z -= 1.0f;
}
if (input->IsKeyPressed(phyre::KeyCode::A)) {
move_direction.x -= 1.0f;
}
if (input->IsKeyPressed(phyre::KeyCode::D)) {
move_direction.x += 1.0f;
}

// Нормализация вектора движения
if (move_direction.LengthSquared() > 0.0f) {
move_direction.Normalize();
}

// Применение движения к игроку
player_->Move(move_direction);

// Обработка прыжка
if (input->IsKeyJustPressed(phyre::KeyCode::SPACE)) {
player_->Jump();
}

// Пауза игры
if (input->IsKeyJustPressed(phyre::KeyCode::ESCAPE)) {
game_paused_ = !game_paused_;
}
}

В завершение, добавим базовый пользовательский интерфейс:

cpp
Скопировать код
// UI.h (фрагмент)
class GameUI {
public:
GameUI(phyre::GraphicsSystem* graphics);

void Update(float delta_time);
void Render();

void ShowMainMenu();
void ShowGameOverScreen();
void ShowPauseMenu();
void ShowHUD(int score, int lives, float health);

private:
phyre::UIPanel main_panel_;
phyre::UIText score_text_;
phyre::UIText lives_text_;
phyre::UIProgressBar health_bar_;

bool show_main_menu_ = true;
bool show_game_over_ = false;
bool show_pause_menu_ = false;
};

Для полноценной игры также потребуется реализовать системы коллизий, аудио, систему частиц для эффектов и многие другие компоненты. PhyreEngine предоставляет базовые инструменты для каждой из этих систем, которые нужно интегрировать в игровой цикл.

Этот базовый каркас демонстрирует структуру типичного проекта на PhyreEngine и показывает основные принципы организации кода. По мере развития проекта, можно добавлять новые функции, оптимизировать существующие системы и расширять игровой контент.

Продвинутые техники и готовые проекты на PhyreEngine

После освоения основ PhyreEngine можно переходить к изучению продвинутых техник, которые позволят поднять качество ваших игр на новый уровень. Рассмотрим несколько ключевых областей. 🔥

Одно из главных преимуществ PhyreEngine — его мощная система рендеринга с поддержкой передовых технологий. Вот пример использования расширенных графических функций:

cpp
Скопировать код
// Настройка системы рендеринга с поддержкой рейтрейсинга
phyre::RenderingSettings settings;
settings.EnableRayTracing(true);
settings.SetRayTracingBounces(3);
settings.EnableGlobalIllumination(true);
settings.SetShadowQuality(phyre::ShadowQuality::Ultra);
settings.EnableTemporalAA(true);

graphics_system_->ApplySettings(settings);

// Создание материала с PBR-свойствами
phyre::Material metal_material;
metal_material.SetAlbedo(phyre::Color(0.95f, 0.76f, 0.1f));
metal_material.SetRoughness(0.2f);
metal_material.SetMetallic(0.9f);
metal_material.SetNormalMap(resource_manager_->LoadTexture("assets/textures/metal_normal.png"));
metal_material.SetAmbientOcclusion(resource_manager_->LoadTexture("assets/textures/metal_ao.png"));

// Применение материала к модели
model_->SetMaterial(metal_material);

PhyreEngine также предлагает мощные инструменты для создания систем частиц и визуальных эффектов:

cpp
Скопировать код
// Создание системы частиц для эффекта огня
auto particle_system = std::make_unique<phyre::ParticleSystem>();
particle_system->SetMaxParticles(10000);
particle_system->SetEmissionRate(500);

phyre::ParticleProperties fire_props;
fire_props.start_color = phyre::Color(1.0f, 0.5f, 0.0f, 0.8f);
fire_props.end_color = phyre::Color(0.5f, 0.0f, 0.0f, 0.0f);
fire_props.start_size = phyre::Vector2(0.1f, 0.1f);
fire_props.end_size = phyre::Vector2(0.5f, 0.5f);
fire_props.min_lifetime = 0.5f;
fire_props.max_lifetime = 2.0f;
fire_props.gravity_effect = -0.1f; // Отрицательная гравитация для эффекта подъема
fire_props.velocity_variance = phyre::Vector3(0.3f, 0.5f, 0.3f);

particle_system->SetParticleProperties(fire_props);
particle_system->SetTexture(resource_manager_->LoadTexture("assets/textures/particle.png"));
particle_system->SetBlendMode(phyre::BlendMode::Additive);

Для оптимизации производительности игры на различных устройствах можно использовать систему уровней детализации (LOD):

cpp
Скопировать код
// Создание модели с несколькими уровнями детализации
phyre::ModelLODGroup character_model;
character_model.AddLOD(0, resource_manager_->LoadModel("assets/models/character_high.pmdl"), 0.0f);
character_model.AddLOD(1, resource_manager_->LoadModel("assets/models/character_medium.pmdl"), 10.0f);
character_model.AddLOD(2, resource_manager_->LoadModel("assets/models/character_low.pmdl"), 25.0f);
character_model.AddLOD(3, resource_manager_->LoadModel("assets/models/character_lowest.pmdl"), 50.0f);

// Автоматический выбор подходящего LOD на основе расстояния до камеры
character_model.SetLODSelectionMode(phyre::LODSelectionMode::Distance);

Для создания сложных поведенческих моделей NPC можно использовать систему искусственного интеллекта PhyreEngine:

cpp
Скопировать код
// Создание поведенческого дерева для вражеского ИИ
auto behavior_tree = std::make_unique<phyre::BehaviorTree>();

// Создание узлов дерева решений
auto root_sequence = behavior_tree->CreateSequence("RootSequence");
auto detect_player = behavior_tree->CreateCondition("DetectPlayer", [this](float dt) -> bool {
return (player_->GetPosition() – enemy_position_).Length() < detection_radius_;
});

auto combat_selector = behavior_tree->CreateSelector("CombatSelector");
auto health_check = behavior_tree->CreateCondition("HealthCheck", [this](float dt) -> bool {
return enemy_health_ < 30.0f; 
});

auto retreat_action = behavior_tree->CreateAction("Retreat", [this](float dt) -> phyre::BehaviorStatus {
// Логика отступления
phyre::Vector3 retreat_dir = (enemy_position_ – player_->GetPosition()).Normalized();
enemy_position_ += retreat_dir * retreat_speed_ * dt;
return phyre::BehaviorStatus::Success;
});

auto attack_sequence = behavior_tree->CreateSequence("AttackSequence");
auto approach_action = behavior_tree->CreateAction("Approach", [this](float dt) -> phyre::BehaviorStatus {
// Логика сближения с игроком
phyre::Vector3 approach_dir = (player_->GetPosition() – enemy_position_).Normalized();
enemy_position_ += approach_dir * approach_speed_ * dt;

if ((player_->GetPosition() – enemy_position_).Length() < attack_range_) {
return phyre::BehaviorStatus::Success;
}
return phyre::BehaviorStatus::Running;
});

auto attack_action = behavior_tree->CreateAction("Attack", [this](float dt) -> phyre::BehaviorStatus {
// Логика атаки
attack_cooldown_ -= dt;
if (attack_cooldown_ <= 0.0f) {
PerformAttack();
attack_cooldown_ = attack_rate_;
return phyre::BehaviorStatus::Success;
}
return phyre::BehaviorStatus::Running;
});

// Сборка дерева
root_sequence->AddChild(detect_player);
root_sequence->AddChild(combat_selector);

combat_selector->AddChild(health_check);
combat_selector->AddChild(attack_sequence);

health_check->SetNextNode(retreat_action);

attack_sequence->AddChild(approach_action);
attack_sequence->AddChild(attack_action);

// Использование дерева поведения
behavior_tree->Update(delta_time);

В индустрии существует множество впечатляющих проектов, созданных с использованием PhyreEngine. Вот некоторые категории игр, для которых этот движок особенно хорошо подходит:

Жанр игры Преимущества PhyreEngine Примеры техник
Экшен/Адвенчура Высокая производительность, качественная графика Продвинутая анимация персонажей, динамическое освещение
Гоночные симуляторы Реалистичная физика, оптимизация для стабильного FPS Процедурные повреждения, системы частиц для эффектов
Шутеры от первого лица Быстрый рендеринг, продвинутые эффекты постобработки Объемный туман, системы разрушения, продвинутый AI
Открытый мир Эффективный стриминг контента, поддержка больших ландшафтов LOD системы, процедурная генерация контента
VR-проекты Низкая задержка, оптимизация для VR Стереоскопический рендеринг, системы взаимодействия

При разработке коммерческих проектов на PhyreEngine важно учитывать особенности лицензирования и распространения. В отличие от Unity или Unreal Engine, для использования PhyreEngine требуется специальное соглашение с Sony Interactive Entertainment, а публикация игр обычно ориентирована прежде всего на экосистему PlayStation.

Для расширения возможностей вашего проекта рекомендуется также изучить интеграцию с дополнительными инструментами и библиотеками, такими как middleware для звука, сетевого взаимодействия или управления данными. PhyreEngine предоставляет API для интеграции с популярными решениями, что позволяет дополнить его функциональность в тех областях, где это необходимо.

PhyreEngine — это не просто инструмент, это полноценная экосистема для создания высококлассных игр. Освоив его основы и продвинутые техники, вы получаете доступ к тому же арсеналу средств, который используют многие профессиональные студии при разработке эксклюзивов PlayStation. Путь от новичка до эксперта в PhyreEngine требует времени и усилий, но результат стоит того — возможность создавать игры с графикой консольного качества и оптимальной производительностью. Начните с малых проектов, экспериментируйте с разными компонентами движка и постепенно наращивайте сложность своих разработок.

Читайте также

Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Какой язык программирования используется для написания скриптов в PhyreEngine?
1 / 5

Загрузка...