734 lines
26 KiB
C#
734 lines
26 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace AibisDream.Framework
|
|
{
|
|
/// <summary>
|
|
/// 合并结果,包含合并后的Sprite和世界位置信息
|
|
/// </summary>
|
|
public struct MergeResult
|
|
{
|
|
public Sprite sprite;
|
|
public Vector3 worldPosition; // 世界空间中心位置
|
|
public Bounds worldBounds; // 世界空间包围盒
|
|
}
|
|
|
|
/// <summary>
|
|
/// SpriteRenderer合并工具类
|
|
/// </summary>
|
|
public static class SpriteMerger
|
|
{
|
|
/// <summary>
|
|
/// 合并多个SpriteRenderer为一个Sprite
|
|
/// </summary>
|
|
/// <param name="renderers">要合并的SpriteRenderer数组</param>
|
|
/// <param name="textureWidth">输出纹理宽度(像素),0则自动计算</param>
|
|
/// <param name="textureHeight">输出纹理高度(像素),0则自动计算</param>
|
|
/// <param name="pixelsPerUnit">Sprite的pixelsPerUnit</param>
|
|
/// <returns>合并结果,包含Sprite和世界位置信息</returns>
|
|
public static MergeResult MergeSpriteRenderers(
|
|
SpriteRenderer[] renderers,
|
|
int textureWidth = 0,
|
|
int textureHeight = 0,
|
|
float pixelsPerUnit = 100f)
|
|
{
|
|
if (renderers == null || renderers.Length == 0)
|
|
{
|
|
Debug.LogError("SpriteRenderer数组为空");
|
|
return new MergeResult();
|
|
}
|
|
|
|
// 过滤掉无效的renderer
|
|
var validRenderers = renderers.Where(r => r != null && r.sprite != null).ToArray();
|
|
if (validRenderers.Length == 0)
|
|
{
|
|
Debug.LogError("没有有效的SpriteRenderer");
|
|
return new MergeResult();
|
|
}
|
|
|
|
// 1. 计算世界空间包围盒
|
|
Bounds worldBounds = CalculateWorldBounds(validRenderers);
|
|
|
|
|
|
|
|
// 2. 确定纹理尺寸
|
|
if (textureWidth <= 0 || textureHeight <= 0)
|
|
{
|
|
// 根据包围盒自动计算尺寸,保持宽高比
|
|
float aspect = worldBounds.size.x / worldBounds.size.y;
|
|
if (aspect > 1f)
|
|
{
|
|
textureWidth = Mathf.NextPowerOfTwo(Mathf.CeilToInt(worldBounds.size.x * pixelsPerUnit));
|
|
textureHeight = Mathf.NextPowerOfTwo(Mathf.CeilToInt(textureWidth / aspect));
|
|
}
|
|
else
|
|
{
|
|
textureHeight = Mathf.NextPowerOfTwo(Mathf.CeilToInt(worldBounds.size.y * pixelsPerUnit));
|
|
textureWidth = Mathf.NextPowerOfTwo(Mathf.CeilToInt(textureHeight * aspect));
|
|
}
|
|
}
|
|
|
|
// 3. 创建RenderTexture和临时Camera
|
|
RenderTexture renderTexture = null;
|
|
Camera tempCamera = null;
|
|
|
|
try
|
|
{
|
|
renderTexture = CreateRenderTexture(textureWidth, textureHeight);
|
|
tempCamera = CreateTempCamera(worldBounds, renderTexture);
|
|
|
|
// 4. 渲染SpriteRenderer到RenderTexture
|
|
RenderSpritesToTexture(validRenderers, tempCamera);
|
|
|
|
// 5. 读取为Texture2D
|
|
Texture2D texture2D = ReadRenderTexture(renderTexture);
|
|
|
|
// 6. 转换为Sprite
|
|
Sprite sprite = TextureToSprite(texture2D, pixelsPerUnit);
|
|
|
|
// 7. 返回结果
|
|
return new MergeResult
|
|
{
|
|
sprite = sprite,
|
|
worldPosition = worldBounds.center,
|
|
worldBounds = worldBounds
|
|
};
|
|
}
|
|
finally
|
|
{
|
|
// 清理资源
|
|
CleanupResources(tempCamera, renderTexture);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算世界空间包围盒
|
|
/// </summary>
|
|
private static Bounds CalculateWorldBounds(SpriteRenderer[] renderers)
|
|
{
|
|
if (renderers.Length == 0) return new Bounds();
|
|
|
|
bool first = true;
|
|
Bounds bounds = new Bounds();
|
|
|
|
foreach (var renderer in renderers)
|
|
{
|
|
if (renderer.sprite == null) continue;
|
|
|
|
// 获取Sprite的本地边界
|
|
Bounds spriteBounds = renderer.sprite.bounds;
|
|
|
|
// 获取世界位置(只考虑位置,不考虑旋转缩放)
|
|
Vector3 worldPos = renderer.transform.position;
|
|
|
|
// 计算世界空间中的四个角点
|
|
Vector3 center = worldPos + spriteBounds.center;
|
|
Vector3 size = spriteBounds.size;
|
|
|
|
Vector3[] corners = new Vector3[]
|
|
{
|
|
center + new Vector3(-size.x / 2, -size.y / 2, 0),
|
|
center + new Vector3(size.x / 2, -size.y / 2, 0),
|
|
center + new Vector3(-size.x / 2, size.y / 2, 0),
|
|
center + new Vector3(size.x / 2, size.y / 2, 0)
|
|
};
|
|
|
|
if (first)
|
|
{
|
|
bounds = new Bounds(corners[0], Vector3.zero);
|
|
first = false;
|
|
}
|
|
|
|
foreach (var corner in corners)
|
|
{
|
|
bounds.Encapsulate(corner);
|
|
}
|
|
}
|
|
|
|
return bounds;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建RenderTexture
|
|
/// </summary>
|
|
private static RenderTexture CreateRenderTexture(int width, int height)
|
|
{
|
|
RenderTexture rt = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32);
|
|
rt.antiAliasing = 1;
|
|
rt.filterMode = FilterMode.Bilinear;
|
|
rt.anisoLevel = 0;
|
|
rt.Create();
|
|
return rt;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建临时Camera
|
|
/// </summary>
|
|
private static Camera CreateTempCamera(Bounds worldBounds, RenderTexture targetTexture)
|
|
{
|
|
GameObject cameraObj = new GameObject("TempSpriteMergerCamera");
|
|
Camera cam = cameraObj.AddComponent<Camera>();
|
|
|
|
cam.orthographic = true;
|
|
cam.orthographicSize = worldBounds.size.y / 2f;
|
|
cam.nearClipPlane = 0.1f;
|
|
cam.farClipPlane = 1000f;
|
|
cam.clearFlags = CameraClearFlags.SolidColor;
|
|
cam.backgroundColor = new Color(0, 0, 0, 0); // 透明背景
|
|
cam.cullingMask = 0; // 不渲染任何Layer,我们手动绘制
|
|
cam.targetTexture = targetTexture;
|
|
|
|
// 设置相机位置在包围盒中心,面向Z轴正方向
|
|
cam.transform.position = new Vector3(
|
|
worldBounds.center.x,
|
|
worldBounds.center.y,
|
|
worldBounds.center.z - 100f
|
|
);
|
|
cam.transform.rotation = Quaternion.identity;
|
|
|
|
return cam;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 渲染SpriteRenderer到RenderTexture
|
|
/// </summary>
|
|
private static void RenderSpritesToTexture(
|
|
SpriteRenderer[] renderers,
|
|
Camera camera)
|
|
{
|
|
// 按sortingOrder排序
|
|
var sortedRenderers = renderers
|
|
.OrderBy(r => r.sortingLayerID)
|
|
.ThenBy(r => r.sortingOrder)
|
|
.ToArray();
|
|
|
|
// 使用CommandBuffer来确保立即渲染
|
|
CommandBuffer cmd = new CommandBuffer();
|
|
cmd.name = "SpriteMerger";
|
|
|
|
// 设置RenderTexture
|
|
cmd.SetRenderTarget(camera.targetTexture);
|
|
cmd.ClearRenderTarget(true, true, new Color(0, 0, 0, 0));
|
|
|
|
// 为每个SpriteRenderer创建Mesh并绘制
|
|
foreach (var renderer in sortedRenderers)
|
|
{
|
|
if (renderer.sprite == null) continue;
|
|
|
|
Mesh mesh = CreateSpriteMesh(renderer);
|
|
if (mesh == null) continue;
|
|
|
|
Material material = renderer.sharedMaterial != null ? renderer.sharedMaterial : GetDefaultSpriteMaterial();
|
|
if (material == null) continue;
|
|
|
|
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
|
|
renderer.GetPropertyBlock(mpb);
|
|
|
|
// 应用颜色
|
|
Color color = renderer.color;
|
|
mpb.SetColor("_Color", color);
|
|
|
|
// 使用单位矩阵(顶点已经是世界空间坐标)
|
|
Matrix4x4 matrix = Matrix4x4.identity;
|
|
|
|
// 绘制Mesh到CommandBuffer
|
|
cmd.DrawMesh(mesh, matrix, material, 0, 0, mpb);
|
|
}
|
|
|
|
// 执行CommandBuffer
|
|
Graphics.ExecuteCommandBuffer(cmd);
|
|
cmd.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 为SpriteRenderer创建Mesh(使用世界空间坐标)
|
|
/// </summary>
|
|
private static Mesh CreateSpriteMesh(SpriteRenderer renderer)
|
|
{
|
|
Sprite sprite = renderer.sprite;
|
|
if (sprite == null) return null;
|
|
|
|
// 获取Sprite的UV和三角形信息
|
|
Vector2[] uvs = sprite.uv;
|
|
ushort[] triangles = sprite.triangles;
|
|
|
|
// 计算世界位置
|
|
Vector3 worldPos = renderer.transform.position;
|
|
Vector3 spriteBoundsCenter = sprite.bounds.center;
|
|
Vector3 spriteBoundsSize = sprite.bounds.size;
|
|
|
|
// Sprite的世界中心位置
|
|
Vector3 spriteWorldCenter = worldPos + spriteBoundsCenter;
|
|
|
|
// 计算Sprite的四个角点(相对于sprite中心,单位:世界单位)
|
|
float halfWidth = spriteBoundsSize.x / 2f;
|
|
float halfHeight = spriteBoundsSize.y / 2f;
|
|
|
|
// 构建四个角的世界坐标
|
|
Vector3[] worldCorners = new Vector3[]
|
|
{
|
|
spriteWorldCenter + new Vector3(-halfWidth, -halfHeight, 0), // 左下
|
|
spriteWorldCenter + new Vector3(-halfWidth, halfHeight, 0), // 左上
|
|
spriteWorldCenter + new Vector3(halfWidth, halfHeight, 0), // 右上
|
|
spriteWorldCenter + new Vector3(halfWidth, -halfHeight, 0) // 右下
|
|
};
|
|
|
|
// 创建顶点数组(使用UV数量,根据UV映射到正确的角点)
|
|
Vector3[] vertices = new Vector3[uvs.Length];
|
|
|
|
// 将UV坐标映射到世界坐标的角点
|
|
for (int i = 0; i < uvs.Length; i++)
|
|
{
|
|
Vector2 uv = uvs[i];
|
|
|
|
// 找到最接近的角点UV
|
|
int cornerIndex = 0;
|
|
float minDist = float.MaxValue;
|
|
Vector2[] cornerUVs = new Vector2[]
|
|
{
|
|
new Vector2(0, 0), // 左下
|
|
new Vector2(0, 1), // 左上
|
|
new Vector2(1, 1), // 右上
|
|
new Vector2(1, 0) // 右下
|
|
};
|
|
|
|
for (int j = 0; j < cornerUVs.Length; j++)
|
|
{
|
|
float dist = Vector2.Distance(uv, cornerUVs[j]);
|
|
if (dist < minDist)
|
|
{
|
|
minDist = dist;
|
|
cornerIndex = j;
|
|
}
|
|
}
|
|
|
|
// 使用世界空间坐标
|
|
vertices[i] = worldCorners[cornerIndex];
|
|
}
|
|
|
|
// 创建Mesh
|
|
Mesh mesh = new Mesh();
|
|
mesh.vertices = vertices;
|
|
mesh.uv = uvs;
|
|
mesh.triangles = System.Array.ConvertAll(triangles, x => (int)x);
|
|
mesh.RecalculateNormals();
|
|
mesh.RecalculateBounds();
|
|
|
|
return mesh;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取默认Sprite材质
|
|
/// </summary>
|
|
private static Material GetDefaultSpriteMaterial()
|
|
{
|
|
// 使用Unity内置的Sprites/Default材质
|
|
return new Material(Shader.Find("Sprites/Default"));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从RenderTexture读取像素数据
|
|
/// </summary>
|
|
private static Texture2D ReadRenderTexture(RenderTexture renderTexture)
|
|
{
|
|
RenderTexture previousActive = RenderTexture.active;
|
|
RenderTexture.active = renderTexture;
|
|
|
|
Texture2D texture2D = new Texture2D(
|
|
renderTexture.width,
|
|
renderTexture.height,
|
|
TextureFormat.RGBA32,
|
|
false
|
|
);
|
|
|
|
texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
|
|
texture2D.Apply();
|
|
|
|
RenderTexture.active = previousActive;
|
|
|
|
return texture2D;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将Texture2D转换为Sprite
|
|
/// </summary>
|
|
private static Sprite TextureToSprite(Texture2D texture, float pixelsPerUnit)
|
|
{
|
|
Sprite sprite = Sprite.Create(
|
|
texture,
|
|
new Rect(0, 0, texture.width, texture.height),
|
|
new Vector2(0.5f, 0.5f), // pivot在中心
|
|
pixelsPerUnit
|
|
);
|
|
|
|
return sprite;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清理临时资源
|
|
/// </summary>
|
|
private static void CleanupResources(Camera camera, RenderTexture renderTexture)
|
|
{
|
|
if (camera != null)
|
|
{
|
|
if (camera.gameObject != null)
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(camera.gameObject);
|
|
}
|
|
}
|
|
|
|
if (renderTexture != null)
|
|
{
|
|
renderTexture.Release();
|
|
UnityEngine.Object.DestroyImmediate(renderTexture);
|
|
}
|
|
}
|
|
|
|
#region 异步版本
|
|
|
|
/// <summary>
|
|
/// 异步合并多个SpriteRenderer为一个Sprite(协程版本)
|
|
/// </summary>
|
|
/// <param name="renderers">要合并的SpriteRenderer数组</param>
|
|
/// <param name="onComplete">完成回调</param>
|
|
/// <param name="textureWidth">输出纹理宽度(像素),0则自动计算</param>
|
|
/// <param name="textureHeight">输出纹理高度(像素),0则自动计算</param>
|
|
/// <param name="pixelsPerUnit">Sprite的pixelsPerUnit</param>
|
|
/// <param name="spritesPerFrame">每帧处理的Sprite数量,用于控制性能</param>
|
|
/// <returns>协程</returns>
|
|
public static IEnumerator MergeSpriteRenderersAsync(
|
|
SpriteRenderer[] renderers,
|
|
Action<MergeResult> onComplete,
|
|
int textureWidth = 0,
|
|
int textureHeight = 0,
|
|
float pixelsPerUnit = 100f,
|
|
int spritesPerFrame = 5)
|
|
{
|
|
if (renderers == null || renderers.Length == 0)
|
|
{
|
|
Debug.LogError("SpriteRenderer数组为空");
|
|
onComplete?.Invoke(new MergeResult());
|
|
yield break;
|
|
}
|
|
|
|
// 过滤掉无效的renderer
|
|
var validRenderers = renderers.Where(r => r != null && r.sprite != null).ToArray();
|
|
if (validRenderers.Length == 0)
|
|
{
|
|
Debug.LogError("没有有效的SpriteRenderer");
|
|
onComplete?.Invoke(new MergeResult());
|
|
yield break;
|
|
}
|
|
|
|
// 1. 计算世界空间包围盒(分帧执行)
|
|
Bounds worldBounds = new Bounds();
|
|
bool boundsCalculated = false;
|
|
yield return CalculateWorldBoundsAsync(validRenderers, result =>
|
|
{
|
|
worldBounds = result;
|
|
boundsCalculated = true;
|
|
});
|
|
|
|
while (!boundsCalculated)
|
|
{
|
|
yield return null;
|
|
}
|
|
|
|
// 2. 确定纹理尺寸
|
|
if (textureWidth <= 0 || textureHeight <= 0)
|
|
{
|
|
float aspect = worldBounds.size.x / worldBounds.size.y;
|
|
if (aspect > 1f)
|
|
{
|
|
textureWidth = Mathf.NextPowerOfTwo(Mathf.CeilToInt(worldBounds.size.x * pixelsPerUnit));
|
|
textureHeight = Mathf.NextPowerOfTwo(Mathf.CeilToInt(textureWidth / aspect));
|
|
}
|
|
else
|
|
{
|
|
textureHeight = Mathf.NextPowerOfTwo(Mathf.CeilToInt(worldBounds.size.y * pixelsPerUnit));
|
|
textureWidth = Mathf.NextPowerOfTwo(Mathf.CeilToInt(textureHeight * aspect));
|
|
}
|
|
}
|
|
|
|
// 3. 创建RenderTexture和临时Camera
|
|
RenderTexture renderTexture = null;
|
|
Camera tempCamera = null;
|
|
|
|
try
|
|
{
|
|
renderTexture = CreateRenderTexture(textureWidth, textureHeight);
|
|
tempCamera = CreateTempCamera(worldBounds, renderTexture);
|
|
|
|
// 4. 异步渲染SpriteRenderer到RenderTexture(分帧执行)
|
|
yield return RenderSpritesToTextureAsync(validRenderers, tempCamera, spritesPerFrame);
|
|
|
|
// 5. 读取为Texture2D(分帧执行)
|
|
Texture2D texture2D = null;
|
|
bool textureRead = false;
|
|
yield return ReadRenderTextureAsync(renderTexture, result =>
|
|
{
|
|
texture2D = result;
|
|
textureRead = true;
|
|
});
|
|
|
|
while (!textureRead)
|
|
{
|
|
yield return null;
|
|
}
|
|
|
|
// 6. 转换为Sprite
|
|
Sprite sprite = TextureToSprite(texture2D, pixelsPerUnit);
|
|
|
|
// 7. 返回结果
|
|
onComplete?.Invoke(new MergeResult
|
|
{
|
|
sprite = sprite,
|
|
worldPosition = worldBounds.center,
|
|
worldBounds = worldBounds
|
|
});
|
|
}
|
|
finally
|
|
{
|
|
// 清理资源
|
|
CleanupResources(tempCamera, renderTexture);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 异步计算世界空间包围盒
|
|
/// </summary>
|
|
private static IEnumerator CalculateWorldBoundsAsync(SpriteRenderer[] renderers, Action<Bounds> onComplete)
|
|
{
|
|
if (renderers.Length == 0)
|
|
{
|
|
onComplete?.Invoke(new Bounds());
|
|
yield break;
|
|
}
|
|
|
|
bool first = true;
|
|
Bounds bounds = new Bounds();
|
|
|
|
// 分帧计算,每帧处理一部分
|
|
int processed = 0;
|
|
int batchSize = 10; // 每帧处理的renderer数量
|
|
|
|
while (processed < renderers.Length)
|
|
{
|
|
int endIndex = Mathf.Min(processed + batchSize, renderers.Length);
|
|
|
|
for (int i = processed; i < endIndex; i++)
|
|
{
|
|
var renderer = renderers[i];
|
|
if (renderer.sprite == null) continue;
|
|
|
|
Bounds spriteBounds = renderer.sprite.bounds;
|
|
Vector3 worldPos = renderer.transform.position;
|
|
Vector3 center = worldPos + spriteBounds.center;
|
|
Vector3 size = spriteBounds.size;
|
|
|
|
Vector3[] corners = new Vector3[]
|
|
{
|
|
center + new Vector3(-size.x / 2, -size.y / 2, 0),
|
|
center + new Vector3(size.x / 2, -size.y / 2, 0),
|
|
center + new Vector3(-size.x / 2, size.y / 2, 0),
|
|
center + new Vector3(size.x / 2, size.y / 2, 0)
|
|
};
|
|
|
|
if (first)
|
|
{
|
|
bounds = new Bounds(corners[0], Vector3.zero);
|
|
first = false;
|
|
}
|
|
|
|
foreach (var corner in corners)
|
|
{
|
|
bounds.Encapsulate(corner);
|
|
}
|
|
}
|
|
|
|
processed = endIndex;
|
|
yield return null; // 每批处理完后让出一帧
|
|
}
|
|
|
|
onComplete?.Invoke(bounds);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 异步渲染SpriteRenderer到RenderTexture(分帧执行)
|
|
/// </summary>
|
|
private static IEnumerator RenderSpritesToTextureAsync(
|
|
SpriteRenderer[] renderers,
|
|
Camera camera,
|
|
int spritesPerFrame)
|
|
{
|
|
// 按sortingOrder排序
|
|
var sortedRenderers = renderers
|
|
.OrderBy(r => r.sortingLayerID)
|
|
.ThenBy(r => r.sortingOrder)
|
|
.ToArray();
|
|
|
|
// 使用CommandBuffer来确保立即渲染
|
|
CommandBuffer cmd = new CommandBuffer();
|
|
cmd.name = "SpriteMerger";
|
|
|
|
// 设置RenderTexture
|
|
cmd.SetRenderTarget(camera.targetTexture);
|
|
cmd.ClearRenderTarget(true, true, new Color(0, 0, 0, 0));
|
|
|
|
// 预先创建所有Mesh(分帧执行)
|
|
List<Mesh> meshes = new List<Mesh>();
|
|
List<Material> materials = new List<Material>();
|
|
List<MaterialPropertyBlock> mpbs = new List<MaterialPropertyBlock>();
|
|
|
|
int processed = 0;
|
|
while (processed < sortedRenderers.Length)
|
|
{
|
|
int endIndex = Mathf.Min(processed + spritesPerFrame, sortedRenderers.Length);
|
|
|
|
for (int i = processed; i < endIndex; i++)
|
|
{
|
|
var renderer = sortedRenderers[i];
|
|
if (renderer.sprite == null) continue;
|
|
|
|
Mesh mesh = CreateSpriteMesh(renderer);
|
|
if (mesh == null) continue;
|
|
|
|
Material material = renderer.sharedMaterial != null ? renderer.sharedMaterial : GetDefaultSpriteMaterial();
|
|
if (material == null) continue;
|
|
|
|
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
|
|
renderer.GetPropertyBlock(mpb);
|
|
Color color = renderer.color;
|
|
mpb.SetColor("_Color", color);
|
|
|
|
meshes.Add(mesh);
|
|
materials.Add(material);
|
|
mpbs.Add(mpb);
|
|
}
|
|
|
|
processed = endIndex;
|
|
yield return null; // 每批处理完后让出一帧
|
|
}
|
|
|
|
// 一次性绘制所有Mesh
|
|
for (int i = 0; i < meshes.Count; i++)
|
|
{
|
|
cmd.DrawMesh(meshes[i], Matrix4x4.identity, materials[i], 0, 0, mpbs[i]);
|
|
}
|
|
|
|
// 执行CommandBuffer
|
|
Graphics.ExecuteCommandBuffer(cmd);
|
|
cmd.Dispose();
|
|
|
|
// 清理临时创建的Mesh(避免内存泄漏)
|
|
foreach (var mesh in meshes)
|
|
{
|
|
if (mesh != null)
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(mesh);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 异步从RenderTexture读取像素数据
|
|
/// </summary>
|
|
private static IEnumerator ReadRenderTextureAsync(RenderTexture renderTexture, Action<Texture2D> onComplete)
|
|
{
|
|
// ReadPixels是同步操作,但我们可以分块读取来减少卡顿
|
|
RenderTexture previousActive = RenderTexture.active;
|
|
RenderTexture.active = renderTexture;
|
|
|
|
Texture2D texture2D = new Texture2D(
|
|
renderTexture.width,
|
|
renderTexture.height,
|
|
TextureFormat.RGBA32,
|
|
false
|
|
);
|
|
|
|
// 读取像素(这个操作可能比较耗时)
|
|
texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
|
|
|
|
yield return null; // 让出一帧
|
|
|
|
texture2D.Apply();
|
|
|
|
RenderTexture.active = previousActive;
|
|
|
|
onComplete?.Invoke(texture2D);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 启动异步合并协程(需要MonoBehaviour实例)
|
|
/// </summary>
|
|
/// <param name="monoBehaviour">用于启动协程的MonoBehaviour实例</param>
|
|
/// <param name="renderers">要合并的SpriteRenderer数组</param>
|
|
/// <param name="onComplete">完成回调</param>
|
|
/// <param name="textureWidth">输出纹理宽度(像素),0则自动计算</param>
|
|
/// <param name="textureHeight">输出纹理高度(像素),0则自动计算</param>
|
|
/// <param name="pixelsPerUnit">Sprite的pixelsPerUnit</param>
|
|
/// <param name="spritesPerFrame">每帧处理的Sprite数量</param>
|
|
public static void StartMergeAsync(
|
|
MonoBehaviour monoBehaviour,
|
|
SpriteRenderer[] renderers,
|
|
Action<MergeResult> onComplete,
|
|
int textureWidth = 0,
|
|
int textureHeight = 0,
|
|
float pixelsPerUnit = 100f,
|
|
int spritesPerFrame = 5)
|
|
{
|
|
if (monoBehaviour == null)
|
|
{
|
|
Debug.LogError("MonoBehaviour实例为空,无法启动协程");
|
|
onComplete?.Invoke(new MergeResult());
|
|
return;
|
|
}
|
|
|
|
monoBehaviour.StartCoroutine(MergeSpriteRenderersAsync(
|
|
renderers,
|
|
onComplete,
|
|
textureWidth,
|
|
textureHeight,
|
|
pixelsPerUnit,
|
|
spritesPerFrame
|
|
));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 使用MonoCenter启动异步合并协程(推荐)
|
|
/// </summary>
|
|
public static void StartMergeAsyncWithMonoCenter(
|
|
SpriteRenderer[] renderers,
|
|
Action<MergeResult> onComplete,
|
|
int textureWidth = 0,
|
|
int textureHeight = 0,
|
|
float pixelsPerUnit = 100f,
|
|
int spritesPerFrame = 5)
|
|
{
|
|
if (MonoCenter.Mono == null)
|
|
{
|
|
Debug.LogError("MonoCenter未初始化,无法启动协程");
|
|
onComplete?.Invoke(new MergeResult());
|
|
return;
|
|
}
|
|
|
|
StartMergeAsync(
|
|
MonoCenter.Mono,
|
|
renderers,
|
|
onComplete,
|
|
textureWidth,
|
|
textureHeight,
|
|
pixelsPerUnit,
|
|
spritesPerFrame
|
|
);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
|