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

442 lines
14 KiB
C#
Raw Permalink Normal View History

2024-08-23 15:49:34 +08:00
using System.Collections.Generic;
using System.IO;
using System.Text;
using Games.Scene;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
public class ServerPathNodeGenerator : EditorWindow
{
private MaterialPropertyBlock _materialProperty;
private const float _defaultLinkLength = 6f;
private float linkLengthSqr
{
get { return _linkLength > 0 ? _linkLength * _linkLength : float.PositiveInfinity; }
}
private const string _scenePathNodeDir = "/../../Public/ServerRun/Scene/";
private const string _line2Text = "#MAX_ID=4999;MAX_RECORD=5000;TableType=Hash;";
private const string _distanceKey = "ServerPathNodeIDist";
private const string _sphereRootName = "ServerPathNode";
private const float _navMeshOffsetMax = 1f;
private const float _navMeshSampleDist = 2f;
private Scene _currentScene;
private List<MyTuple<Transform, Transform>> _links;
private Vector2 _pointPos;
private Vector2 _scrollViewPoint;
private bool _pointError;
private FloatSetting _linkLength;
private Material _material;
private Transform _root;
[MenuItem("Scene/生成服务器寻路点")]
public static void ShowEditorWindow()
{
GetWindow<ServerPathNodeGenerator>();
}
private void Awake()
{
SceneManager.sceneUnloaded += OnSceneUnloaded;
SceneView.onSceneGUIDelegate += OnSceneGui;
_linkLength = new FloatSetting(_distanceKey, _defaultLinkLength);
_material = new Material(Shader.Find("Unlit/Color"));
_materialProperty = new MaterialPropertyBlock();
OnSceneChange();
}
private void OnDestroy()
{
if (_material != null)
DestroyImmediate(_material);
if (_root != null)
DestroyImmediate(_root.gameObject);
SceneManager.sceneUnloaded -= OnSceneUnloaded;
SceneView.onSceneGUIDelegate -= OnSceneGui;
}
private void OnGUI()
{
// Add Points
_pointPos = EditorGUILayout.Vector2Field("点位置:", _pointPos, GUILayout.Width(200f));
var linkLength = EditorGUILayout.FloatField("最大连接距离", _linkLength);
if (linkLength != _linkLength)
_linkLength.Set(linkLength);
// Add Point
GUILayout.Space(20f);
GUILayout.Label("添加点位");
if (GUILayout.Button("添加 于指定位置", GUILayout.Width(100f)))
{
if (_root != null)
{
var point = _pointPos.InsertY();
CreateSphereAt(_root.childCount, point);
}
}
GUILayout.Space(20f);
if (GUILayout.Button("整理路径点"))
{
if (_root != null)
{
var sphereList = GetSphereList();
var posList = new Vector3[sphereList.Count];
for (var i = 0; i < posList.Length; i++)
{
posList[i] = sphereList[i].position;
DestroyImmediate(sphereList[i].gameObject);
}
for (var i = 0; i < posList.Length; i++)
{
CreateSphereAt(i, posList[i]);
}
}
}
GUILayout.Space(20f);
if (_pointError)
EditorGUILayout.HelpBox("上次生成时有路径点不在NavMesh上", MessageType.Error);
else
GUILayout.Label("生成和保存Txt");
if (GUILayout.Button("预览链接"))
{
_pointError = CheckAllPointPos();
if (!_pointError)
BuildLinks();
}
GUILayout.Space(5f);
if (GUILayout.Button("保存为Txt"))
{
_pointError = CheckAllPointPos();
if (!_pointError)
{
BuildLinks();
WriteFileRecord();
}
}
}
private void OnSceneGui(SceneView sceneView)
{
Handles.color = Color.magenta;
for (var i = 0; i < _links.Count; i++)
{
var source = _links[i].first;
var target = _links[i].second;
if (source != null && target != null)
Handles.DrawLine(source.position, target.position);
}
}
private void OnSceneUnloaded(Scene scene)
{
if (scene.buildIndex == _currentScene.buildIndex)
OnSceneChange();
}
private void OnSceneChange()
{
_links = new List<MyTuple<Transform, Transform>>();
_currentScene = SceneManager.GetActiveScene();
ReadFileRecord();
}
private string GetFileName()
{
return Application.dataPath + _scenePathNodeDir + _currentScene.name + "RoadPath.txt";
}
private void ReadFileRecord()
{
var rootObj = GameObject.Find(_sphereRootName);
if (rootObj != null)
DestroyImmediate(rootObj);
rootObj = new GameObject(_sphereRootName);
_root = rootObj.transform;
var fileName = GetFileName();
if (File.Exists(fileName))
{
var lines = File.ReadAllLines(fileName);
if (lines.Length > 4)
{
var pathNodeList = new List<PathNodeData>();
var id = 0;
for (var i = 4; i < lines.Length; i++)
{
var lineSuccess = false;
var segments = lines[i].Split('\t');
if (segments.Length > 3)
{
int index;
int posX;
int posZ;
if (int.TryParse(segments[0], out index) &&
int.TryParse(segments[2], out posX) &&
int.TryParse(segments[3], out posZ))
{
var point = new Vector3(posX * 0.01f, 0f, posZ * 0.01f);
var sphere = CreateSphereAt(id, point);
MarkPointPosition(sphere);
var pathNode = new PathNodeData() { index = index, sphere = sphere, linkTo = new List<int>()};
pathNodeList.Add(pathNode);
for (var j = 4; j < segments.Length; j++)
{
int linkTo;
if (int.TryParse(segments[j], out linkTo) && linkTo > -1)
pathNode.linkTo.Add(linkTo);
else
break;
}
lineSuccess = true;
}
id++;
}
if (!lineSuccess)
Debug.LogError(string.Format("第{0}行数据读取失败,已经跳过!", i + 1));
}
// 重新构造链接
for (var i = 0; i < pathNodeList.Count; i++)
{
var source = pathNodeList[i];
for (var j = 0; j < source.linkTo.Count; j++)
{
var target = pathNodeList.Find(a => a.index == source.linkTo[j]);
if (target != null)
{
if (_links.Find(a => a.first == source.sphere && a.second == target.sphere) == null &&
_links.Find(a => a.first == target.sphere && a.second == source.sphere) == null)
{
_links.Add(new MyTuple<Transform, Transform>(source.sphere, target.sphere));
}
}
}
}
}
}
}
private void WriteFileRecord()
{
var sphereList = GetSphereList();
var maxNodeCount = 0;
for (var i = 0; i < sphereList.Count; i++)
{
var sphere = sphereList[i];
var nodeCount = 0;
for (var j = 0; j < _links.Count; j++)
{
var link = _links[j];
if (link.first != null && link.second != null &&
(link.first == sphere || link.second == sphere))
nodeCount++;
}
maxNodeCount = Mathf.Max(maxNodeCount, nodeCount);
}
var builder = new StringBuilder();
// Line 0
builder.Append("Id");
builder.Append('\t');
builder.Append("Desc");
builder.Append('\t');
builder.Append("PosX");
builder.Append('\t');
builder.Append("PosZ");
for (var i = 0; i < maxNodeCount; i++)
{
builder.Append('\t');
builder.Append("Node");
builder.Append(i + 1);
}
// Line 1
builder.Append('\r');
builder.Append("INT");
builder.Append('\t');
builder.Append("STRING");
builder.Append('\t');
builder.Append("INT");
builder.Append('\t');
builder.Append("INT");
for (var i = 0; i < maxNodeCount; i++)
{
builder.Append('\t');
builder.Append("INT");
}
// Line 2
builder.Append('\r');
builder.Append(_line2Text);
// Line 3
builder.Append('\r');
builder.Append("#点id");
builder.Append('\t');
builder.Append("程序不读");
builder.Append('\t');
builder.Append("点位置x100倍");
builder.Append('\t');
builder.Append("点位置y100倍");
for (var i = 0; i < maxNodeCount; i++)
{
builder.Append('\t');
builder.Append("可链接的节点");
builder.Append(i + 1);
}
// Data Lines
for (var i = 0; i < sphereList.Count; i++)
{
var source = sphereList[i];
builder.Append('\r');
builder.Append(i);
builder.Append('\t');
builder.Append("坐标");
builder.Append(i);
builder.Append('\t');
builder.Append(Mathf.RoundToInt(sphereList[i].position.x * 100f));
builder.Append('\t');
builder.Append(Mathf.RoundToInt(sphereList[i].position.z * 100f));
var nodePos = 0;
for (var j = 0; j < sphereList.Count; j++)
{
var target = sphereList[j];
if (_links.Find(a =>
a.first == source && a.second == target || a.first == target && a.second == source) != null)
{
builder.Append('\t');
builder.Append(j);
nodePos++;
}
}
for (var j = nodePos; j < maxNodeCount; j++)
{
builder.Append('\t');
builder.Append(-1);
}
}
// Extra Line at End
builder.Append('\r');
var fileName = GetFileName();
var directory = Path.GetDirectoryName(fileName);
if (string.IsNullOrEmpty(directory))
Debug.LogError("文件路径错误 " + fileName);
else
{
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
var bytes = Encoding.Default.GetBytes(builder.ToString());
File.WriteAllBytes(fileName, bytes);
}
}
private bool CheckAllPointPos()
{
var result = false;
var sphereList = GetSphereList();
for (var i = 0; i < sphereList.Count; i++)
if (!MarkPointPosition(sphereList[i]))
result = true;
return result;
}
private bool MarkPointPosition(Transform sphere)
{
var source = sphere.transform.position;
source.x = Mathf.Floor(source.x * 100f) * 0.01f;
source.z = Mathf.Floor(source.z * 100f) * 0.01f;
source.y = 100f;
var ray = new Ray(source, Vector3.down);
RaycastHit rayHit;
if (Physics.Raycast(ray, out rayHit, 200f, ActiveScene.terrainLayMask))
source = rayHit.point;
else
source.y = 0f;
NavMeshHit navHit;
var result = NavMesh.SamplePosition(source, out navHit, _navMeshSampleDist, NavMesh.AllAreas);
if (result)
{
if ((source.RemoveY() - navHit.position.RemoveY()).sqrMagnitude > _navMeshOffsetMax.ToSquare())
result = false;
else
source = navHit.position;
}
sphere.position = source;
var sphereRenderer = sphere.GetComponent<Renderer>();
if (sphereRenderer != null)
{
sphereRenderer.GetPropertyBlock(_materialProperty);
_materialProperty.SetColor("_Color", result ? Color.green : Color.red);
sphereRenderer.SetPropertyBlock(_materialProperty);
}
return result;
}
private Transform CreateSphereAt(int index, Vector3 pos)
{
var sphereObj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphereObj.name = "Point_" + index;
var sphereCol = sphereObj.GetComponent<Collider>();
if (sphereCol)
DestroyImmediate(sphereCol);
var sphere = sphereObj.transform;
sphere.SetParent(_root);
sphere.position = pos;
MarkPointPosition(sphere);
return sphere;
}
private List<Transform> GetSphereList()
{
var result = new List<Transform>();
if (_root != null)
{
foreach (Transform child in _root)
result.Add(child);
}
return result;
}
private void BuildLinks()
{
_links.Clear();
var sphereList = GetSphereList();
for (var i = 1; i < sphereList.Count; i++)
{
for (var j = 0; j < i; j++)
{
var source = sphereList[i].position;
var target = sphereList[j].position;
if ((source.RemoveY() - target.RemoveY()).sqrMagnitude < linkLengthSqr)
{
NavMeshHit hit;
if (!NavMesh.Raycast(source, target, out hit, NavMesh.AllAreas))
_links.Add(new MyTuple<Transform, Transform>(sphereList[i], sphereList[j]));
}
}
}
}
/// <summary>
/// 初始化时,临时使用的数据
/// </summary>
private class PathNodeData
{
public int index;
public List<int> linkTo;
public Transform sphere;
}
}