Хочу получить точное изображение террейна, со всеми деревьями и травами, в виде PNG.
Application.CaptureScreenshot
Не подходит, так как надо сначала навести камеру, выровнять её для получения квадратного изображения, посмотреть все углы чтобы нигде не вылезало... Вообщем когда делаешь это больше одного раза, хотелось бы автоматизировать.
Как это можно сделать? Чтобы фотографировал, например, только весь террейн или плоскость.
Для того, чтобы "сфотографировать" отдельный объект или группу объектов можно использовать Render Texture, а далее с помощью несложных манипуляций в коде можно сохранить полученный Render Texute в текстуру и сохранить как PNG файл.
Подобная техника, например, используется для создания мини-карт в играх. Туториал, иллюстрирующий процесс создания мини-карты.
По поводу автоматизации, тут вам придется сделать это самому, но так как рендерить вы будете через отдельную камеру, то сможете и настроить ее по вашему желанию. Вот еще один пример работы с Render Texture.
Привет!
2)Точно подгоняем параметры камеры под размер террейна
Когда то нечто подобное делал, но только для GameObject
'ов.
Нашел где-то пример, точно не помню, но код остался.
float CalculateOptimalDistanceToObject(Bounds bounds) {
float maxSize = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z);
float dist = 2 * maxSize / (2 * Mathf.Tan(.5f * _camera.fieldOfView * Mathf.Deg2Rad));
return dist;
}
Нужно для начала расчитать границы Bounds
Объекта и используя их можем подогнать камеру. На счет террайна точно не уверен как будет работать, но в принципе объекты они и в африке объекты)
Использование:
float dist = CalculateOptimalDistanceToObject(_terrain.terrainData.bounds);
_camera.transform.position = _terrain.transform.position + Vector3.back * dist;
На всякий случай, если хотите подгонять камеры для какихто объектов игровых? вот вам код вычисления границ объекта.
Bounds CalculateBounds(GameObject go) {
Bounds bounds = new Bounds(go.transform.position, Vector3.zero);
Transform[] childs = go.GetComponentsInChildren<Transform>();
for(int i = 0; i < childs.Length; i++) {
//childs[i].gameObject.layer = _LayerPreview;
Renderer r = childs[i].GetComponent<Renderer>();
if(r != null) {
bounds.Encapsulate(r.bounds);
}
}
return bounds;
}
Код не идеален и вам наверное придется кое-что поднастроить для своих нужд, но главное что он делает то что требуется
Вообще, если я вас правильно понял, то вам нужна волшебная кнопка "Сделать скриншоты террейнов на сценах".
Ну, давайте думать:
Перспективную камеру мы не сможем настроить как надо, ибо террейн может быть разной высоты в разных точках, выйдет не круто. Значит используем ортогональную камеру.
Делать все лучше через UnityEditor
, не дергая UnityEngine
.
Рендерить лучше в RenderTexture
, после сохранять в обычную текстуру и на диск.
Потом не забыть удалить все за собой.
Я склепал вот такой вот код учитывая все предыдущие замечания (код обильно прокомментирован):
using UnityEditor;
using UnityEngine;
using UnityEditor.SceneManagement;
using System.IO;
public class MenuTest : MonoBehaviour {
// Добавляем пункт меню
[MenuItem("MyMenu/Capture Terrain ScreenShots")]
static void CaptureTerrainScreenShots()
{
int textureHeight = 1024;
// Получаем количество сцен
int sceneCount = UnityEngine.SceneManagement.SceneManager.sceneCountInBuildSettings;
for (int i = 0; i < sceneCount; i++)
{
// Получаем путь к сцене
string scenePath = UnityEngine.SceneManagement.SceneUtility.GetScenePathByBuildIndex(i);
// И ее имя
string sceneName = System.IO.Path.GetFileNameWithoutExtension(scenePath);
// Открываем сцену
EditorSceneManager.OpenScene(scenePath);
// Получаем объект типа Terrain в сцене
Terrain terrain = FindObjectOfType<Terrain>();
if (terrain == null) continue;
// Берем его размер
Vector3 size = terrain.terrainData.size;
// Создаем камеру, с которой будем снимать
Camera camera = new GameObject("ScreenShotCamera", typeof(Camera)).GetComponent<Camera>();
// Позиционируем ее над террейном
camera.transform.position = terrain.transform.position + new Vector3(size.x / 2, size.y, size.z / 2);
camera.transform.LookAt(new Vector3(size.x / 2, 0, size.z / 2));
camera.orthographic = true;
camera.orthographicSize = size.z / 2;
// Создаем текстуру, в которую будем рендерить
// (Предварительно сохранив текущую)
RenderTexture currentRT = RenderTexture.active;
// Создаем текстуру, пропорциональную размеру террейна
RenderTexture rt = new RenderTexture(textureHeight, (int)(textureHeight * size.z / size.x), 24);
// Или вот так - 1 юнит = 1 пиксель
// RenderTexture rt = new RenderTexture(text(int)size.x, (int)size.z, 24);
// Устанавливаем созданную текстуру как целевую
RenderTexture.active = rt;
camera.targetTexture = rt;
// Принудительно вызываем рендер камеры
camera.GetComponent<Camera>().Render();
// Получаем обычную текстуру из RenderTexture'ы
// Ее можно будет использовать в игре, или же
// Сохранить, что мы и сделаем
Texture2D image = new Texture2D(rt.width, rt.height);
image.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
image.Apply();
// Пишем текстуру в .png файл
byte[] bytes = image.EncodeToPNG();
File.WriteAllBytes(Application.dataPath + "/TerrainScreenshots/" + sceneName + ".png", bytes);
// Восстанавливаем рендер таргет
RenderTexture.active = currentRT;
// Чистим все (при необходимости)
DestroyImmediate(image);
DestroyImmediate(camera.gameObject);
DestroyImmediate(rt);
}
}
}
Когда вы сохраните этот код в свой проект, у вас появится сверху пункт меню MyMenu/Capture Terrain ScreenShots
:
После нажатия на который все сцены, которые вы добавили в настройки билда (File/BuildSettings
), будут прогнаны в цикле, и первый попавшийся террейн на них будет запечатлен на камеру.
Код абсолютно рабочий, тестировал на своей машине. Не забудьте только указать нужный путь для сохранения скриншотов (напомню, что все папки на пути должны быть созданы, иначе будет ошибка вида "часть пути не существует бла-бла-бла").
Кстати, есть не очень очевидный момент в коде. Я когда настраиваю камеру, устанавливаю какой-то size
:
camera.orthographicSize = size.z / 2;
Который равен глубине ландшафта. А где же ширина, спросите вы? А ширина задается пропорционально ширине текстуры, которую вы установите в camera.targetTexture
, так что я специально задаю размер текстуры лишь через textureHeight
, а ширина уже считается пропорцией.
И да. Если вам нужно сделать скриншоты и на тех сценах, которые не добавлены в настройки билда, то вам придется придумать альтернативный способ получения сцен. К примеру, если они лежат в одной папке Resources/ЭтаСамаяПапка
, то можно будет использовать Resources.LoadAll
. Однако это уже совсем другая история (мне лень писать еще и итерирование по файловой системе, уж простите).
Как-то так. Надеюсь на галочку)
Перенесла проект с компьютера на ноут, после чего возникла проблема при запуске проекта
В Visual Studio 2017 к проекту на Xamarin подключил библиотеку Microcharts через nuget managerМне требуется некоторые функции изменить в библиотеке, но я не знаю,...
Есть gridView (devexpress) сетка отображения данных из коллекцииВ нем есть своя встроенная панель поиска где вводится текст, и по этому тексту данные...
Ошибка 1 Наиболее подходящий перегруженный метод для SystemThreading