2025-10-13 11:10:52 +08:00
|
|
|
using UnityEngine;
|
|
|
|
|
using System.Collections.Generic;
|
2025-10-15 20:13:48 +08:00
|
|
|
using System.Linq;
|
2025-10-13 11:10:52 +08:00
|
|
|
|
|
|
|
|
public class ShadowColliderGenerator : MonoBehaviour
|
|
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
[Header("References")]
|
|
|
|
|
public Camera shadowCamera;
|
|
|
|
|
public RenderTexture shadowRT;
|
|
|
|
|
public PolygonCollider2D polygonCollider;
|
|
|
|
|
|
|
|
|
|
[Header("Settings")]
|
|
|
|
|
[Range(0f, 1f)]
|
|
|
|
|
public float shadowThreshold = 0.3f;
|
|
|
|
|
public int gridSize = 2; // 网格大小,越小越精确
|
|
|
|
|
public float simplifyTolerance = 1f;
|
2025-10-13 11:10:52 +08:00
|
|
|
|
|
|
|
|
private Texture2D processedTexture;
|
|
|
|
|
|
|
|
|
|
void Start()
|
|
|
|
|
{
|
|
|
|
|
if (polygonCollider == null)
|
2025-10-15 20:13:48 +08:00
|
|
|
polygonCollider = GetComponent<PolygonCollider2D>();
|
|
|
|
|
|
|
|
|
|
processedTexture = new Texture2D(shadowRT.width, shadowRT.height, TextureFormat.R8, false);
|
|
|
|
|
GenerateCollider();
|
2025-10-13 11:10:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Update()
|
|
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
if (Input.GetKeyDown(KeyCode.Space))
|
|
|
|
|
{
|
|
|
|
|
GenerateCollider();
|
|
|
|
|
}
|
2025-10-13 11:10:52 +08:00
|
|
|
}
|
|
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
[ContextMenu("Generate Collider")]
|
|
|
|
|
public void GenerateCollider()
|
2025-10-13 11:10:52 +08:00
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
if (shadowCamera == null || shadowRT == null) return;
|
|
|
|
|
|
|
|
|
|
// 渲染阴影
|
2025-10-13 11:10:52 +08:00
|
|
|
shadowCamera.Render();
|
|
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
// 读取纹理
|
2025-10-13 11:10:52 +08:00
|
|
|
RenderTexture.active = shadowRT;
|
|
|
|
|
processedTexture.ReadPixels(new Rect(0, 0, shadowRT.width, shadowRT.height), 0, 0);
|
|
|
|
|
processedTexture.Apply();
|
|
|
|
|
RenderTexture.active = null;
|
|
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
// 生成轮廓
|
|
|
|
|
List<Vector2> contour = GenerateContourFromTexture();
|
2025-10-13 11:10:52 +08:00
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
if (contour.Count > 2)
|
2025-10-13 11:10:52 +08:00
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
List<Vector2> worldPoints = ConvertToWorldCoordinates(contour);
|
2025-10-13 11:10:52 +08:00
|
|
|
polygonCollider.SetPath(0, worldPoints);
|
2025-10-15 20:13:48 +08:00
|
|
|
Debug.Log($"Collider generated with {worldPoints.Count} points");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
polygonCollider.pathCount = 0;
|
|
|
|
|
Debug.Log("No contour generated");
|
2025-10-13 11:10:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
List<Vector2> GenerateContourFromTexture()
|
2025-10-13 11:10:52 +08:00
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
int width = processedTexture.width;
|
|
|
|
|
int height = processedTexture.height;
|
|
|
|
|
|
|
|
|
|
// 创建二值化网格
|
|
|
|
|
bool[,] grid = CreateBinaryGrid(width, height);
|
2025-10-13 11:10:52 +08:00
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
// 找到所有边界点
|
|
|
|
|
List<Vector2> boundaryPoints = FindBoundaryPoints(grid);
|
2025-10-13 11:10:52 +08:00
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
// 连接边界点形成轮廓
|
|
|
|
|
List<Vector2> contour = ConnectBoundaryPoints(boundaryPoints);
|
|
|
|
|
|
|
|
|
|
return contour;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool[,] CreateBinaryGrid(int width, int height)
|
|
|
|
|
{
|
|
|
|
|
bool[,] grid = new bool[width / gridSize, height / gridSize];
|
|
|
|
|
|
|
|
|
|
for (int x = 0; x < grid.GetLength(0); x++)
|
2025-10-13 11:10:52 +08:00
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
for (int y = 0; y < grid.GetLength(1); y++)
|
2025-10-13 11:10:52 +08:00
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
int texX = x * gridSize;
|
|
|
|
|
int texY = y * gridSize;
|
|
|
|
|
Color pixel = processedTexture.GetPixel(texX, texY);
|
|
|
|
|
grid[x, y] = pixel.grayscale < shadowThreshold;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return grid;
|
|
|
|
|
}
|
2025-10-13 11:10:52 +08:00
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
List<Vector2> FindBoundaryPoints(bool[,] grid)
|
|
|
|
|
{
|
|
|
|
|
List<Vector2> boundaryPoints = new List<Vector2>();
|
|
|
|
|
int width = grid.GetLength(0);
|
|
|
|
|
int height = grid.GetLength(1);
|
|
|
|
|
|
|
|
|
|
for (int x = 0; x < width; x++)
|
|
|
|
|
{
|
|
|
|
|
for (int y = 0; y < height; y++)
|
|
|
|
|
{
|
|
|
|
|
if (grid[x, y])
|
2025-10-13 11:10:52 +08:00
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
// 检查是否是边界(有非阴影像素邻居)
|
|
|
|
|
bool isBoundary = false;
|
|
|
|
|
|
|
|
|
|
// 检查4邻域
|
|
|
|
|
if (x == 0 || !grid[x - 1, y]) isBoundary = true;
|
|
|
|
|
else if (x == width - 1 || !grid[x + 1, y]) isBoundary = true;
|
|
|
|
|
else if (y == 0 || !grid[x, y - 1]) isBoundary = true;
|
|
|
|
|
else if (y == height - 1 || !grid[x, y + 1]) isBoundary = true;
|
|
|
|
|
|
|
|
|
|
if (isBoundary)
|
|
|
|
|
{
|
|
|
|
|
boundaryPoints.Add(new Vector2(x * gridSize, y * gridSize));
|
|
|
|
|
}
|
2025-10-13 11:10:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
Debug.Log($"Found {boundaryPoints.Count} boundary points");
|
|
|
|
|
return boundaryPoints;
|
2025-10-13 11:10:52 +08:00
|
|
|
}
|
|
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
List<Vector2> ConnectBoundaryPoints(List<Vector2> points)
|
2025-10-13 11:10:52 +08:00
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
if (points.Count < 3) return points;
|
|
|
|
|
|
|
|
|
|
List<Vector2> contour = new List<Vector2>();
|
|
|
|
|
List<Vector2> remaining = new List<Vector2>(points);
|
|
|
|
|
|
|
|
|
|
// 找到最左边的点作为起点
|
|
|
|
|
Vector2 start = remaining.OrderBy(p => p.x).First();
|
|
|
|
|
contour.Add(start);
|
|
|
|
|
remaining.Remove(start);
|
|
|
|
|
|
|
|
|
|
Vector2 current = start;
|
|
|
|
|
Vector2 previousDirection = Vector2.right;
|
2025-10-13 11:10:52 +08:00
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
int maxIterations = points.Count * 2;
|
|
|
|
|
int iterations = 0;
|
|
|
|
|
|
|
|
|
|
while (remaining.Count > 0 && iterations < maxIterations)
|
2025-10-13 11:10:52 +08:00
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
iterations++;
|
|
|
|
|
|
|
|
|
|
// 找到与当前方向最一致的下一个点
|
|
|
|
|
float bestScore = float.MinValue;
|
|
|
|
|
Vector2 bestPoint = Vector2.zero;
|
|
|
|
|
int bestIndex = -1;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < remaining.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
Vector2 toPoint = (remaining[i] - current).normalized;
|
|
|
|
|
float distance = Vector2.Distance(current, remaining[i]);
|
|
|
|
|
|
|
|
|
|
// 评分:方向一致性 + 距离权重
|
|
|
|
|
float directionScore = Vector2.Dot(previousDirection, toPoint);
|
|
|
|
|
float distanceScore = 1f / (distance + 0.1f);
|
|
|
|
|
float totalScore = directionScore + distanceScore * 0.1f;
|
|
|
|
|
|
|
|
|
|
if (totalScore > bestScore && distance < 50f) // 距离限制
|
|
|
|
|
{
|
|
|
|
|
bestScore = totalScore;
|
|
|
|
|
bestPoint = remaining[i];
|
|
|
|
|
bestIndex = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bestIndex >= 0)
|
|
|
|
|
{
|
|
|
|
|
previousDirection = (bestPoint - current).normalized;
|
|
|
|
|
contour.Add(bestPoint);
|
|
|
|
|
remaining.RemoveAt(bestIndex);
|
|
|
|
|
current = bestPoint;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确保轮廓闭合
|
|
|
|
|
if (contour.Count > 2 && Vector2.Distance(contour[0], contour[contour.Count - 1]) > 30f)
|
|
|
|
|
{
|
|
|
|
|
contour.Add(contour[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 简化轮廓
|
|
|
|
|
contour = SimplifyContour(contour);
|
|
|
|
|
|
|
|
|
|
Debug.Log($"Connected {contour.Count} points into contour");
|
|
|
|
|
return contour;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<Vector2> SimplifyContour(List<Vector2> contour)
|
|
|
|
|
{
|
|
|
|
|
if (contour.Count <= 3) return contour;
|
|
|
|
|
|
|
|
|
|
List<Vector2> simplified = new List<Vector2>();
|
|
|
|
|
simplified.Add(contour[0]);
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < contour.Count - 1; i++)
|
2025-10-13 11:10:52 +08:00
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
// 如果角度变化太大,保留这个点
|
|
|
|
|
if (i > 1)
|
|
|
|
|
{
|
|
|
|
|
Vector2 prevDir = (contour[i] - contour[i - 1]).normalized;
|
|
|
|
|
Vector2 nextDir = (contour[i + 1] - contour[i]).normalized;
|
|
|
|
|
float angle = Vector2.Angle(prevDir, nextDir);
|
|
|
|
|
|
|
|
|
|
if (angle > 30f) // 角度阈值
|
|
|
|
|
{
|
|
|
|
|
simplified.Add(contour[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-13 11:10:52 +08:00
|
|
|
}
|
|
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
simplified.Add(contour[contour.Count - 1]);
|
|
|
|
|
|
|
|
|
|
return simplified;
|
2025-10-13 11:10:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<Vector2> ConvertToWorldCoordinates(List<Vector2> texturePoints)
|
|
|
|
|
{
|
|
|
|
|
List<Vector2> worldPoints = new List<Vector2>();
|
|
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
foreach (Vector2 texCoord in texturePoints)
|
2025-10-13 11:10:52 +08:00
|
|
|
{
|
2025-10-15 20:13:48 +08:00
|
|
|
Vector3 viewportPos = new Vector3(
|
|
|
|
|
texCoord.x / processedTexture.width,
|
|
|
|
|
texCoord.y / processedTexture.height,
|
|
|
|
|
shadowCamera.nearClipPlane
|
2025-10-13 11:10:52 +08:00
|
|
|
);
|
|
|
|
|
|
2025-10-15 20:13:48 +08:00
|
|
|
Vector3 worldPos = shadowCamera.ViewportToWorldPoint(viewportPos);
|
|
|
|
|
worldPoints.Add(new Vector2(worldPos.x, worldPos.y));
|
2025-10-13 11:10:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return worldPoints;
|
|
|
|
|
}
|
2025-10-15 20:13:48 +08:00
|
|
|
|
|
|
|
|
// 调试:显示边界点
|
|
|
|
|
void OnDrawGizmosSelected()
|
|
|
|
|
{
|
|
|
|
|
if (processedTexture == null) return;
|
|
|
|
|
|
|
|
|
|
Gizmos.color = Color.red;
|
|
|
|
|
|
|
|
|
|
// 显示边界点
|
|
|
|
|
bool[,] grid = CreateBinaryGrid(processedTexture.width, processedTexture.height);
|
|
|
|
|
List<Vector2> boundaryPoints = FindBoundaryPoints(grid);
|
|
|
|
|
|
|
|
|
|
foreach (Vector2 point in boundaryPoints)
|
|
|
|
|
{
|
|
|
|
|
Vector3 viewportPos = new Vector3(
|
|
|
|
|
point.x / processedTexture.width,
|
|
|
|
|
point.y / processedTexture.height,
|
|
|
|
|
shadowCamera.nearClipPlane
|
|
|
|
|
);
|
|
|
|
|
Vector3 worldPos = shadowCamera.ViewportToWorldPoint(viewportPos);
|
|
|
|
|
Gizmos.DrawSphere(worldPos, 0.02f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 显示碰撞器轮廓
|
|
|
|
|
if (polygonCollider != null && polygonCollider.pathCount > 0)
|
|
|
|
|
{
|
|
|
|
|
Gizmos.color = Color.green;
|
|
|
|
|
Vector2[] path = polygonCollider.GetPath(0);
|
|
|
|
|
for (int i = 0; i < path.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
Vector3 start = path[i];
|
|
|
|
|
Vector3 end = path[(i + 1) % path.Length];
|
|
|
|
|
Gizmos.DrawLine(start, end);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-13 11:10:52 +08:00
|
|
|
}
|