Files
JJBB/Assets/Editor/Scripts/Path/ServerObstacle.cs

519 lines
21 KiB
C#
Raw Permalink Normal View History

2024-08-23 15:49:34 +08:00
/********************************************************************************
* ServerObstacle.cs
* \MLDJ\Editor\ServerObstacle.cs
*
* 2013-10-25
*
*
*
*********************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
public class ServerObstacle : Editor
{
// 射线检测的layer
private const int _raycastLayer = 2;
private const byte _mNPath = 1; //可行走区域标识
private const float _rayLength = 1000f;
//private static Vector3 m_TestRayOrigin = new Vector3(0, 1000.0f, 0); //进行射线检测的射线原点
//private static readonly Vector3 m_TestRayDirection = new Vector3(0, -1000.0f, 0); //进行射线检测的射线方向
private const float _rectDistance = 0.2f;
private const string _displayRootName = "ServerObstacleRoot";
// Server文件夹路径
private static string rootPath
{
get { return Application.dataPath.MoveUp().MoveUp().Open("Public"); }
//get { return @"F:\Mmo3d\Public"; }
}
private static Rect? GetNavMeshRect()
{
Rect? result = null;
var triangulation = NavMesh.CalculateTriangulation();
if (triangulation.vertices.Length == 0)
{
Debug.LogError("无法获得当前地图的NavMesh");
}
else
{
// 计算实际NavMesh的范围
var point = triangulation.vertices[0];
var minX = point.x;
var maxX = point.x;
var minZ = point.z;
var maxZ = point.z;
for (var i = 1; i < triangulation.vertices.Length; i++)
{
point = triangulation.vertices[i];
minX = Mathf.Min(minX, point.x);
maxX = Mathf.Max(maxX, point.x);
minZ = Mathf.Min(minZ, point.z);
maxZ = Mathf.Max(maxZ, point.z);
}
result = new Rect(minX, minZ, maxX - minX, maxZ - minZ);
}
return result;
}
/// <summary>
/// 为配表上存在,实际文件都丢失的场景制作空碰撞区
/// </summary>
public static void CreateEmptyObstacle(string sceneName)
{
var scenePath = GetSceneFilePath(sceneName);
var dir = Path.GetDirectoryName(scenePath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
using (var fs = File.Create(scenePath))
{
using (var writer = new BinaryWriter(fs))
{
writer.Write(0f);
writer.Write(0f);
writer.Write(0);
writer.Write(0);
}
}
}
//private static Rect? GetSceneRect()
//{
// Rect? result = null;
// var filePath = rootPath.Open(@"PublicTables\SceneClass.txt");
// if (!File.Exists(filePath))
// Debug.LogError("SceneClass文件不存在");
// else
// {
// var triangulation = UnityEngine.AI.NavMesh.CalculateTriangulation();
// if (triangulation.vertices.Length == 0)
// Debug.LogError("无法获得当前地图的NavMesh");
// else
// {
// var lines = File.ReadAllLines(filePath);
// if (lines.Length == 0)
// Debug.LogError("SceneClass文件损坏");
// else
// {
// var segments = lines[0].Trim().Split('\t');
// var nameId = GetIndexOfTitle(segments, "ResName");
// var xMinId = GetIndexOfTitle(segments, "BasePosX");
// var zMinId = GetIndexOfTitle(segments, "BasePosZ");
// var widthId = GetIndexOfTitle(segments, "Length");
// var heightId = GetIndexOfTitle(segments, "Width");
// if (nameId >= 0 &&
// xMinId >= 0 &&
// zMinId >= 0 &&
// widthId >= 0 &&
// heightId >= 0)
// {
// var sceneName = Path.GetFileNameWithoutExtension(SceneManager.GetActiveScene().name);
// string[] sceneLine = null;
// for (var i = 1; i < lines.Length; i++)
// {
// segments = lines[i].Trim().Split('\t');
// if (segments.Length > nameId &&
// segments[nameId] == sceneName)
// {
// sceneLine = segments;
// break;
// }
// }
// if (sceneLine == null)
// Debug.LogError(string.Format("SceneClass不包含{0}场景的数据!", sceneName));
// else
// {
// // 获得场景范围
// try
// {
// result = new Rect(float.Parse(segments[xMinId]), float.Parse(segments[zMinId]), float.Parse(segments[widthId]), float.Parse(segments[heightId]));
// }
// catch (Exception e)
// {
// Debug.LogError(e);
// result = null;
// }
// if (result != null)
// {
// // 计算实际NavMesh的范围
// var point = triangulation.vertices[0];
// var minX = point.x;
// var maxX = point.x;
// var minZ = point.z;
// var maxZ = point.z;
// for (var i = 1; i < triangulation.vertices.Length; i++)
// {
// point = triangulation.vertices[i];
// minX = Mathf.Min(minX, point.x);
// maxX = Mathf.Max(maxX, point.x);
// minZ = Mathf.Min(minZ, point.z);
// maxZ = Mathf.Max(maxZ, point.z);
// }
// if (minX < result.Value.xMin ||
// minZ < result.Value.yMin ||
// maxX > result.Value.xMax ||
// maxZ > result.Value.yMax)
// {
// var navRect = new Rect(minX, minZ, maxX - minX, maxZ - minZ);
// Debug.LogError(string.Format("SceneClass定义{0}场景的范围(min {1}, max {2}),未完全包含实际行走区域(min {3}, max {4})", sceneName, result.Value.min, result.Value.max, navRect.min, navRect.max));
// result = null;
// }
// }
// }
// }
// }
// }
// }
// return result;
//}
//private static int GetIndexOfTitle(string[] segments, string title)
//{
// var result = segments.FindIndex(a => a == title);
// if (result < 0)
// Debug.LogError(string.Format("SceneClass无法获得{0}标题位置", title));
// return result;
//}
//////////////////////////////////////////////////////////////////////////
// 生成当前场景服务器阻挡
//////////////////////////////////////////////////////////////////////////
[MenuItem("ProTool/ServerObstacle/Create")]
public static void CreateObstacle()
{
// 试图获得通过NavMesh生成的Collider
var navMeshColliderObj = GameObject.Find(NavMeshToCollider.navMeshColliderName);
var navMeshColliderFind = navMeshColliderObj != null;
if (!navMeshColliderFind)
{
NavMeshToCollider.ConvertNavMeshToCollider();
navMeshColliderObj = GameObject.Find(NavMeshToCollider.navMeshColliderName);
}
var success = false;
var navMeshCollider = navMeshColliderObj.GetComponent<MeshCollider>();
if (navMeshCollider == null)
{
var scene = SceneManager.GetActiveScene();
var sceneName = Path.GetFileNameWithoutExtension(scene.name);
CreateEmptyObstacle(sceneName);
Debug.LogError("无法在场景中获得NavMeshCollider");
}
else
{
var originLayer = navMeshCollider.gameObject.layer;
navMeshCollider.gameObject.layer = _raycastLayer;
var rect = GetNavMeshRect();
if (rect == null)
{
CreateEmptyObstacle(GetCurrentSceneName());
Debug.LogError("无法在场景中获得NavMeshCollider");
}
else
{
//获得当前场景的实际长和宽
var width = Mathf.CeilToInt(rect.Value.width / _rectDistance);
var height = Mathf.CeilToInt(rect.Value.height / _rectDistance);
Debug.Log("场景尺寸:" + rect.Value + " 像素尺寸:" + width + ", " + height);
//初始化文件
var obstacleFilePath = GetSceneFilePath();
var dir = Path.GetDirectoryName(obstacleFilePath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
using (var fs = File.Create(obstacleFilePath))
{
using (var writer = new BinaryWriter(fs))
{
writer.Write(rect.Value.xMin);
writer.Write(rect.Value.yMin);
writer.Write(width);
writer.Write(height);
for (var i = 0; i < width; i++)
for (var j = 0; j < height; j++)
{
var nState = GetScenePosPathState(i * _rectDistance + rect.Value.xMin,
j * _rectDistance + rect.Value.yMin);
writer.Write(nState);
}
}
}
// 如果是自己创建的额外碰撞体,则移除这个碰撞体
if (!navMeshColliderFind)
DestroyImmediate(navMeshColliderObj);
else
navMeshCollider.gameObject.layer = originLayer;
success = true;
}
}
if (success)
Debug.Log(string.Format("服务器碰撞输出成功,路径{0}", GetSceneFilePath()));
else
Debug.LogWarning(string.Format("场景{0}保存为空白文件!", GetCurrentSceneName()));
}
//////////////////////////////////////////////////////////////////////////
// 查看当前场景服务器阻挡
//////////////////////////////////////////////////////////////////////////
[MenuItem("ProTool/ServerObstacle/Show")]
public static void ShowObstacle()
{
//初始化文件
var obstaclFilePath = GetSceneFilePath();
var obstacleRoot = CreateObstacleRoot().transform;
if (!File.Exists(obstaclFilePath))
{
Debug.LogError("无法找到文件 " + obstaclFilePath);
return;
}
var error = false;
//创建路径点模型
var pathObj =
AssetDatabase.LoadAssetAtPath("Assets/Project3D/Tool/Editor/Path/Path.prefab", typeof(GameObject)) as
GameObject;
using (var fs = File.OpenRead(obstaclFilePath))
{
using (var reader = new BinaryReader(fs))
{
try
{
var xMin = reader.ReadSingle();
var zMin = reader.ReadSingle();
var width = reader.ReadInt32();
var height = reader.ReadInt32();
Debug.Log(string.Format("地图开始({0}, {1}),地图像素({2}{3}", xMin, zMin, width, height));
var lineList = new List<ObstacleBlock>();
ObstacleBlock current = null;
// 合并线型碰撞区
for (var i = 0; i < width; i++)
for (var j = 0; j < height; j++)
{
var state = reader.ReadByte();
if (state > 0)
if (current == null ||
current.zMax != j - 1 ||
current.xMax != i)
{
current = new ObstacleBlock(i, j);
lineList.Add(current);
}
else
{
current.zMax = j;
}
else
current = null;
}
// 合并体型碰撞区
var rectList = new List<ObstacleBlock>();
current = null;
while (lineList.Count > 0)
{
if (current == null)
{
current = lineList[0];
lineList.RemoveAt(0);
rectList.Add(current);
}
var merge = -1;
for (var i = 0; i < lineList.Count; i++)
if (lineList[i].zMin == current.zMin &&
lineList[i].zMax == current.zMax &&
lineList[i].xMin == current.xMax + 1)
merge = i;
if (merge >= 0)
{
current.xMax = lineList[merge].xMax;
lineList.RemoveAt(merge);
}
else
{
current = null;
}
}
var maxHeight = 0f;
var triangulation = NavMesh.CalculateTriangulation();
// 计算实际NavMesh的范围
for (var i = 0; i < triangulation.vertices.Length; i++)
if (maxHeight < triangulation.vertices[i].y)
maxHeight = triangulation.vertices[i].y;
maxHeight += 5f;
for (var i = 0; i < rectList.Count; i++)
{
var rect = rectList[i];
var pathInst = Instantiate(pathObj);
var rectWidth = (rect.xMax - rect.xMin + 1) * _rectDistance;
var rectHeight = (rect.zMax - rect.zMin + 1) * _rectDistance;
var rectStartX = rect.xMin * _rectDistance + xMin;
var rectStartZ = rect.zMin * _rectDistance + zMin;
pathInst.transform.position = new Vector3(rectStartX + rectWidth * 0.5f, maxHeight,
rectStartZ + rectHeight * 0.5f);
pathInst.transform.localScale = new Vector3(rectWidth, 0f, rectHeight);
pathInst.transform.SetParent(obstacleRoot);
}
}
catch (Exception e)
{
Debug.LogError(e);
error = true;
}
}
}
if (error)
DestroyImmediate(obstacleRoot);
else
Debug.Log(string.Format("服务器碰撞加载成功,路径{0}", obstaclFilePath));
}
//////////////////////////////////////////////////////////////////////////
// 隐藏当前场景服务器阻挡
//////////////////////////////////////////////////////////////////////////
[MenuItem("ProTool/ServerObstacle/Hide")]
public static void HideObstacle()
{
CreateObstacleRoot();
Debug.Log("Server Obstacle Hide OK");
}
private static string GetCurrentSceneName()
{
var scene = SceneManager.GetActiveScene();
return Path.GetFileNameWithoutExtension(scene.name);
}
//得到当前场景的阻挡文件全路径
private static string GetSceneFilePath()
{
return GetSceneFilePath(GetCurrentSceneName());
}
private static string GetSceneFilePath(string sceneName)
{
return rootPath.Open(@"ServerRun\Scene").Open(sceneName + ".path");
}
//创建Obstacle阻挡显示根节点如果有则清空没有则创建
private static GameObject CreateObstacleRoot()
{
var obRoot = GameObject.Find(_displayRootName);
if (obRoot != null)
DestroyImmediate(obRoot);
obRoot = new GameObject(_displayRootName);
return obRoot;
}
//关键函数,根据某个点获取当前点状态
private static byte GetScenePosPathState(float fX, float fZ)
{
//var info = new ObstacleInfo
//{
// m_fX = fX,
// m_fZ = fZ
//};
byte state = 0;
var rayOrigin = new Vector3(fX + 0.5f * _rectDistance, _rayLength * 0.5f, fZ + 0.5f * _rectDistance);
//var originalLayer = collider.gameObject.layer;
// Unity保留的无名Layer应该不会有其他物体使用
// Boxcast不支持由collider调用因此只能用这种保险度较低的方法检测
//const int boxcastLayer = 0;
//collider.gameObject.layer = boxcastLayer;
//if (Physics.Raycast(rayOrigin, Vector3.down, _rayLength * 0.5f, _raycastLayer.ToFlag()))
//{
// state = _mNPath;
//}
if (Physics.BoxCast(rayOrigin, Vector3.one * _rectDistance * 0.5f, Vector3.down,
Quaternion.identity, _rayLength, _raycastLayer.ToFlag()))
state = _mNPath;
//collider.gameObject.layer = originalLayer;
//var ray = new Ray(m_TestRayOrigin, m_TestRayDirection);
//RaycastHit hit;
//if (collider.Raycast(ray, out hit, float.PositiveInfinity))
// info.m_Value = m_nPath;
//for (int i = 0; i < m_ServerObstacleTestAgent.Count; ++i)
//{
// m_ServerObstacleTestAgent[i].destination = pos;
// if (m_ServerObstacleTestAgent[i].hasPath)
// {
// info.m_Value = m_nPath;
// break;
// }
//}
return state;
}
//private static float GetScenePosy(float fX, float fZ)
//{
// var rayOrigin = new Vector3(fX, _rayLength * 0.5f, fZ);
// var ray = new Ray(rayOrigin, Vector3.down);
// RaycastHit hit;
// return Physics.Raycast(ray, out hit, _rayLength) ? hit.point.y : 0f;
// //for (int i = 0; i < m_ServerObstacleTestAgent.Count; ++i)
// //{
// // m_ServerObstacleTestAgent[i].destination = pos;
// // if (m_ServerObstacleTestAgent[i].hasPath)
// // {
// // info.m_Value = m_nPath;
// // break;
// // }
// //}
//}
//struct与byte[]相互转换函数
//private static ObstacleInfo Byte2Struct(byte[] arr)
//{
// var structSize = Marshal.SizeOf(typeof(ObstacleInfo));
// var ptemp = Marshal.AllocHGlobal(structSize);
// Marshal.Copy(arr, 0, ptemp, structSize);
// var rs = (ObstacleInfo) Marshal.PtrToStructure(ptemp, typeof(ObstacleInfo));
// Marshal.FreeHGlobal(ptemp);
// return rs;
//}
//private static byte[] Struct2Byte(ObstacleInfo s)
//{
// var structSize = Marshal.SizeOf(typeof(ObstacleInfo));
// var buffer = new byte[structSize];
// //分配结构体大小的内存空间
// var structPtr = Marshal.AllocHGlobal(structSize);
// //将结构体拷到分配好的内存空间
// Marshal.StructureToPtr(s, structPtr, false);
// //从内存空间拷到byte数组
// Marshal.Copy(structPtr, buffer, 0, structSize);
// //释放内存空间
// Marshal.FreeHGlobal(structPtr);
// return buffer;
//}
//private static List<NavMeshAgent> m_ServerObstacleTestAgent = new List<NavMeshAgent>(); //用以进行联通关系检测的NavAgent集合
////保存的数据结构
//private struct ObstacleInfo
//{
// public float m_fX;
// public float m_fZ;
// public byte m_nValue;
//}
private class ObstacleBlock
{
public int xMax;
public readonly int xMin;
public int zMax;
public readonly int zMin;
public ObstacleBlock(int x, int z)
{
xMin = x;
xMax = x;
zMin = z;
zMax = z;
}
}
}