using System; using System.Collections; using System.Collections.Generic; using System.IO; using BITKit; using BITKit.Entities; using BITKit.WorldNode; using Cysharp.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Net.Project.B.World; using Project.B.Map; using Unity.Mathematics; using UnityEngine; using UnityEngine.AI; using UnityEngine.SceneManagement; using Object = UnityEngine.Object; namespace Net.Project.B.WorldNode { public class UnityMinimapService:IWorldMinimapService { public string Directory { get; } = Path.Combine(Application.persistentDataPath, "Overviews").Replace('/', '\\'); private readonly IGameMapService _gameMapService; private readonly ILogger _logger; private readonly IEntitiesService _entitiesService; private UniTaskCompletionSource _waitMinimap=new(); public object MinimapObject { get; private set; } public float2 Offset { get;private set; } public int Size { get; private set;} public event Action OnMinimapInitialized; public UnityMinimapService(ILogger logger, IEntitiesService entitiesService, IGameMapService gameMapService) { _logger = logger; _entitiesService = entitiesService; _gameMapService = gameMapService; _entitiesService.OnAdd += OnNodeRegistered; _gameMapService.OnMapChanged += OnMapChanged; } private async void OnMapChanged(Guid arg1, string arg2) { if(string.IsNullOrEmpty(arg2))return; _waitMinimap?.TrySetCanceled(); _waitMinimap = new(); try { await _waitMinimap.Task.Timeout(TimeSpan.FromSeconds(1)); return; } catch(OperationCanceledException){} catch (TimeoutException) { _logger.LogInformation("未找到预设Minimap,开始自动创建"); } foreach (var scriptableGameMapData in _entitiesService.QueryComponents().ToArray()) { if(scriptableGameMapData.MapAddress != arg2)continue; if(scriptableGameMapData.MapOverview is not Object{ }obj)continue; if(!obj)continue; MinimapObject = scriptableGameMapData.MapOverview; Offset = scriptableGameMapData.Offset; Size = scriptableGameMapData.Size; OnMinimapInitialized?.Invoke(); _logger.LogInformation($"从{nameof(ScriptableGameMapData)}中找到了Minimap"); return; } var worldBounds = new Bounds(default,Vector3.one*32); foreach (var rootGameObject in SceneManager.GetActiveScene().GetRootGameObjects()) { foreach (var collider in rootGameObject.GetComponentsInChildren()) { var bounds = collider.bounds; if(bounds.size.GetLength()>32 || collider.gameObject.isStatic)continue; worldBounds.Encapsulate(bounds); } } var size = worldBounds.size; var camera = new GameObject().AddComponent(); //camera.cullingMask = LayerMask.NameToLayer("Default"); camera.orthographicSize = math.max(size.x,size.z); camera.transform.position = worldBounds.center + Vector3.up * camera.orthographicSize; camera.transform.localEulerAngles = new(90, 0, 0); camera.orthographic = true; var rendererTexture = new RenderTexture( (int)camera.orthographicSize*8, (int)camera.orthographicSize*8, 32); camera.targetTexture = rendererTexture; camera.Render(); var cameraPosition = camera.transform.position; Offset = new(cameraPosition.x, cameraPosition.z); Size = (int)camera.orthographicSize; camera.targetTexture = null; SaveRendererTextureToPng(_gameMapService.CurrentMap,rendererTexture); Object.Destroy(camera.gameObject); MinimapObject = rendererTexture; OnMinimapInitialized?.Invoke(); } private async void OnNodeRegistered(IEntity entity) { try { if(entity.ServiceProvider.GetService() is not {}minimapNode)return; if (entity.ServiceProvider.GetService() is not { } camera) { if (entity.ServiceProvider.GetService() is {} transform && transform.TryGetComponent(out camera) ) { } else { _logger.LogWarning("未找到Camera,跳过"); return; } } await UniTask.NextFrame(); if (!camera) { _logger.LogWarning("Camera 已被销毁,跳过"); return; } GameObject sample = null; if(false) { NavMeshTriangulation navMeshTriangluation = NavMesh.CalculateTriangulation(); int[] triangles = navMeshTriangluation.indices; Vector3[] vertices = navMeshTriangluation.vertices; var mesh = new Mesh(); mesh.vertices = vertices; mesh.triangles = triangles; mesh.RecalculateNormals(); mesh.RecalculateBounds(); mesh.Optimize(); sample = new GameObject(); sample.transform.position = default; sample.transform.rotation = default; sample.AddComponent().mesh = mesh; sample.AddComponent().material = new Material(Shader.Find("Unlit/Color")) { color = new Color32(64, 64, 64, 255), renderQueue = 4000 }; } camera.Render(); SaveRendererTextureToPng(_gameMapService.CurrentMap,camera.targetTexture); await UniTask.NextFrame(); if(sample) Object.Destroy(sample); MinimapObject = camera.targetTexture; camera.targetTexture = null; Object.Destroy(camera.gameObject); _logger.LogInformation("已获取到Minimap"); var cameraPosition = camera.transform.position; Offset = new(cameraPosition.x, cameraPosition.z); Size = (int)camera.orthographicSize; OnMinimapInitialized?.Invoke(); _waitMinimap.TrySetResult(); } catch (Exception e) { _logger.LogError(e,e.Message); } } private void SaveRendererTextureToPng(string name,RenderTexture rendererTexture) { var screenshot = new Texture2D(rendererTexture.width, rendererTexture.height, TextureFormat.RGBA32, false); RenderTexture.active = rendererTexture; screenshot.ReadPixels(new Rect(0, 0, rendererTexture.width, rendererTexture.height), 0, 0); screenshot.Apply(); RenderTexture.active = null; var bytes = screenshot.EncodeToPNG(); var fileName = DateTime.Now.Ticks; var path = Path.Combine(Directory,$"{name}_{fileName}.png"); new DirectoryInfo(Directory).Create(); File.WriteAllBytes(path, bytes); } } }