Эффективная окраска стен в Unity с использованием текстурного атласа
Руководство по реализации независимой окраски сторон стен в рантайме для вашего игрового редактора уровня
Ключевые моменты
- Оптимизация производительности: Использование текстурного атласа снижает количество draw calls, что критично для мобильных устройств и сложных сцен, позволяя эффективно управлять текстурами без потери FPS.
- Независимая окраска сторон: С помощью правильной UV-раскладки в Blender и скриптов в Unity, каждая сторона стены может быть окрашена отдельно, обеспечивая гибкость, как в The Sims.
- Простота реализации: Система на основе индексов текстур (0-7) и кликов мыши позволяет игрокам легко выбирать и применять текстуры, с минимальными изменениями в коде для поддержки рантайм-редактора.
Подготовка 3D-модели стены в Blender
Перед тем как перейти к реализации в Unity, важно правильно подготовить модель стены в Blender. Это обеспечит, что каждая сторона стены может быть окрашена независимо без лишних затрат на производительность. Стена, как простая лоуполи-модель, должна иметь отдельные UV-острова для каждой грани, чтобы текстуры могли применяться selectively.
В Blender создайте базовую геометрию стены. Для достижения независимой окраски сторон, разделите модель на подмодели или используйте UV-раскладку, где каждая сторона (внутренняя и внешняя) имеет свой собственный UV-пространство. Это можно сделать с помощью инструментов UV Editor:
- Выделите грани стены и используйте Unwrap (Ctrl+E) для создания отдельных UV-островов. Например, назначьте UV-координаты так, чтобы внутренняя сторона занимала один диапазон, а внешняя — другой.
- Поскольку текстуры будут храниться в атласе 8x8 (с индексами от 0 до 7), настройте UV-координаты в диапазоне от 0 до 1 для каждой стороны, но с возможностью смещения для выбора конкретной текстуры из атласа.
Экспортируйте модель в FBX или OBJ формат, чтобы импортировать её в Unity. Убедитесь, что UV-каналы сохранены. Это позволит в Unity манипулировать UV-координатами через код для изменения текстуры на лету.
Преимущества использования UV-островов
Разделение UV на острова предотвращает искажения текстур и упрощает управление. Например, если внутренняя сторона стены имеет UV от (0,0) до (0.5,1), а внешняя — от (0.5,0) до (1,1), то в атласе вы можете назначать разные секции текстуры без изменения геометрии модели.
Изображение: Пример UV-карты для текстурного атласа, показывающее разделение на секции. Это помогает визуализировать, как индексы текстур (0-7) отображаются на модель.
Настройка текстурного атласа в Unity
Текстовый атлас — это ключ к оптимизации, так как он объединяет все текстуры в один файл, снижая количество draw calls. В вашем случае, с 8 текстурами в сетке, атлас можно создать вручную или с помощью инструментов Unity.
Создайте текстурный атлас в Photoshop или TexturePacker, разместив текстуры в 2x4 или 4x2 сетку (в зависимости от размера). Импортируйте атлас в Unity как Texture2D. Затем назначьте его материалу, который будет использоваться для стен.
Для управления текстурами через код используйте свойства материала, такие как mainTextureOffset и mainTextureScale, чтобы смещать UV-координаты и выбирать нужную секцию атласа. Поскольку текстуры имеют индексы от 0 до 7, рассчитайте UV-смещение на основе индекса:
- Если атлас разделен на 8 секций (например, 2 строки и 4 столбца), то для индекса i UV-offset будет (i % 4 / 4, Math.Floor(i / 4) / 2) для нормализации в диапазоне [0,1].
Код для применения текстуры
В скрипте C# реализуйте систему, где игрок выбирает текстуру по индексу, а затем кликает на стену. Используйте Raycast для определения, какая сторона стены была задета. Затем измените UV-координаты или материал для этой грани.
using UnityEngine;
public class WallPainter : MonoBehaviour
{
public Texture2D textureAtlas; // Атлас текстур
public int selectedTextureIndex = 0; // Индекс выбранной текстуры (0-7)
private Material wallMaterial; // Материал стены
void Start()
{
// Присвойте материал объекту стены
Renderer renderer = GetComponent<Renderer>();
if (renderer != null)
{
wallMaterial = renderer.material;
wallMaterial.mainTexture = textureAtlas; // Назначьте атлас текстур
}
}
void Update()
{
if (Input.GetMouseButtonDown(0)) // Клик левой кнопкой мыши
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
// Проверьте, что hit.collider — это стена
if (hit.collider.gameObject == gameObject) // Или используйте тег/компонент для идентификации
{
PaintWallSide(hit, selectedTextureIndex);
}
}
}
}
public void SetSelectedTexture(int index)
{
if (index >= 0 && index <= 7)
{
selectedTextureIndex = index;
}
}
private void PaintWallSide(RaycastHit hit, int textureIndex)
{
// Рассчитайте UV-смещение на основе индекса
float atlasWidth = 4.0f; // Ширина атласа в секциях (4 столбца)
float atlasHeight = 2.0f; // Высота атласа в секциях (2 строки для 8 текстур)
float uOffset = (textureIndex % 4) / atlasWidth;
float vOffset = Mathf.Floor(textureIndex / 4.0f) / atlasHeight;
// Примените смещение к UV-координатам. Это упрощенный подход; для точности используйте Mesh.vertices и Mesh.uv
Mesh mesh = GetComponent<MeshFilter>().mesh;
Vector2[] uvs = mesh.uv;
// Найдите UV для hit.triIndex (индекс треугольника)
int triangleIndex = hit.triangleIndex * 3; // Каждый треугольник имеет 3 вершины
for (int i = 0; i < 3; i++)
{
// Измените UV для вершин треугольника. Это может потребовать более сложной логики для независимых сторон.
uvs[mesh.triangles[triangleIndex + i]] = new Vector2(uvs[mesh.triangles[triangleIndex + i]].x + uOffset, uvs[mesh.triangles[triangleIndex + i]].y + vOffset);
}
mesh.uv = uvs; // Примените изменения UV
mesh.UploadMeshData(false); // Обновите mesh для рендеринга
// Альтернативно, если используете отдельные материалы для сторон, измените material index
}
}
В этом коде PaintWallSide метод изменяет UV-координаты треугольника, на который кликнули, чтобы переместить текстуру в нужную секцию атласа. Для более точного контроля, особенно если стена имеет несколько граней, используйте подмесхи или несколько материалов.
Преимущества текстурного атласа
Атлас уменьшает память и повышает производительность, так как GPU обрабатывает один текстурный сэмплер вместо нескольких. Из источников, таких как Unity документация, видно, что динамический атлас может автоматически расти, но для вашего случая статический атлас с фиксированными индексами подойдет лучше.
Управление текстурами в рантайме
В вашем редакторе уровня игроки должны уметь выбирать текстуру и окрашивать сегменты стен. Для этого интегрируйте UI-элементы, такие как кнопки для выбора индекса текстуры, и обработку ввода для кликов.
Чтобы обеспечить независимую окраску, модель стены должна быть разделена на подмесхи (submeshes), где каждая сторона — отдельный submesh с собственным материалом. Это позволит назначать разные текстуры без изменения UV глобально.
-
Подход с submeshes: В Blender создайте отдельные объекты для внутренней и внешней сторон стены, экспортируйте как один mesh с несколькими submeshes. В Unity назначьте разные материалы или один материал с атласом и изменяйте индекс текстуры.
-
Shader-based подход: Используйте кастомный шейдер, который принимает индекс текстуры и вычисляет UV на основе атласа. Это может быть эффективнее, но требует знаний HLSL.
Для кликов используйте Raycast с учетом нормали грани (hit.normal), чтобы определить, внутренняя или внешняя сторона была задета. Затем примените текстуру через код.
Таблица индексов текстур в атласе
Для ясности, вот таблица, показывающая, как индексы текстур (0-7) отображаются на UV-координатах в атласе размером 4x2 секции. Это поможет в расчете смещений.
| Индекс | U-координата (offset) | V-координата (offset) | Описание |
|---|---|---|---|
| 0 | 0.0 - 0.25 | 0.0 - 0.5 | Текстура для стены, например, кирпич |
| 1 | 0.25 - 0.5 | 0.0 - 0.5 | Дерево или другой материал |
| 2 | 0.5 - 0.75 | 0.0 - 0.5 | Камень |
| 3 | 0.75 - 1.0 | 0.0 - 0.5 | Металл |
| 4 | 0.0 - 0.25 | 0.5 - 1.0 | Обои |
| 5 | 0.25 - 0.5 | 0.5 - 1.0 | Плитка |
| 6 | 0.5 - 0.75 | 0.5 - 1.0 | Краска |
| 7 | 0.75 - 1.0 | 0.5 - 1.0 | Другой материал |
Эта таблица показывает, как на основе индекса рассчитывать UV-смещение. Используйте её в коде для точного позиционирования текстуры.
Интеграция с редактором уровня
В вашем рантайм-редакторе добавьте UI для выбора текстуры (например, кнопки с индексами). После выбора индекса, при клике на стену, вызовите метод окраски. Для поддержки окраски отдельных сегментов, разделите стены на мелкие объекты или используйте компоненты для хранения текстурного состояния.
Добавление визуальных элементов и видео
Чтобы лучше понять концепцию текстурных атласов, посмотрите это видео, которое демонстрирует, как использовать SpriteAtlas в Unity для похожих целей. Видео фокусируется на 2D, но принципы применимы и к 3D-моделям.
Это видео объясняет, как настроить SpriteAtlas и применить его к материалам, что напрямую связано с вашей задачей. Оно показывает, как уменьшить draw calls и управлять текстурами эффективно, что критично для вашего сценария с окраской стен.
Часто задаваемые вопросы (FAQ)
Что делать, если текстура растягивается на стенах?
Чтобы избежать растяжения, убедитесь, что UV-координаты в Blender настроены на единообразное tiling. В Unity используйте свойства material.mainTextureScale для корректировки размера текстуры.
Как оптимизировать производительность для большого количества стен?
Используйте статический батчинг в Unity, комбинируя стены в один mesh, и держите текстурный атлас как можно меньшим. Избегайте частых изменений UV на лету; вместо этого используйте precomputed варианты.
Можно ли использовать шейдеры для более сложной окраски?
Да, кастомные шейдеры могут обрабатывать текстурные индексы напрямую, что ускорит рендеринг. Посмотрите примеры в GitHub-репозиториях, таких как TexturePaint, для вдохновения.
Что если игрок хочет отменить окраску?
Храните историю изменений или используйте undo-систему в коде, сохраняя исходные UV или индексы текстур для каждой грани.
Ссылки
Last updated May 7, 2025