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

442 lines
14 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.

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;
}
}