Forest_Client/Forest/Assets/Scripts/Gameplay/Level/LevelUtils.cs

521 lines
19 KiB
C#

using System;
using System.Linq;
using UnityEngine;
using Framework.Constants;
using System.Collections.Generic;
namespace Gameplay.Level
{
/// <summary>
/// 用于检测整块木板和洞口位置关系 (并非木板的孔和洞的位置)
/// </summary>
public static class LevelUtils
{
/// <summary>
/// 根据木板的洞决定层级顺序
/// </summary>
public static List<int> SortPlankLayer(List<List<int>> holesOfPlanksIndex)
{
List<int> elementCounts = holesOfPlanksIndex.Select(innerList => innerList.Count).ToList();
List<int> sortedIndexes = elementCounts
.Select((value, index) => new { Value = value, Index = index })
.OrderByDescending(item => item.Value)
.Select(item => item.Index)
.ToList();
return sortedIndexes;
}
public static bool IsCanAddThumbtack(Plank plank, Kong kong)
{
return IsNonOverlap(plank.Obj, kong.Obj) || IsOverlapCompletely(plank.Obj, kong.Obj);
}
public static bool IsOverlapCompletely(GameObject plank, GameObject hole)
{
return !IsIntersect(plank, hole) && JudgeContains(hole.transform.position, plank);
}
private static bool IsNonOverlap(GameObject plank, GameObject hole)
{
return !IsIntersect(plank, hole) && !JudgeContains(hole.transform.position, plank);
}
public static bool IsIntersect(GameObject plank, GameObject hole)
{
var isIntersect = false;
var vertices = GetVertices(plank);
var radius = GetRadius(hole);
for (var i = 0; i < vertices.Length; i++)
{
var p1 = vertices[i];
var p2 = vertices[(i + 1) % vertices.Length];
if (IsLineCircleIntersecting(p1, p2, hole.transform.position, radius))
{
isIntersect = true;
break;
}
}
return isIntersect;
}
public static List<Collider2D> GetTriggerColliders(GameObject obj, string layerMask)
{
List<Collider2D> hitColliders = new List<Collider2D>();
try
{
Collider2D collider = obj.GetComponent<Collider2D>();
ContactFilter2D contactFilter = new ContactFilter2D();
contactFilter.useTriggers = true;
contactFilter.SetLayerMask(LayerMask.GetMask(layerMask));
Physics2D.OverlapCollider(collider, contactFilter, hitColliders);
return hitColliders;
}
catch (Exception e)
{
DebugUtil.LogError("LevelUtils.GetTriggerColliders Fail. Obj: {0}, layerMask: {1}, Error:{2}", obj.name,
layerMask, e);
return hitColliders;
}
}
public static Collider2D GetTriggerColliderOfPlank(GameObject obj, Plank plank)
{
var hits = GetTriggerColliders(obj, "HoleOfPlank");
foreach (var hit in hits)
{
if (plank.HolesOfPlank.TryGetValue(hit.name, out var holeOfPlank))
{
return hit;
}
}
return null;
}
public static float GetRadius(GameObject obj)
{
var bounds = obj.GetComponent<SpriteRenderer>().bounds;
return bounds.extents.x;
}
private static Vector2[] GetVertices(GameObject obj)
{
SpriteRenderer spriteRenderer = obj.GetComponent<SpriteRenderer>();
Sprite sprite = spriteRenderer.sprite;
Bounds spriteBounds = sprite.bounds;
Vector2 bottomLeft = new Vector2(spriteBounds.min.x, spriteBounds.min.y);
Vector2 bottomRight = new Vector2(spriteBounds.max.x, spriteBounds.min.y);
Vector2 topLeft = new Vector2(spriteBounds.min.x, spriteBounds.max.y);
Vector2 topRight = new Vector2(spriteBounds.max.x, spriteBounds.max.y);
//var originalVertices = sprite.vertices; //所有顶点
var originalVertices = new[] { bottomLeft, bottomRight, topLeft, topRight };
Vector2 spriteCenter = sprite.bounds.center;
// 计算每个顶点相对于Sprite中心的角度
Dictionary<Vector3, float> vertexAngles = new Dictionary<Vector3, float>();
foreach (Vector3 vertex in originalVertices)
{
Vector2 vertexPosition = new Vector2(vertex.x, vertex.y);
Vector2 direction = vertexPosition - spriteCenter;
float angle = Mathf.Atan2(direction.y, direction.x);
vertexAngles.Add(vertex, angle);
}
Vector2[] sortedVertices = originalVertices.OrderBy(vertex => vertexAngles[vertex]).ToArray();
for (var i = 0; i < sortedVertices.Length; i++)
{
sortedVertices[i] = obj.transform.TransformPoint(sortedVertices[i]);
}
return sortedVertices;
}
private static bool IsLineCircleIntersecting(Vector2 lineStart, Vector2 lineEnd, Vector2 circleCenter,
float circleRadius)
{
float distance = 0f;
if (Math.Abs(lineStart.x - lineEnd.x) < float.Epsilon)
{
distance = Mathf.Abs(circleCenter.x - lineStart.x);
}
else if (Math.Abs(lineStart.y - lineEnd.y) < float.Epsilon)
{
distance = Mathf.Abs(circleCenter.y - lineStart.y);
}
else
{
float slope = (lineEnd.y - lineStart.y) / (lineEnd.x - lineStart.x);
float intercept = lineStart.y - slope * lineStart.x;
distance = Mathf.Abs(slope * circleCenter.x - circleCenter.y + intercept) /
Mathf.Sqrt(Mathf.Pow(slope, 2) + 1);
}
return !(distance >= circleRadius);
}
public static bool JudgeContains(Vector3 point, GameObject Obj)
{
RaycastHit2D[] hits = Physics2D.RaycastAll(point, Vector2.zero);
foreach (var hit in hits)
{
if (hit.collider != null && hit.collider.name == Obj.name)
{
return true;
}
}
return false;
}
public static List<Vector2Int> ConvertIndexToVector2Ints(List<int> indexList, int columns)
{
List<Vector2Int> coordinatesArray = new List<Vector2Int>();
foreach (int index in indexList)
{
int row = index / columns;
int column = index % columns;
Vector2Int coordinates = new Vector2Int(row, column);
coordinatesArray.Add(coordinates);
}
return coordinatesArray;
}
public static List<int> ConvertVector2IntsToIndex(List<Vector2Int> vectorList, int columns)
{
List<int> indexArray = new List<int>();
foreach (Vector2Int coordinates in vectorList)
{
int index = coordinates.x * columns + coordinates.y;
indexArray.Add(index);
}
return indexArray;
}
public static Vector2Int ConvertIndexToVector2Int(int index, int columns)
{
return new Vector2Int(index / columns, index % columns);
}
public static int ConvertVector2IntToIndex(Vector2Int pos, int columns)
{
return pos.x * columns + pos.y;
}
public static Color GetRgbaColor(string strColor)
{
var color = Convert.ToUInt32(strColor, 16);
return new Color((color >> 24 & 0xFF) / 255f, (color >> 16 & 0xFF) / 255f,
(color >> 8 & 0xFF) / 255f, (color & 0xFF) / 255f);
}
/// <summary>
/// 根据行数和方向创造hole的位置
/// </summary>
public static List<float> GeneratePositions(int count, int direction)
{
try
{
var positions = new List<float>(count);
var spacing = 2 * LevelConstants.Radius + LevelConstants.Spacing;
var pos = 0 -
(count % 2 != 0
? count / 2
: (LevelConstants.Radius + LevelConstants.Spacing / 2) + (count / 2 - 1)) * spacing;
if (direction.Equals(-1))
{
pos -= LevelConstants.Offset;
}
pos *= direction;
for (var i = 0; i < count; i++)
{
positions.Add(pos);
pos += spacing * direction;
}
return positions;
}
catch (Exception e)
{
DebugUtil.LogError("LevelUtils.GeneratePositions Fail. Error: {0}", e);
return new List<float>();
}
}
/// <summary>
///得到可以开孔的位置
/// </summary>
public static List<Vector3> GetAdditionalAddedHolesPos(Vector3 firstPos, int count)
{
var positions = new List<Vector3>(count);
for (var i = 0; i < count; i++)
{
var pos = new Vector3(firstPos.x + i * (LevelConstants.Spacing + LevelConstants.Radius * 2),
firstPos.y - LevelConstants.Spacing - LevelConstants.Radius * 2,
firstPos.z);
positions.Add(pos);
}
return positions;
}
public static string GetPlankName(LevelData.PlankType plankType,
LevelData.SquareNormType squareNormType)
{
if (!squareNormType.Equals(LevelData.SquareNormType.None))
return plankType + squareNormType.ToString();
return plankType.ToString();
}
#region 横竖方形生成
private static Vector3 GetSquarePlankPosition(List<int> holeIndex,
Dictionary<string, Kong> holeDic, LevelData.SquareNormType squareNormType,
bool isHorizontal = true)
{
try
{
var limit = JudgeSquareIndexLimit(squareNormType);
if (limit == holeIndex.Count - 1)
{
var realIndex = holeIndex[limit / 2];
var pos = holeDic[$"Kong{realIndex}"].Obj.transform.position;
float offsetValue = isHorizontal
? LevelConstants.Radius + LevelConstants.Spacing / 2
: -LevelConstants.Radius - LevelConstants.Spacing / 2;
float x = limit % 2 != 0 ? pos.x + offsetValue : pos.x;
float y = limit % 2 != 0 ? pos.y + offsetValue : pos.y;
var center = isHorizontal ? new Vector3(x, pos.y, pos.z) : new Vector3(pos.x, y, pos.z);
return center;
}
else
{
if (holeIndex.Count % 2 == 0)
{
var realIndex = holeIndex[(holeIndex.Count - 1) / 2];
var offset = (LevelConstants.Radius * 2 + LevelConstants.Spacing) * limit / 2;
var pos = holeDic[$"Kong{realIndex}"].Obj.transform.position;
var center = isHorizontal
? new Vector3(pos.x + offset, pos.y, pos.z)
: new Vector3(pos.x, pos.y - offset, pos.z);
return center;
}
else
{
var index = (holeIndex.Count - 1) / 2;
var realIndex = holeIndex[index];
var offset = limit % 2 != 0
? (LevelConstants.Radius + LevelConstants.Spacing) * limit / 2.0f
: 0;
var pos = holeDic[$"Kong{realIndex}"].Obj.transform.position;
var center = isHorizontal
? new Vector3(pos.x + offset, pos.y, pos.z)
: new Vector3(pos.x, pos.y - offset, pos.z);
return center;
}
}
}
catch (Exception e)
{
DebugUtil.LogError("LevelUtils.GetSquarePlankPosition Fail. Error: {0}", e);
return new Vector3();
}
}
#endregion
#region 多排方形
private static Vector3 GetMultiSquarePlankPosition(List<int> holeIndex,
Dictionary<string, Kong> holeDic)
{
var firstIndex = holeIndex[0];
var finalIndex = holeIndex[^1];
var firstPos = holeDic[$"Kong{firstIndex}"].Obj.transform.position;
var finalPos = holeDic[$"Kong{finalIndex}"].Obj.transform.position;
return (firstPos + finalPos) / 2;
}
#endregion
#region 圆形
private static Vector3 GetCirclePlankPosition(List<int> holeIndex,
Dictionary<string, Kong> holeDic)
{
return holeDic[$"Kong{holeIndex[0]}"].Obj.transform.position;
}
#endregion
#region 倾斜木板
private static Vector3 GetInclinedSquarePlankPosition(List<int> holeIndex, Dictionary<string, Kong> holeDic)
{
//两孔木板 限制为1
var limit = 1;
var realIndex = holeIndex[limit / 2];
var nextIndex = holeIndex[limit / 2 + 1];
var pos = holeDic[$"Kong{realIndex}"].Obj.transform.position;
var nextPos = holeDic[$"Kong{nextIndex}"].Obj.transform.position;
var offsetPos = new Vector3((pos.x - nextPos.x) / 2 + nextPos.x, (pos.y - nextPos.y) / 2 + nextPos.y,
pos.z);
return limit % 2 != 0 ? offsetPos : pos;
}
#endregion
#region 直角三角形
private static Vector3 GetRightTrianglePlankPosition(List<int> holeIndex, Dictionary<string, Kong> holeDic,
bool isLeft = true)
{
var pos = holeDic[$"Kong{holeIndex[0]}"].Obj.transform.position;
var offsetX = isLeft
? pos.x + LevelConstants.Radius + LevelConstants.SpacingOfRightTriangle
: pos.x - LevelConstants.Radius - LevelConstants.SpacingOfRightTriangle;
return new Vector3(offsetX, pos.y - LevelConstants.Radius - LevelConstants.SpacingOfRightTriangle, pos.z);
}
#endregion
#region 直角三角形_Down
private static Vector3 GetRightTrianglePlankPositionDown(List<int> holeIndex, Dictionary<string, Kong> holeDic,
bool isLeft = true)
{
var pos = holeDic[$"Kong{holeIndex[0]}"].Obj.transform.position;
var offsetX = isLeft
? pos.x + LevelConstants.Radius + LevelConstants.SpacingOfRightTriangle
: pos.x - LevelConstants.Radius - LevelConstants.SpacingOfRightTriangle;
return new Vector3(offsetX, pos.y + LevelConstants.Radius + LevelConstants.SpacingOfRightTriangle, pos.z);
}
#endregion
#region 倾斜三角
private static Vector3 GetInclinedTrianglePlankPositionUp(List<int> holeIndex, Dictionary<string, Kong> holeDic,
bool isUp = true)
{
var pos = holeDic[$"Kong{holeIndex[0]}"].Obj.transform.position;
float offsetValue = Mathf.Sqrt(2) * (LevelConstants.Radius + LevelConstants.SpacingOfTriangle);
return new Vector3(pos.x, isUp ? pos.y + offsetValue : pos.y - offsetValue, pos.z);
}
private static Vector3 GetInclinedTrianglePlankPositionLeft(List<int> holeIndex,
Dictionary<string, Kong> holeDic,
bool isLeft = true)
{
var pos = holeDic[$"Kong{holeIndex[0]}"].Obj.transform.position;
float offsetValue = Mathf.Sqrt(2) * (LevelConstants.Radius + LevelConstants.SpacingOfTriangle);
return new Vector3(isLeft ? pos.x - offsetValue : pos.x + offsetValue, pos.y, pos.z);
}
#endregion
public static Vector3 GetPlankPositionInfo(Dictionary<string, Kong> holeDic, List<int> holeIndex,
LevelData.PlankType plankType, LevelData.SquareNormType squareNormType)
{
switch (plankType)
{
case LevelData.PlankType.HorizontalSquare:
return GetSquarePlankPosition(holeIndex, holeDic, squareNormType);
case LevelData.PlankType.VerticalSquare:
return GetSquarePlankPosition(holeIndex, holeDic, squareNormType, false);
case LevelData.PlankType.Circle:
return GetCirclePlankPosition(holeIndex, holeDic);
case LevelData.PlankType.MultiSquare:
return GetMultiSquarePlankPosition(holeIndex, holeDic);
case LevelData.PlankType.InclinedSquareToLeft:
return GetInclinedSquarePlankPosition(holeIndex, holeDic);
case LevelData.PlankType.InclinedSquareToRight:
return GetInclinedSquarePlankPosition(holeIndex, holeDic);
case LevelData.PlankType.RightTriangleToLeft:
return GetRightTrianglePlankPosition(holeIndex, holeDic);
case LevelData.PlankType.RightTriangleToRight:
return GetRightTrianglePlankPosition(holeIndex, holeDic, false);
case LevelData.PlankType.InclinedTriangle:
return GetInclinedTrianglePlankPositionUp(holeIndex, holeDic);
case LevelData.PlankType.InclinedTriangle_180:
return GetInclinedTrianglePlankPositionUp(holeIndex, holeDic, false);
case LevelData.PlankType.InclinedTriangle_90:
return GetInclinedTrianglePlankPositionLeft(holeIndex, holeDic);
case LevelData.PlankType.InclinedTriangle_270:
return GetInclinedTrianglePlankPositionLeft(holeIndex, holeDic, false);
case LevelData.PlankType.RightTriangleToLeft_Down:
return GetRightTrianglePlankPositionDown(holeIndex, holeDic);
case LevelData.PlankType.RightTriangleToRight_Down:
return GetRightTrianglePlankPositionDown(holeIndex, holeDic, false);
default:
return Vector3.zero;
}
}
private static int JudgeSquareIndexLimit(LevelData.SquareNormType squareNormType)
{
int limitIndex;
switch (squareNormType)
{
case LevelData.SquareNormType.WithTwo:
limitIndex = 1;
break;
case LevelData.SquareNormType.WithThree:
limitIndex = 2;
break;
case LevelData.SquareNormType.WithFour:
limitIndex = 3;
break;
case LevelData.SquareNormType.WithFive:
limitIndex = 4;
break;
default:
limitIndex = 0;
break;
}
return limitIndex;
}
}
}