Net.Like.Xue.Tokyo/Packages-Local/Com.Project.B.Unity/World/UnityMinimapService.cs

253 lines
8.2 KiB
C#

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<UnityMinimapService> _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<UnityMinimapService> 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<ScriptableGameMapData>().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<Collider>())
{
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>();
//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<UnityMinimapNode>() is not {}minimapNode)return;
if (entity.ServiceProvider.GetService<Camera>() is not { } camera)
{
if (entity.ServiceProvider.GetService<Transform>() is {} transform && transform.TryGetComponent<Camera>(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<MeshFilter>().mesh = mesh;
sample.AddComponent<MeshRenderer>().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);
}
}
}