Files
JJBB/Assets/Editor/Scripts/Path/ServerObstacle.cs
2024-08-23 15:49:34 +08:00

519 lines
21 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************************************************************
* 文件名: 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;
}
}
}