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("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 scenePaths = new List(); 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($"Успешно экспортировано {processedCount} сцен в {Application.persistentDataPath}"); EditorUtility.RevealInFinder(Application.persistentDataPath); // Откроет папку в проводнике } } private void ExportSingleScene(Scene scene) { List BlockData = new List(); BlockData.Add(scene.name); // Первым элементом идет имя уровня // Находим все объекты Rtool. (Используем старый метод для совместимости со всеми версиями Unity) Rtool[] rtools = FindObjectsOfType(); foreach (Rtool o in rtools) { List TBD = new List(); 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().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 { v0, v1, v2, v3 }); // 11, 12, 13, 14 BlockData.Add(string.Join(", ", TBD)); } SaveLevelData(BlockData, scene.name); } private void SaveLevelData(List 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(List 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(); } } }