198 lines
7.7 KiB
C#
198 lines
7.7 KiB
C#
|
|
using UnityEngine;
|
|||
|
|
using UnityEditor;
|
|||
|
|
using UnityEditor.SceneManagement;
|
|||
|
|
using UnityEngine.SceneManagement;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Security.Cryptography;
|
|||
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|||
|
|
|
|||
|
|
public class LPJExporterWindow : EditorWindow
|
|||
|
|
{
|
|||
|
|
private float zoomValue = 1f;
|
|||
|
|
private DefaultAsset sceneFolder;
|
|||
|
|
|
|||
|
|
[MenuItem("Tools/LPJ Batch Exporter")]
|
|||
|
|
public static void ShowWindow()
|
|||
|
|
{
|
|||
|
|
GetWindow<LPJExporterWindow>("LPJ Exporter");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnGUI()
|
|||
|
|
{
|
|||
|
|
GUILayout.Label("Настройки экспорта", EditorStyles.boldLabel);
|
|||
|
|
zoomValue = EditorGUILayout.FloatField("Zoom Value", zoomValue);
|
|||
|
|
|
|||
|
|
GUILayout.Space(15);
|
|||
|
|
GUILayout.Label("Режим: Build Settings", EditorStyles.boldLabel);
|
|||
|
|
if (GUILayout.Button("Экспортировать сцены из Build Settings"))
|
|||
|
|
{
|
|||
|
|
ExportBuildSettingsScenes();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
GUILayout.Space(15);
|
|||
|
|
GUILayout.Label("Режим: Из папки", EditorStyles.boldLabel);
|
|||
|
|
sceneFolder = (DefaultAsset)EditorGUILayout.ObjectField("Папка со сценами", sceneFolder, typeof(DefaultAsset), false);
|
|||
|
|
|
|||
|
|
if (GUILayout.Button("Экспортировать сцены из папки"))
|
|||
|
|
{
|
|||
|
|
if (sceneFolder != null)
|
|||
|
|
{
|
|||
|
|
string folderPath = AssetDatabase.GetAssetPath(sceneFolder);
|
|||
|
|
ExportScenesFromFolder(folderPath);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
EditorUtility.DisplayDialog("Ошибка", "Сначала выберите папку со сценами!", "OK");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ExportBuildSettingsScenes()
|
|||
|
|
{
|
|||
|
|
List<string> scenePaths = new List<string>();
|
|||
|
|
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
|
|||
|
|
{
|
|||
|
|
if (scene.enabled) scenePaths.Add(scene.path);
|
|||
|
|
}
|
|||
|
|
ProcessScenes(scenePaths.ToArray());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ExportScenesFromFolder(string folderPath)
|
|||
|
|
{
|
|||
|
|
string[] guids = AssetDatabase.FindAssets("t:Scene", new[] { folderPath });
|
|||
|
|
string[] scenePaths = new string[guids.Length];
|
|||
|
|
|
|||
|
|
for (int i = 0; i < guids.Length; i++)
|
|||
|
|
{
|
|||
|
|
scenePaths[i] = AssetDatabase.GUIDToAssetPath(guids[i]);
|
|||
|
|
}
|
|||
|
|
ProcessScenes(scenePaths);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ProcessScenes(string[] scenePaths)
|
|||
|
|
{
|
|||
|
|
if (scenePaths.Length == 0)
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning("Сцены для экспорта не найдены.");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Сохраняем текущую открытую сцену, чтобы вернуться к ней после экспорта
|
|||
|
|
if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return;
|
|||
|
|
string originalScenePath = EditorSceneManager.GetActiveScene().path;
|
|||
|
|
|
|||
|
|
int processedCount = 0;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < scenePaths.Length; i++)
|
|||
|
|
{
|
|||
|
|
EditorUtility.DisplayProgressBar("Экспорт в .lpj", $"Обработка сцены {i + 1}/{scenePaths.Length}: {Path.GetFileNameWithoutExtension(scenePaths[i])}", (float)i / scenePaths.Length);
|
|||
|
|
|
|||
|
|
Scene scene = EditorSceneManager.OpenScene(scenePaths[i], OpenSceneMode.Single);
|
|||
|
|
ExportSingleScene(scene);
|
|||
|
|
processedCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
EditorUtility.ClearProgressBar();
|
|||
|
|
|
|||
|
|
// Возвращаемся в исходную сцену
|
|||
|
|
if (!string.IsNullOrEmpty(originalScenePath))
|
|||
|
|
{
|
|||
|
|
EditorSceneManager.OpenScene(originalScenePath, OpenSceneMode.Single);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Debug.Log($"<color=green><b>Успешно экспортировано {processedCount} сцен в {Application.persistentDataPath}</b></color>");
|
|||
|
|
EditorUtility.RevealInFinder(Application.persistentDataPath); // Откроет папку в проводнике
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ExportSingleScene(Scene scene)
|
|||
|
|
{
|
|||
|
|
List<string> BlockData = new List<string>();
|
|||
|
|
BlockData.Add(scene.name); // Первым элементом идет имя уровня
|
|||
|
|
|
|||
|
|
// Находим все объекты Rtool. (Используем старый метод для совместимости со всеми версиями Unity)
|
|||
|
|
Rtool[] rtools = FindObjectsOfType<Rtool>();
|
|||
|
|
|
|||
|
|
foreach (Rtool o in rtools)
|
|||
|
|
{
|
|||
|
|
List<float> TBD = new List<float>();
|
|||
|
|
TBD.Add(o.gameObject.transform.position.x * zoomValue); // 0
|
|||
|
|
TBD.Add(o.gameObject.transform.position.y * zoomValue); // 1
|
|||
|
|
TBD.Add(o.gameObject.transform.position.z * zoomValue); // 2
|
|||
|
|
TBD.Add(o.gameObject.transform.rotation.x); // 3
|
|||
|
|
TBD.Add(o.gameObject.transform.rotation.y); // 4
|
|||
|
|
TBD.Add(o.gameObject.transform.rotation.z); // 5
|
|||
|
|
TBD.Add(o.gameObject.transform.rotation.w); // 6
|
|||
|
|
TBD.Add(o.BlockID); // 7
|
|||
|
|
|
|||
|
|
if (o.BlockID==10) o.pos1 = o.gameObject.GetComponent<TPBlock>().SecondTeleport.transform.position;
|
|||
|
|
|
|||
|
|
TBD.Add(o.pos1.x * zoomValue); // 8
|
|||
|
|
TBD.Add(o.pos1.y * zoomValue); // 9
|
|||
|
|
TBD.Add(o.pos1.z * zoomValue); // 10
|
|||
|
|
|
|||
|
|
// Защита от выхода за пределы массива (на случай если values не инициализирован или меньше 4 элементов)
|
|||
|
|
float v0 = o.values != null && o.values.Count > 0 ? o.values[0] : 0f;
|
|||
|
|
float v1 = o.values != null && o.values.Count > 1 ? o.values[1] : 0f;
|
|||
|
|
float v2 = o.values != null && o.values.Count > 2 ? o.values[2] : 0f;
|
|||
|
|
float v3 = o.values != null && o.values.Count > 3 ? o.values[3] : 0f;
|
|||
|
|
|
|||
|
|
TBD.AddRange(new List<float> { v0, v1, v2, v3 }); // 11, 12, 13, 14
|
|||
|
|
|
|||
|
|
BlockData.Add(string.Join(", ", TBD));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
SaveLevelData(BlockData, scene.name);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void SaveLevelData(List<string> BlockData, string name)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
byte[] data = ListToBytes(BlockData);
|
|||
|
|
string fileHash = GetSHA256Hash(data);
|
|||
|
|
string correctPath = Path.Combine(Application.persistentDataPath, $"{name}.lpj");
|
|||
|
|
|
|||
|
|
File.WriteAllBytes(correctPath, data);
|
|||
|
|
}
|
|||
|
|
catch (System.Exception e)
|
|||
|
|
{
|
|||
|
|
Debug.LogError($"Save failed for level {BlockData[0]}: {e.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private byte[] ListToBytes<T>(List<T> list)
|
|||
|
|
{
|
|||
|
|
// Примечание: BinaryFormatter устарел в новых версиях .NET из-за уязвимостей безопасности.
|
|||
|
|
// Так как это локальный Editor-скрипт и данные формируешь ты сам, это не критично.
|
|||
|
|
BinaryFormatter formatter = new BinaryFormatter();
|
|||
|
|
using (MemoryStream stream = new MemoryStream())
|
|||
|
|
{
|
|||
|
|
#pragma warning disable SYSLIB0011 // Подавляем предупреждение об устаревании BinaryFormatter
|
|||
|
|
formatter.Serialize(stream, list);
|
|||
|
|
#pragma warning restore SYSLIB0011
|
|||
|
|
|
|||
|
|
return stream.ToArray();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private string GetSHA256Hash(byte[] data)
|
|||
|
|
{
|
|||
|
|
using (SHA256 sha256 = SHA256.Create())
|
|||
|
|
{
|
|||
|
|
byte[] hashBytes = sha256.ComputeHash(data);
|
|||
|
|
StringBuilder sb = new StringBuilder();
|
|||
|
|
foreach (byte b in hashBytes)
|
|||
|
|
{
|
|||
|
|
sb.Append(b.ToString("x2"));
|
|||
|
|
}
|
|||
|
|
return sb.ToString();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|