1270 lines
46 KiB
C#
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
|
|
}
|
|
} |