Files
Main/Assets/Plugins/Code/FuncellEditor/PathEditor/Proxy/PathGridData.cs
2025-01-25 04:38:09 +08:00

1270 lines
46 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
#if FUNCELL_EDITOR
namespace PathEditor.Proxy.Editor
#else
namespace PathEditor.Proxy.Plugin
#endif
{
public enum PathGridType : byte
{
None = 0, //普通地面
Block = 1, //阻挡
Jump = 2, //可跳跃阻挡
Water = 3, //水面
Grass = 4, //草地
Stone = 5, //砖石地面
Sand = 6, //沙地
Marsh = 7, //沼泽
Wood = 8, //木地板
Snow = 9, //雪地
UserBlock = 10, //用户设置的阻挡
Safe = 11, //安全区
Num,
}
public class PathGirdData
{
public enum Flag : byte
{
None = 0,
AddBlock = 1,
RemoveBlock = 2,
}
public struct Point
{
public int x, y;
public Point(int _x, int _y) { x = _x; y = _y; }
}
#region//静态变量
public const int MaxRowCount = 512;
public const int MaxColCount = 512;
public static PathGridType[,] _cacheMergedData = new PathGridType[MaxRowCount, MaxColCount];
//path grid的每个单元格的高度,大小为float
public static short[] _cacheHeightMap = new short[MaxRowCount * MaxColCount];
//path grid的每个单元格周围不是none类型的个数,大小为byte
public static byte[,] _cacheMergeWeightData = new byte[MaxRowCount, MaxColCount];
#endregion
#region//共有变量
//场景名称
public string LevelName = string.Empty;
//画刷的宽度
public int WeightRadius = 0;
//场景的path grid的列数和行数
public int NumberOfColumns = 0;
public int NumberOfRows = 0;
//path grid的单元格的大小
public float CellSize = 1.0f;
//[PathGrid]游戏对象的位置
public Vector3 Position = Vector3.zero;
//单元格显示的高度的限制
public Vector2 HeightRangeLimit = new Vector2(float.MinValue, float.MaxValue);
//path grid的每个单元格的类型,容量为numberOfColumns*numberOfRows,一个类型大小为byte
public PathGridType[,] MergedData = null;
//path grid的每个单元格的高度,大小为float
public short[] HeightMap = null;
//path grid的每个单元格周围不是none类型的个数,大小为byte
public byte[,] MergeWeightData = null;
#endregion
#region//私有变量
private float _recipCellSize = 0f;
#endregion
#region//属性
public float RecipCellSize
{
get
{
return _recipCellSize;
}
}
#endregion
#region//共有函数
public void SetCellSize(float cellSize)
{
CellSize = cellSize;
_recipCellSize = 1f / CellSize;
}
//获取int
public static int GetInt(byte[] bytes, ref int readLen)
{
var result = BitConverter.ToInt32(bytes, readLen);
readLen += 4;
return result;
}
//获取int16
public static Int16 GetInt16(byte[] bytes, ref int readLen)
{
var result = BitConverter.ToInt16(bytes, readLen);
readLen += 2;
return result;
}
//获取uint16
public static UInt16 GetUInt16(byte[] bytes, ref int readLen)
{
var result = BitConverter.ToUInt16(bytes, readLen);
readLen += 2;
return result;
}
//获取int8
public static byte GetInt8(byte[] bytes, ref int readLen)
{
var result = bytes[readLen];
readLen += 1;
return result;
}
//获fload
public static float GetFloat(byte[] bytes, ref int readLen)
{
var result = BitConverter.ToSingle(bytes, readLen);
readLen += 4;
return result;
}
//获取string
public static string GetString(byte[] bytes, int size, ref int readLen)
{
var result = System.Text.Encoding.UTF8.GetString(bytes, readLen, size).Trim((char)0);
readLen += size;
return result;
}
//从文件流中载入数据
public bool Load(byte[] bytes)
{
int readLen = 0;
int count = GetInt(bytes, ref readLen);
LevelName = GetString(bytes, count, ref readLen);
WeightRadius = GetInt(bytes, ref readLen);
NumberOfColumns = GetInt(bytes, ref readLen);
NumberOfRows = GetInt(bytes, ref readLen);
CellSize = GetFloat(bytes, ref readLen);
Position = new Vector3(GetFloat(bytes, ref readLen), GetFloat(bytes, ref readLen), GetFloat(bytes, ref readLen));
HeightRangeLimit = new Vector2(GetFloat(bytes, ref readLen), GetFloat(bytes, ref readLen));
if(NumberOfColumns > MaxColCount || NumberOfRows > MaxRowCount)
{
UnityEngine.Debug.LogErrorFormat("Load PathGridData Failed, NumberOfColumns = {0}, NumberOfRows = {1}", NumberOfColumns, NumberOfRows);
return false;
}
int listCount = GetInt(bytes, ref readLen);
MergedData = _cacheMergedData;
int counter = 0;
int curRow = 0;
int curCol = 0;
for (int i = 0; i < listCount; ++i)
{
var curCount = GetInt16(bytes, ref readLen);
var curValue = GetInt8(bytes, ref readLen);
for (int j = counter; j < (counter + curCount); ++j)
{
MergedData[curRow, curCol] = (PathGridType)curValue;
++curCol;
if (curCol >= NumberOfColumns)
{
++curRow;
curCol = 0;
}
}
counter += curCount;
}
HeightMap = _cacheHeightMap;
unsafe
{
fixed (byte* ptr = bytes)
{
var srcPrt = new IntPtr((void*)(ptr + readLen));
Marshal.Copy(srcPrt, HeightMap, 0, NumberOfRows * NumberOfColumns);
}
}
readLen += NumberOfRows * NumberOfColumns * 2;
listCount = GetInt(bytes, ref readLen);
MergeWeightData = _cacheMergeWeightData;
counter = 0;
curRow = 0;
curCol = 0;
for (int i = 0; i < listCount; ++i)
{
var curCount = GetInt16(bytes, ref readLen);
var curValue = GetInt8(bytes, ref readLen);
for (int j = counter; j < (counter + curCount); ++j)
{
MergeWeightData[curRow, curCol] = curValue;
++curCol;
if (curCol >= NumberOfColumns)
{
++curRow;
curCol = 0;
}
}
counter += curCount;
}
_recipCellSize = 1.0f / CellSize;
return true;
}
//保存数据到文件流
public void Save(BinaryWriter w, PathData pb)
{
w.Write(LevelName.Length);
w.Write(LevelName.ToCharArray());
w.Write(WeightRadius);
w.Write(NumberOfColumns);
w.Write(NumberOfRows);
w.Write(CellSize);
w.Write(Position.x);
w.Write(Position.y);
w.Write(Position.z);
w.Write(HeightRangeLimit.x);
w.Write(HeightRangeLimit.y);
pb.MergeOutput();
if (pb.m_mergedPathGrid != null)
{
var coutList = new List<ushort>();
var valueList = new List<PathGridType>();
ushort curCount = 0;
PathGridType curValue = 0;
int allCount = 0;
for (int j = 0; j < NumberOfRows; ++j)
{
for (int i = 0; i < NumberOfColumns; ++i)
{
var tvalue = pb.m_mergedPathGrid[j, i];
if (j == 0 && i == 0)
{
curValue = tvalue;
}
if (curValue == tvalue && curCount < ushort.MaxValue)
{
++curCount;
}
else
{
allCount += curCount;
coutList.Add(curCount);
valueList.Add(curValue);
curValue = tvalue;
curCount = 1;
}
}
}
allCount += curCount;
coutList.Add(curCount);
valueList.Add(curValue);
w.Write(coutList.Count);
for (int i = 0; i < coutList.Count; ++i)
{
w.Write(coutList[i]);
w.Write((byte)valueList[i]);
}
}
if (pb.m_heightMap != null)
{
for (int j = 0; j < NumberOfRows; ++j)
{
for (int i = 0; i < NumberOfColumns; ++i)
{
ushort height = (ushort)(pb.m_heightMap[j, i] * 100f);
w.Write(height);
}
}
}
if (pb.m_mergedWeightPathGrid != null)
{
var coutList = new List<ushort>();
var valueList = new List<Byte>();
ushort curCount = 0;
byte curValue = 0;
int allCount = 0;
for (int j = 0; j < NumberOfRows; ++j)
{
for (int i = 0; i < NumberOfColumns; ++i)
{
var tvalue = pb.m_mergedWeightPathGrid[j, i];
if (j == 0 && i == 0)
{
curValue = tvalue;
}
if (curValue == tvalue && curCount < ushort.MaxValue)
{
++curCount;
}
else
{
allCount += curCount;
coutList.Add(curCount);
valueList.Add(curValue);
curValue = tvalue;
curCount = 1;
}
}
}
allCount += curCount;
coutList.Add(curCount);
valueList.Add(curValue);
w.Write(coutList.Count);
for (int i = 0; i < coutList.Count; ++i)
{
w.Write(coutList[i]);
w.Write(valueList[i]);
}
}
}
//获取格子类型
public PathGridType GetCellType(int column, int row)
{
if (!IsRowAndColValid(column, row))
return PathGridType.None;
return MergedData[row, column];
}
//行列是否有效
public bool IsRowAndColValid(int col, int row)
{
if (row >= 0 && row < NumberOfRows && col >= 0 && col < NumberOfColumns)
{
return true;
}
return false;
}
//是否是阻挡
public bool IsBlock(int column, int row)
{
if (!IsRowAndColValid(column, row))
return true;
var value = GetCellType(column, row);
switch (value)
{
case PathGridType.Block:
case PathGridType.UserBlock:
case PathGridType.None:
case PathGridType.Jump:
return true;
}
return false;
}
//是否是跳跃阻挡
public bool IsJump(int column, int row)
{
if (!IsRowAndColValid(column, row))
return false;
var value = GetCellType(column, row);
if (value == PathGridType.Jump)
return true;
return false;
}
//是否是安全区
public bool IsSafe(int column, int row)
{
if (!IsRowAndColValid(column, row))
return false;
var value = GetCellType(column, row);
if (value == PathGridType.Wood)
return true;
return false;
}
//获取生成的阻挡数据
public PathGridType GetBakeBlock(int column, int row)
{
if (!IsRowAndColValid(column, row))
return PathGridType.Block;
var value = GetCellType(column, row);
if (value == PathGridType.Block)
{
return PathGridType.Block;
}
return PathGridType.None;
}
#region //获取高度
public float GetHeight(int column, int row)
{
if (!IsRowAndColValid(column, row))
return 0f;
return HeightMap[row * NumberOfColumns + column] / 100f;
}
public float GetHeight(ref Vector3 pos)
{
if (Position.y < HeightRangeLimit.x || Position.y > HeightRangeLimit.y)
{
return HeightRangeLimit.x;
}
float size = 1 / CellSize;
int column = (int)((pos.x - Position.x) * size);
int row = (int)((pos.z - Position.z) * size);
if (column < 0 || column >= NumberOfColumns)
{
return HeightRangeLimit.x;
}
if (row < 0 || row >= NumberOfRows)
{
return HeightRangeLimit.x;
}
return GetHeight(column, row);
}
public float GetLerpHeight(int col, int row, float defaultHeight)
{
if (!IsRowAndColValid(col, row))
{
return defaultHeight;
}
if(IsBlock(col, row))
{
//阻挡不纳入高度插值
return defaultHeight;
}
return GetHeight(col, row);
}
public float GetHeight(float x, float z)
{
float xPos = x - Position.x;
float zPos = z - Position.z;
int col = (int)(xPos / CellSize);
int row = (int)(zPos / CellSize);
if (!IsRowAndColValid(col, row))
{
return 0f;
}
float xLerp = (xPos - col * CellSize) / CellSize;
float zLerp = (zPos - row * CellSize) / CellSize;
float height0 = GetHeight(col, row);
float height1 = height0;
float height2 = height0;
float height3 = height0;
if (xLerp <= 0.5f && zLerp <= 0.5f)
{
height1 = GetLerpHeight(col - 1, row, height0);
height2 = GetLerpHeight(col, row - 1, height0);
height3 = GetLerpHeight(col - 1, row - 1, height0);
xLerp = 0.5f - xLerp;
zLerp = 0.5f - zLerp;
}
else if (xLerp <= 0.5f && zLerp >= 0.5f)
{
height1 = GetLerpHeight(col - 1, row, height0);
height2 = GetLerpHeight(col, row + 1, height0);
height3 = GetLerpHeight(col - 1, row + 1, height0);
xLerp = 0.5f - xLerp;
zLerp = zLerp - 0.5f;
}
else if (xLerp >= 0.5f && zLerp >= 0.5f)
{
height1 = GetLerpHeight(col + 1, row, height0);
height2 = GetLerpHeight(col, row + 1, height0);
height3 = GetLerpHeight(col + 1, row + 1, height0);
xLerp = xLerp - 0.5f;
zLerp = zLerp - 0.5f;
}
else if (xLerp >= 0.5f && zLerp <= 0.5f)
{
height1 = GetLerpHeight(col + 1, row, height0);
height2 = GetLerpHeight(col, row - 1, height0);
height3 = GetLerpHeight(col + 1, row - 1, height0);
xLerp = xLerp - 0.5f;
zLerp = 0.5f - zLerp;
}
float minHeight = Mathf.Min(Mathf.Min(height0, height1), Mathf.Min(height2, height3));
float maxHeight = Mathf.Max(Mathf.Max(height0, height1), Mathf.Max(height2, height3));
//if (maxHeight - minHeight > CellSize)
// return height0;
return Mathf.Lerp(Mathf.Lerp(height0, height1, xLerp),
Mathf.Lerp(height2, height3, xLerp), zLerp);
}
#endregion
public bool IsBlock(Vector3 pos)
{
if (Position.y < HeightRangeLimit.x || Position.y > HeightRangeLimit.y)
{
return false;
}
int column = (int)((pos.x - Position.x) * _recipCellSize);
int row = (int)((pos.z - Position.z) * _recipCellSize);
if (column < 0 || column >= NumberOfColumns)
{
return false;
}
if (row < 0 || row >= NumberOfRows)
{
return false;
}
return IsBlock(column, row);
}
public bool IsValid(Vector2 pos)
{
int column = (int)((pos.x - Position.x) * _recipCellSize);
int row = (int)((pos.y - Position.z) * _recipCellSize);
if (column < 0 || column >= NumberOfColumns)
{
return false;
}
if (row < 0 || row >= NumberOfRows)
{
return false;
}
return true;
}
public bool CanJump(int column, int row, float height)
{
if (column < 0 || column >= NumberOfColumns)
{
return false;
}
if (row < 0 || row >= NumberOfRows)
{
return false;
}
var cellType = MergedData[row, column];
if (cellType == PathGridType.None || cellType == PathGridType.Block || cellType == PathGridType.UserBlock)
return false;
if (GetHeight(column, row) > height)
return false;
return true;
}
public int GetCellID(ref Vector3 pos)
{
int column = (int)((pos.x - Position.x) * _recipCellSize);
int row = (int)((pos.z - Position.z) * _recipCellSize);
if (column < 0 || column >= NumberOfColumns)
{
return -1;
}
if (row < 0 || row >= NumberOfRows)
{
return -1;
}
return (row << 16) | column;
}
public int GetCellID(ref Vector2 pos)
{
int column = (int)((pos.x - Position.x) * _recipCellSize);
int row = (int)((pos.y - Position.z) * _recipCellSize);
if (column < 0 || column >= NumberOfColumns)
{
return -1;
}
if (row < 0 || row >= NumberOfRows)
{
return -1;
}
return (row << 16) | column;
}
public int GetCellID(float x, float y)
{
int column = (int)((x - Position.x) * _recipCellSize);
int row = (int)((y - Position.z) * _recipCellSize);
if (column < 0 || column >= NumberOfColumns)
{
return -1;
}
if (row < 0 || row >= NumberOfRows)
{
return -1;
}
return (row << 16) | column;
}
public int GetCellID(int column, int row)
{
return (row << 16) | column;
}
public Vector2 GetCellPosition2d(int column, int row)
{
return new Vector2(column * CellSize, row * CellSize);
}
public Vector2 GetCellPosition2d(int id)
{
return new Vector2((id & 0xFFFF) * CellSize, (id >> 16) * CellSize);
}
public bool GetCellCoord(int id, out int column, out int row)
{
if (id == -1)
{
column = -1;
row = -1;
return false;
}
row = id >> 16;
column = id & 0xFFFF;
return true;
}
public bool IsBlock(Vector2 pos)
{
int column = (int)((pos.x - Position.x) * _recipCellSize);
int row = (int)((pos.y - Position.z) * _recipCellSize);
if (column < 0 || column >= NumberOfColumns)
{
return false;
}
if (row < 0 || row >= NumberOfRows)
{
return false;
}
return IsBlock(column, row);
}
public bool HasPathInRange(Vector3 pos, float radius = 1)
{
if (Position.y < HeightRangeLimit.x || Position.y > HeightRangeLimit.y)
{
return false;
}
int column = (int)((pos.x - Position.x) * _recipCellSize);
int row = (int)((pos.z - Position.z) * _recipCellSize);
if (column < 0 || column >= NumberOfColumns)
{
return false;
}
if (row < 0 || row >= NumberOfRows)
{
return false;
}
int cellRadius = (int)(radius * _recipCellSize);
return _HasPathInRange(column, row, cellRadius);
}
public bool HasPathInRange(Vector2 pos, float radius = 1)
{
int column = (int)((pos.x - Position.x) * _recipCellSize);
int row = (int)((pos.y - Position.z) * _recipCellSize);
if (column < 0 || column >= NumberOfColumns)
{
return false;
}
if (row < 0 || row >= NumberOfRows)
{
return false;
}
int cellRadius = (int)(radius * _recipCellSize);
return _HasPathInRange(column, row, cellRadius);
}
public bool[,] GetSolidity(bool transpose)
{
if (transpose)
{
bool[,] ret = new bool[NumberOfColumns, NumberOfRows];
for (int j = 0; j < NumberOfRows; ++j)
{
for (int i = 0; i < NumberOfColumns; ++i)
{
ret[i, j] = IsBlock(i, j);
}
}
return ret;
}
else
{
bool[,] ret = new bool[NumberOfRows, NumberOfColumns];
for (int j = 0; j < NumberOfRows; ++j)
{
for (int i = 0; i < NumberOfColumns; ++i)
{
ret[j, i] = IsBlock(i, j);
}
}
return ret;
}
}
public byte[,] GetWeight(bool transpose)
{
if (transpose)
{
byte[,] ret = new byte[NumberOfColumns, NumberOfRows];
for (int j = 0; j < NumberOfRows; ++j)
{
for (int i = 0; i < NumberOfColumns; ++i)
{
ret[i, j] = MergeWeightData[j, i];
}
}
return ret;
}
else
{
byte[,] ret = new byte[NumberOfRows, NumberOfColumns];
for (int j = 0; j < NumberOfRows; ++j)
{
for (int i = 0; i < NumberOfColumns; ++i)
{
ret[j, i] = MergeWeightData[j, i];
}
}
return ret;
}
}
bool _IsBlockJump(int column, int row)
{
if (column < 0 || column >= NumberOfColumns)
{
return true;
}
if (row < 0 || row >= NumberOfRows)
{
return true;
}
var cellType = MergedData[row, column];
if (cellType == PathGridType.None || cellType == PathGridType.Block || cellType == PathGridType.UserBlock)
return true;
return false;
}
bool _IsBlock(int column, int row)
{
if (!IsRowAndColValid(column, row))
return true;
var cellType = MergedData[row, column];
if (cellType == PathGridType.None || cellType == PathGridType.Block || cellType == PathGridType.Jump || cellType == PathGridType.UserBlock)
return true;
return false;
}
bool _IsSafe(int column, int row)
{
if (!IsRowAndColValid(column, row))
return true;
var cellType = MergedData[row, column];
if (cellType == PathGridType.Wood || cellType == PathGridType.None || cellType == PathGridType.Block || cellType == PathGridType.Jump || cellType == PathGridType.UserBlock)
return true;
return false;
}
public bool SetBlock(int column, int row, PathGridType type)
{
if (!IsRowAndColValid(column, row))
return false;
MergedData[row, column] = type;
return true;
}
bool _HasPathInRange(int column, int row, int radius)
{
int startRowPos = row - radius;
int startColumnPos = column - radius;
int endRowPos = row + radius;
int endColumnPos = column + radius;
for (int j = startRowPos; j <= endRowPos; j++)
{
for (int i = startColumnPos; i <= endColumnPos; i++)
{
if (!_IsBlock(i, j))
{
return true;
}
}
}
return false;
}
bool _HasBlockInRange(int column, int row, int radius)
{
int startRowPos = row - radius;
int startColumnPos = column - radius;
int endRowPos = row + radius;
int endColumnPos = column + radius;
for (int j = startRowPos; j <= endRowPos; j++)
{
for (int i = startColumnPos; i <= endColumnPos; i++)
{
if (_IsBlock(i, j))
{
return true;
}
}
}
return false;
}
//Ray casting technique described in paper:
//A Fast Voxel Traversal Algorithm for Ray Tracing - John Amanatides, Andrew Woo
//http://www.cse.yorku.ca/~amana/research/grid.pdf
public bool Raycast2d(Vector2 start, Vector2 end, out Vector2 hit)
{
var p1 = new Vector2(start.x * _recipCellSize, start.y * _recipCellSize);
var p2 = new Vector2(end.x * _recipCellSize, end.y * _recipCellSize);
hit = Vector2.zero;
if ((int)p1.x == (int)p2.x && (int)p1.y == (int)p2.y)
{
//since it doesn't cross any boundaries, there can't be a collision
return false;
}
//find out which direction to step, on each axis
var stepX = (p2.x > p1.x) ? 1 : -1;
var stepY = (p2.y > p1.y) ? 1 : -1;
var rayDirection = new Vector2(p2.x - p1.x, p2.y - p1.y);
//find out how far to move on each axis for every whole integer step on the other
var ratioX = rayDirection.x / rayDirection.y;
var ratioY = rayDirection.y / rayDirection.x;
if (rayDirection.y == 0.0f)
ratioX = 0.0f;
if (rayDirection.x == 0.0f)
ratioY = 0.0f;
var deltaY = p2.x - p1.x;
var deltaX = p2.y - p1.y;
//faster than Math.abs()...
deltaX = deltaX < 0 ? -deltaX : deltaX;
deltaY = deltaY < 0 ? -deltaY : deltaY;
//initialise the integer test coordinates with the coordinates of the starting tile, in tile space ( integer )
//Note: using noralised version of p1
var testX = (int)p1.x;
var testY = (int)p1.y;
//initialise the non-integer step, by advancing to the next tile boundary / ( whole integer of opposing axis )
//if moving in positive direction, move to end of curent tile, otherwise the beginning
var maxX = deltaX * ((stepX > 0) ? (1.0f - (p1.x % 1)) : (p1.x % 1));
var maxY = deltaY * ((stepY > 0) ? (1.0f - (p1.y % 1)) : (p1.y % 1));
var endTileX = (int)p2.x;
var endTileY = (int)p2.y;
var collisionPoint = new Vector2();
while (testX != endTileX || testY != endTileY)
{
if (maxX < maxY)
{
maxX += deltaX;
testX += stepX;
if (deltaX == 0.0f)
{
bool state = stepX > 0 ? (testX > endTileX) : (testX < endTileX);
if (state)
return false;
}
if (_IsBlock(testX, testY))
{
collisionPoint.x = testX;
if (stepX < 0)
{
//add one if going left
collisionPoint.x += 1.0f;
}
collisionPoint.y = p1.y + ratioY * (collisionPoint.x - p1.x);
collisionPoint.x *= CellSize;
collisionPoint.y *= CellSize;
hit = collisionPoint;
return true;
}
}
else
{
maxY += deltaY;
testY += stepY;
if (deltaY == 0.0f)
{
bool state = stepY > 0 ? (testY > endTileY) : (testY < endTileY);
if (state)
return false;
}
if (_IsBlock(testX, testY))
{
collisionPoint.y = testY;
if (stepY < 0)
{
//add one if going up
collisionPoint.y += 1.0f;
}
collisionPoint.x = p1.x + ratioX * (collisionPoint.y - p1.y);
//scale up
collisionPoint.x *= CellSize;
collisionPoint.y *= CellSize;
hit = collisionPoint;
return true;
}
}
}
return false;
}
public bool Raycast2dJump(Vector2 start, Vector2 end, out Vector2 hit)
{
var p1 = new Vector2(start.x * _recipCellSize, start.y * _recipCellSize);
var p2 = new Vector2(end.x * _recipCellSize, end.y * _recipCellSize);
hit = Vector2.zero;
if ((int)p1.x == (int)p2.x && (int)p1.y == (int)p2.y)
{
//since it doesn't cross any boundaries, there can't be a collision
return false;
}
//find out which direction to step, on each axis
var stepX = (p2.x > p1.x) ? 1 : -1;
var stepY = (p2.y > p1.y) ? 1 : -1;
var rayDirection = new Vector2(p2.x - p1.x, p2.y - p1.y);
//find out how far to move on each axis for every whole integer step on the other
var ratioX = rayDirection.x / rayDirection.y;
var ratioY = rayDirection.y / rayDirection.x;
var deltaY = p2.x - p1.x;
var deltaX = p2.y - p1.y;
//faster than Math.abs()...
deltaX = deltaX < 0 ? -deltaX : deltaX;
deltaY = deltaY < 0 ? -deltaY : deltaY;
//initialise the integer test coordinates with the coordinates of the starting tile, in tile space ( integer )
//Note: using noralised version of p1
var testX = (int)p1.x;
var testY = (int)p1.y;
//initialise the non-integer step, by advancing to the next tile boundary / ( whole integer of opposing axis )
//if moving in positive direction, move to end of curent tile, otherwise the beginning
var maxX = deltaX * ((stepX > 0) ? (1.0f - (p1.x % 1)) : (p1.x % 1));
var maxY = deltaY * ((stepY > 0) ? (1.0f - (p1.y % 1)) : (p1.y % 1));
var endTileX = (int)p2.x;
var endTileY = (int)p2.y;
var collisionPoint = new Vector2();
while (testX != endTileX || testY != endTileY)
{
if (maxX < maxY)
{
maxX += deltaX;
testX += stepX;
if (_IsBlockJump(testX, testY))
{
collisionPoint.x = testX;
if (stepX < 0)
{
//add one if going left
collisionPoint.x += 1.0f;
}
collisionPoint.y = p1.y + ratioY * (collisionPoint.x - p1.x);
collisionPoint.x *= CellSize;
collisionPoint.y *= CellSize;
hit = collisionPoint;
return true;
}
}
else
{
maxY += deltaY;
testY += stepY;
if (_IsBlockJump(testX, testY))
{
collisionPoint.y = testY;
if (stepY < 0)
{
//add one if going up
collisionPoint.y += 1.0f;
}
collisionPoint.x = p1.x + ratioX * (collisionPoint.y - p1.y);
//scale up
collisionPoint.x *= CellSize;
collisionPoint.y *= CellSize;
hit = collisionPoint;
return true;
}
}
}
return false;
}
public bool Raycast2dSafe(Vector2 start, Vector2 end, out Vector2 hit)
{
var p1 = new Vector2(start.x * _recipCellSize, start.y * _recipCellSize);
var p2 = new Vector2(end.x * _recipCellSize, end.y * _recipCellSize);
hit = Vector2.zero;
if ((int)p1.x == (int)p2.x && (int)p1.y == (int)p2.y)
{
//since it doesn't cross any boundaries, there can't be a collision
return false;
}
//find out which direction to step, on each axis
var stepX = (p2.x > p1.x) ? 1 : -1;
var stepY = (p2.y > p1.y) ? 1 : -1;
var rayDirection = new Vector2(p2.x - p1.x, p2.y - p1.y);
//find out how far to move on each axis for every whole integer step on the other
var ratioX = rayDirection.x / rayDirection.y;
var ratioY = rayDirection.y / rayDirection.x;
var deltaY = p2.x - p1.x;
var deltaX = p2.y - p1.y;
//faster than Math.abs()...
deltaX = deltaX < 0 ? -deltaX : deltaX;
deltaY = deltaY < 0 ? -deltaY : deltaY;
//initialise the integer test coordinates with the coordinates of the starting tile, in tile space ( integer )
//Note: using noralised version of p1
var testX = (int)p1.x;
var testY = (int)p1.y;
//initialise the non-integer step, by advancing to the next tile boundary / ( whole integer of opposing axis )
//if moving in positive direction, move to end of curent tile, otherwise the beginning
var maxX = deltaX * ((stepX > 0) ? (1.0f - (p1.x % 1)) : (p1.x % 1));
var maxY = deltaY * ((stepY > 0) ? (1.0f - (p1.y % 1)) : (p1.y % 1));
var endTileX = (int)p2.x;
var endTileY = (int)p2.y;
var collisionPoint = new Vector2();
while (testX != endTileX || testY != endTileY)
{
if (maxX < maxY)
{
maxX += deltaX;
if (testX != endTileX)
{
testX += stepX;
//Debug.Log(string.Format("====testX {0} testY {1}====", testX, testY));
if (_IsSafe(testX, testY))
{
collisionPoint.x = testX;
if (stepX < 0)
{
//add one if going left
collisionPoint.x += 1.0f;
}
collisionPoint.y = p1.y + ratioY * (collisionPoint.x - p1.x);
collisionPoint.x *= CellSize;
collisionPoint.y *= CellSize;
hit = collisionPoint;
//Debug.LogError("is safe 1");
return true;
}
}
}
else
{
maxY += deltaY;
if (testY != endTileY)
{
testY += stepY;
//Debug.Log(string.Format("====testX {0} testY {1}====", testX, testY));
if (_IsSafe(testX, testY))
{
collisionPoint.y = testY;
if (stepY < 0)
{
//add one if going up
collisionPoint.y += 1.0f;
}
collisionPoint.x = p1.x + ratioX * (collisionPoint.y - p1.y);
//scale up
collisionPoint.x *= CellSize;
collisionPoint.y *= CellSize;
hit = collisionPoint;
//Debug.LogError("is safe 2");
return true;
}
}
}
}
return false;
}
//是否有可跳跃的阻挡
bool _IsJumpBlock(int column, int row)
{
if (!IsRowAndColValid(column, row))
return true;
if (MergedData[row, column] == PathGridType.Jump)
return true;
return false;
}
public bool Raycast2dJumpBlock(Vector2 start, Vector2 end, out Vector2 hit)
{
var p1 = new Vector2(start.x * _recipCellSize, start.y * _recipCellSize);
var p2 = new Vector2(end.x * _recipCellSize, end.y * _recipCellSize);
hit = Vector2.zero;
if ((int)p1.x == (int)p2.x && (int)p1.y == (int)p2.y)
{
//since it doesn't cross any boundaries, there can't be a collision
return false;
}
//find out which direction to step, on each axis
var stepX = (p2.x > p1.x) ? 1 : -1;
var stepY = (p2.y > p1.y) ? 1 : -1;
var rayDirection = new Vector2(p2.x - p1.x, p2.y - p1.y);
//find out how far to move on each axis for every whole integer step on the other
var ratioX = rayDirection.x / rayDirection.y;
var ratioY = rayDirection.y / rayDirection.x;
var deltaY = p2.x - p1.x;
var deltaX = p2.y - p1.y;
//faster than Math.abs()...
deltaX = deltaX < 0 ? -deltaX : deltaX;
deltaY = deltaY < 0 ? -deltaY : deltaY;
//initialise the integer test coordinates with the coordinates of the starting tile, in tile space ( integer )
//Note: using noralised version of p1
var testX = (int)p1.x;
var testY = (int)p1.y;
//initialise the non-integer step, by advancing to the next tile boundary / ( whole integer of opposing axis )
//if moving in positive direction, move to end of curent tile, otherwise the beginning
var maxX = deltaX * ((stepX > 0) ? (1.0f - (p1.x % 1)) : (p1.x % 1));
var maxY = deltaY * ((stepY > 0) ? (1.0f - (p1.y % 1)) : (p1.y % 1));
var endTileX = (int)p2.x;
var endTileY = (int)p2.y;
var collisionPoint = new Vector2();
while (testX != endTileX || testY != endTileY)
{
if (maxX < maxY)
{
maxX += deltaX;
testX += stepX;
if (_IsJumpBlock(testX, testY))
{
collisionPoint.x = testX;
if (stepX < 0)
{
//add one if going left
collisionPoint.x += 1.0f;
}
collisionPoint.y = p1.y + ratioY * (collisionPoint.x - p1.x);
collisionPoint.x *= CellSize;
collisionPoint.y *= CellSize;
hit = collisionPoint;
return true;
}
}
else
{
maxY += deltaY;
testY += stepY;
if (_IsJumpBlock(testX, testY))
{
collisionPoint.y = testY;
if (stepY < 0)
{
//add one if going up
collisionPoint.y += 1.0f;
}
collisionPoint.x = p1.x + ratioX * (collisionPoint.y - p1.y);
//scale up
collisionPoint.x *= CellSize;
collisionPoint.y *= CellSize;
hit = collisionPoint;
return true;
}
}
}
return false;
}
public bool RaycastEx2d(Vector2 start, Vector2 end, out Vector2 hit, Func<int, int, bool, bool> func)
{
var p1 = new Vector2(start.x * _recipCellSize, start.y * _recipCellSize);
var p2 = new Vector2(end.x * _recipCellSize, end.y * _recipCellSize);
hit = Vector2.zero;
if ((int)p1.x == (int)p2.x && (int)p1.y == (int)p2.y)
{
//since it doesn't cross any boundaries, there can't be a collision
return false;
}
//find out which direction to step, on each axis
var stepX = (p2.x > p1.x) ? 1 : -1;
var stepY = (p2.y > p1.y) ? 1 : -1;
var rayDirection = new Vector2(p2.x - p1.x, p2.y - p1.y);
//find out how far to move on each axis for every whole integer step on the other
var ratioX = rayDirection.x / rayDirection.y;
var ratioY = rayDirection.y / rayDirection.x;
var deltaY = p2.x - p1.x;
var deltaX = p2.y - p1.y;
//faster than Math.abs()...
deltaX = deltaX < 0 ? -deltaX : deltaX;
deltaY = deltaY < 0 ? -deltaY : deltaY;
//initialise the integer test coordinates with the coordinates of the starting tile, in tile space ( integer )
//Note: using noralised version of p1
var testX = (int)p1.x;
var testY = (int)p1.y;
//initialise the non-integer step, by advancing to the next tile boundary / ( whole integer of opposing axis )
//if moving in positive direction, move to end of curent tile, otherwise the beginning
var maxX = deltaX * ((stepX > 0) ? (1.0f - (p1.x % 1)) : (p1.x % 1));
var maxY = deltaY * ((stepY > 0) ? (1.0f - (p1.y % 1)) : (p1.y % 1));
var endTileX = (int)p2.x;
var endTileY = (int)p2.y;
var collisionPoint = new Vector2();
while (testX != endTileX || testY != endTileY)
{
if (maxX < maxY)
{
maxX += deltaX;
testX += stepX;
if (func(testX, testY, _IsBlock(testX, testY)))
{
collisionPoint.x = testX;
if (stepX < 0)
{
//add one if going left
collisionPoint.x += 1.0f;
}
collisionPoint.y = p1.y + ratioY * (collisionPoint.x - p1.x);
collisionPoint.x *= CellSize;
collisionPoint.y *= CellSize;
hit = collisionPoint;
return true;
}
}
else
{
maxY += deltaY;
testY += stepY;
if (func(testX, testY, _IsBlock(testX, testY)))
{
collisionPoint.y = testY;
if (stepY < 0)
{
//add one if going up
collisionPoint.y += 1.0f;
}
collisionPoint.x = p1.x + ratioX * (collisionPoint.y - p1.y);
//scale up
collisionPoint.x *= CellSize;
collisionPoint.y *= CellSize;
hit = collisionPoint;
return true;
}
}
}
return false;
}
#endregion
}
}