using UnityEditor;
using UnityEngine;
using UnityEditor.IMGUI.Controls;
using System.Collections.Generic;
using GameCoreEditor.Tool.DamageDebugger;
public partial class BulletMonitorWindow : EditorWindow
{
private E_DmgDebugType _dmgDebugType;
private E_DmgDebugType _preDmgDebugType;
private static BulletMonitorWindow window; //窗口实例对象,必须是一个static
private float m_ToolBarHeight; //工具栏高度,TreeView使用此偏移
/// <summary>
/// 窗口内显示的GUI面板
/// </summary>
[SerializeField] TreeViewState m_TreeViewState;
[SerializeField] MultiColumnHeaderState m_MultiColumnHeaderState;
BulletAnalyzeTreeView m_TreeView;
private GameObject _bulletRoot;
private GameObject BulletRoot
{
get
{
if (_bulletRoot == null)
{
_bulletRoot = GameObject.Find("GameLevelMGRoot/BulletRoot");
}
return _bulletRoot;
}
}
[MenuItem("合金弹头工具集/策划/子弹监控工具")]
public static void OpenMonitorWindow() //打开窗口函数,必须是static
{
window = GetWindow<BulletMonitorWindow>(false, "子弹监控工具", true); //实例化窗口
window.Show(); //显示窗口
}
//初始化树形结构
void OnEnable()
{
//检查是否已存在序列化视图状态(在程序集重新加载后
// 仍然存在的状态)
if (m_TreeViewState == null)
m_TreeViewState = new TreeViewState();
//初始化多列头
bool firstInit = m_MultiColumnHeaderState == null;
var headerState = BulletAnalyzeTreeView.CreateDefaultMultiColumnHeaderState(multiColumnTreeViewRect.width);
if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_MultiColumnHeaderState, headerState))
MultiColumnHeaderState.OverwriteSerializedFields(m_MultiColumnHeaderState, headerState);
m_MultiColumnHeaderState = headerState;
var multiColumnHeader = new MyMultiColumnHeader(headerState);
if (firstInit)
multiColumnHeader.ResizeToFit();
//搜索框初始化
searchField = new SearchField();
//根据viewState和多列头初始化树形视图
m_TreeView = new BulletAnalyzeTreeView(m_TreeViewState, multiColumnHeader);
//事件监听
DamageDebuggerManager.bulletClickedCallback += OnClickedCallback;
DamageDebuggerManager.bulletCreateCallback += BulletCreateCallback;
DamageDebuggerManager.bulletOnCollisionCallback += BulletCollisionInfoNewAddCallback;
}
public void OnDisable()
{
DamageDebuggerManager.bulletClickedCallback -= OnClickedCallback;
DamageDebuggerManager.bulletCreateCallback -= BulletCreateCallback;
DamageDebuggerManager.bulletOnCollisionCallback -= BulletCollisionInfoNewAddCallback;
}
#region 点击、生命周期、碰撞等注册事件的函数
private void OnClickedCallback(Bullet bullet)
{
Selection.activeGameObject = bullet.gameObject;
m_TreeView.SetBulletItemSelection(bullet.Ident);
}
private void BulletCreateCallback(Bullet bullet)
{
m_TreeView?.AddBullet(bullet);
}
private void ActorCreateCallback(Actor actor)
{
m_TreeView?.AddActor(actor);
}
private void BulletCollisionInfoNewAddCallback(Bullet bullet, E_DmgDebugEvent debugEvent, string hitBoxName = "", Actor actor = null)
{
m_TreeView.RefreshCollisionInfo(bullet.Ident, bullet.m_bulletData, debugEvent, hitBoxName, actor);
}
#endregion
#region GUI显示
Vector2 m_ScrollPosition;
Rect multiColumnTreeViewRect
{
get { return new Rect(20, 30, position.width - 40, position.height - 60); }
}
Rect searchToolbarRect
{
get { return new Rect(20f, m_ToolBarHeight, position.width - 40f, 20f); }
}
SearchField searchField;
private void DrawSeacrchBar()
{
m_TreeView.state.searchString = searchField.OnGUI(searchToolbarRect, m_TreeView.state.searchString);
}
private void OnGUI()
{
if (DamageDebuggerManager.Enable() == false)
{
DamageDebuggerManager.DamageDebuggerEnableSwitch();
}
DrawToolBar();
DrawSeacrchBar();
m_TreeView.OnGUI(new Rect(0, m_ToolBarHeight + 20f, position.width, position.height / 2));
// 获取选中的项
IList<int> selectedIds = m_TreeView.GetSelection();
if (selectedIds.Count > 0)
{
EditorGUILayout.Space(position.height / 2);
EditorGUILayout.BeginVertical();
m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition, GUILayout.Width(position.width));
//信息标题
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("配置ID");
EditorGUILayout.LabelField("名称");
EditorGUILayout.LabelField("当前状态");
EditorGUILayout.LabelField("创建时间");
EditorGUILayout.LabelField("创建帧号");
EditorGUILayout.LabelField("创建位置");
EditorGUILayout.LabelField("销毁时间");
EditorGUILayout.LabelField("销毁帧号");
EditorGUILayout.LabelField("销毁方式");
EditorGUILayout.LabelField("销毁位置");
EditorGUILayout.LabelField("碰撞类型");
EditorGUILayout.LabelField("碰撞对象");
EditorGUILayout.LabelField("碰撞位置");
EditorGUILayout.EndHorizontal();
// 遍历选中的项
foreach (int id in selectedIds)
{
GetSceneSelectedObj();
// 使用TreeViewItem来获取具体信息
var item = m_TreeView.FindBulletAnalyzeItem(id);
//场景
OnSelectionHierachyShow(item);
EditorGUILayout.BeginHorizontal();
if (item != null)
{
// 在TreeView下方显示信息
//EditorGUI.BeginChangeCheck();
EditorGUILayout.TextField(item.configID.ToString());
string newItemDisplayName = EditorGUILayout.TextField(item.displayName);
EditorGUILayout.TextField(item.curState);
EditorGUILayout.TextField(item.createTime);
EditorGUILayout.TextField(item.createFrame);
EditorGUILayout.TextField(item.createPos);
EditorGUILayout.TextField(item.deadTime);
EditorGUILayout.TextField(item.deadFrame);
EditorGUILayout.TextField(item.deadType);
EditorGUILayout.TextField(item.deadPos);
EditorGUILayout.TextField(item.hitBoxType);
EditorGUILayout.TextField(item.hitBoxName);
EditorGUILayout.TextField(item.hitPos);
//if (EditorGUI.EndChangeCheck())
//{
// 更新TreeViewItem的显示名称
// item.displayName = newItemDisplayName;
// m_TreeView.Reload();
//}
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
}
}
private void GetSceneSelectedObj()
{
GameObject selectedGameObject = Selection.activeGameObject;
// 检查游戏对象是否不为空
if (selectedGameObject != null)
{
// 获取Bullet组件
Bullet bullet = selectedGameObject.GetComponent<Bullet>();
if (bullet != null)
{
var bulletItem = m_TreeView.GetBulletItemByIdent(bullet.Ident);
if (bulletItem != null)
{
m_TreeView.SetSelection(new List<int> { bulletItem.id });
}
}
}
}
private void OnSelectionHierachyShow(BulletAnalyzeTreeItem item)
{
var bullets = BulletRoot.GetComponentsInChildren<BulletBase>();
foreach (BulletBase bullet in bullets)
{
if (bullet.Ident == item.ident)
{
Selection.activeGameObject = bullet.gameObject;
}
}
}
#region 主窗口
private GUIContent[] toolbarContent = null;
private GUIContent[] selectedListContent = null;
private int selectedId = 0;
private void OnSelectedChange(E_DmgDebugType preType, E_DmgDebugType curType)
{
if (DamageDebuggerManager.Enable())
{
//关掉之前的
if (DamageDebuggerManager.Enable() && DamageDebuggerManager.Instance.TypeDebugConfigSet.TryGetValue(preType, out var preTypeConfig))
{
preTypeConfig.SetEnable(false); //切换打开状态
DamageDebuggerManager.Instance.TypeDebugConfigSet[preType] = preTypeConfig;
}
//打开现在的
if (DamageDebuggerManager.Enable() && DamageDebuggerManager.Instance.TypeDebugConfigSet.TryGetValue(_dmgDebugType, out var curTypeConfig))
{
curTypeConfig.SetEnable(true); //切换打开状态
DamageDebuggerManager.Instance.TypeDebugConfigSet[curType] = curTypeConfig;
}
}
}
private void OpenAnotherWindow<T>(string title) where T : EditorWindow
{
T window = EditorWindow.GetWindow<T>(true); //实例化窗口
window.titleContent = new GUIContent(title);
window.Show(); //显示窗口
}
/// <summary>
/// 顶边栏
/// </summary>
void DrawToolBar()
{
if (toolbarContent == null || toolbarContent.Length == 0)
{
toolbarContent = new GUIContent[]
{
new GUIContent("子弹命中率统计"), //index = 0
new GUIContent("DPS分析"),
new GUIContent("子弹追踪开关"),
new GUIContent("局内伤害类型实时显示"),
};
}
if (selectedListContent == null || selectedListContent.Length == 0)
{
selectedListContent = new GUIContent[]
{
new GUIContent("不显示"), //index = 0
new GUIContent("死亡信息"), //index = 0
new GUIContent("碰撞信息"),
new GUIContent("碰撞失效情况"),
};
}
// 绘制工具栏
GUILayoutOption[] toolbarOptions =
{
GUILayout.ExpandWidth(true),
GUILayout.Height(40)
};
GUILayout.BeginHorizontal(EditorStyles.toolbar, toolbarOptions);
if (GUILayout.Button(toolbarContent[0]))
{
OpenAnotherWindow<BulletAccuracyCheckerWindow>("子弹命中率统计");
}
if (GUILayout.Button(toolbarContent[1]))
{
OpenAnotherWindow<DamageShow>("DPS记录");
}
DamageDebuggerManager.Instance.openTrace = GUILayout.Toggle(DamageDebuggerManager.Instance.openTrace, toolbarContent[2]);
GUILayout.FlexibleSpace();
GUILayout.Label(toolbarContent[3]);
selectedId = EditorGUILayout.Popup(selectedId, selectedListContent);
DamageDebuggerUIManager.IsShowInGame = selectedId > 0;
if (DamageDebuggerUIManager.IsShowInGame)
{
DamageDebuggerUIManager.UIDisplayTime = 5;
DamageDebuggerUIManager.MaxDisplayCount = 10;
}
_dmgDebugType = (E_DmgDebugType)selectedId;
OnSelectedChange(_preDmgDebugType, _dmgDebugType);
_preDmgDebugType = _dmgDebugType;
GUILayout.EndHorizontal();
m_ToolBarHeight = GUILayoutUtility.GetLastRect().yMax;
}
#endregion
private void OnInspectorUpdate()
{
m_TreeView?.Refresh();
Repaint();
}
void DrawTreeBottomToolBar(Rect rect)
{
GUILayout.BeginArea(rect);
using (new EditorGUILayout.HorizontalScope())
{
var style = "miniButton";
//展开和关闭
if (GUILayout.Button("Expand All", style))
{
m_TreeView.ExpandAll();
}
if (GUILayout.Button("Collapse All", style))
{
m_TreeView.CollapseAll();
}
//自由空行
GUILayout.FlexibleSpace();
//自由空行
GUILayout.FlexibleSpace();
if (GUILayout.Button("Set sorting", style))
{
var myColumnHeader = (MyMultiColumnHeader)m_TreeView.
multiColumnHeader;
myColumnHeader.SetSortingColumns(new int[] { 4, 3, 2 }, new
[] { true, false, true });
myColumnHeader.mode = MyMultiColumnHeader.Mode.LargeHeader;
}
//
GUILayout.Label("Header: ", "minilabel");
if (GUILayout.Button("Large", style))
{
var myColumnHeader = (MyMultiColumnHeader)m_TreeView.
multiColumnHeader;
myColumnHeader.mode = MyMultiColumnHeader.Mode.LargeHeader;
}
if (GUILayout.Button("Default", style))
{
var myColumnHeader = (MyMultiColumnHeader)m_TreeView.
multiColumnHeader;
myColumnHeader.mode = MyMultiColumnHeader.Mode.
DefaultHeader;
}
if (GUILayout.Button("No sort", style))
{
var myColumnHeader = (MyMultiColumnHeader)m_TreeView.
multiColumnHeader;
myColumnHeader.mode = MyMultiColumnHeader.Mode.
MinimumHeaderWithoutSorting;
}
GUILayout.Space(10);
//if (GUILayout.Button("values <-> controls", style))
//{
// m_TreeView.showControls = !m_TreeView.showControls;
//}
}
GUILayout.EndArea();
}
#endregion
#region 多行设置
internal class MyMultiColumnHeader : MultiColumnHeader
{
Mode m_Mode;
public enum Mode
{
LargeHeader,
DefaultHeader,
MinimumHeaderWithoutSorting
}
public MyMultiColumnHeader(MultiColumnHeaderState state)
: base(state)
{
mode = Mode.DefaultHeader;
}
public Mode mode
{
get
{
return m_Mode;
}
set
{
m_Mode = value;
switch (m_Mode)
{
case Mode.LargeHeader:
canSort = true;
height = 37f;
break;
case Mode.DefaultHeader:
canSort = true;
height = DefaultGUI.defaultHeight;
break;
case Mode.MinimumHeaderWithoutSorting:
canSort = false;
height = DefaultGUI.minimumHeight;
break;
}
}
}
protected override void ColumnHeaderGUI(MultiColumnHeaderState.Column column, Rect headerRect, int columnIndex)
{
// Default column header gui
base.ColumnHeaderGUI(column, headerRect, columnIndex);
// Add additional info for large header
if (mode == Mode.LargeHeader)
{
// Show example overlay stuff on some of the columns
if (columnIndex > 2)
{
headerRect.xMax -= 3f;
var oldAlignment = EditorStyles.largeLabel.alignment;
EditorStyles.largeLabel.alignment = TextAnchor.UpperRight;
GUI.Label(headerRect, 36 + columnIndex + "%", EditorStyles.largeLabel);
EditorStyles.largeLabel.alignment = oldAlignment;
}
}
}
};
#endregion
}
TreeVIew
using System;
using System.Collections.Generic;
using System.Linq;
using GameCoreEditor.SkillLink;
using GameCoreEditor.Tool.DamageDebugger;
using MS.GameLevel;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using static CSkillInfo;
using static GameCoreEditor.SkillLink.SkillLinkProfilerWindow;
public partial class BulletMonitorWindow : EditorWindow
{
class BulletAnalyzeTreeView : TreeView
{
const int KRowHeight = 20;
const int KToggleWidth = 20;
private static int m_TreeViewIdPosition = 0;
private static int NextId => m_TreeViewIdPosition++;
BulletAnalyzeTreeItem root;
private Dictionary<int, BulletAnalyzeTreeItem> items = new Dictionary<int, BulletAnalyzeTreeItem>();
NBDict<ulong, BulletAnalyzeTreeItem_Actor> actorMap = new NBDict<ulong, BulletAnalyzeTreeItem_Actor>();//角色数据
// All columns
enum MyColumns
{
Icon,
Value,
Name,
}
public enum SortOption
{
Name,
Value
}
#region TreeView编辑器方法
/// <summary>
/// 构建单个actor的子弹目录
/// </summary>
/// <param name="actor"></param>
/// <param name="actorNode"></param>
/// <param name="depth"></param>
void BuildActorCategory(Actor actor, BulletAnalyzeTreeItem_Actor actorNode, int depth)
{
GameLevelMG gameLevelMg = GetTempLevelMG();
// 子弹列表
if (gameLevelMg != null)
{
BulletManager bulletManager = gameLevelMg.BulletManager;
foreach (var actorBullet in bulletManager.GetAllBullet(actor))
{
if (actorBullet == null)
{
continue;
}
var bullet = bulletManager.Get(actorBullet);
if (actorNode.bulletMap.ContainsKey(bullet.Ident))
{
continue;
}
var pRoot = new BulletAnalyzeTreeItem_Bullet();
pRoot.id = NextId;
pRoot.depth = depth;
pRoot.CanDraw = true;
pRoot.ItemInvalid = false;
pRoot.configID = bullet.Config.ConfigID;
pRoot.ident = bullet.Ident;
pRoot.createTime = bullet.CreateTime.ToString();
pRoot.createFrame = gameLevelMg.currentGame.LogicFrameCount.ToString();
pRoot.createPos = bullet.m_bulletData.m_bornPos.ToString();
//pRoot.baseName = "子弹列表";
pRoot.displayName = bullet.name.ToString();
//actorNode.bulletRoot = pRoot;
if (actorNode.children != null)
actorNode.children.Insert(0, pRoot);
else
actorNode.AddChild(pRoot);
actorNode.bulletMap.Add(bullet.Ident, pRoot);
}
}
Repaint();
}
/// <summary>
/// 构造函数,带多个列
/// </summary>
/// <param name="treeViewState"></param>
/// <param name="multicolumnHeader"></param>
public BulletAnalyzeTreeView(TreeViewState treeViewState, MultiColumnHeader multicolumnHeader) : base(treeViewState, multicolumnHeader)
{
rowHeight = 20;
columnIndexForTreeFoldouts = 2;
showBorder = true;
customFoldoutYOffset = 0;
//multicolumnHeader.sortingChanged += OnSortingChanged; 检测行排序何时发生改变
Reload();
}
/// <summary>
/// 构造行
/// </summary>
/// <param name="root"></param>
/// <returns></returns>
protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
{
var rows = base.BuildRows(root); // 获取默认的行列表
List<TreeViewItem> filteredRows = new List<TreeViewItem>();
bool hasResults = false;
foreach (var row in rows)
{
if (DoesItemMatchSearch(row))
{
filteredRows.Add(row);
hasResults = true;
}
}
return filteredRows;
}
/// <summary>
/// 搜索匹配
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private bool DoesItemMatchSearch(TreeViewItem item)
{
// 如果搜索字符串为空,显示所有项
if (string.IsNullOrEmpty(state.searchString))
{
return true;
}
// 检查项的displayName是否包含searchString
return item.displayName.IndexOf(state.searchString, StringComparison.OrdinalIgnoreCase) >= 0;
}
/// <summary>
/// 建树函数
/// </summary>
/// <returns>最终的树结构</returns>
protected override TreeViewItem BuildRoot()
{
if(root == null)
root = new BulletAnalyzeTreeItem();
if (items == null) items = new Dictionary<int, BulletAnalyzeTreeItem>();
//this.rootItem?.children.Clear();
m_TreeViewIdPosition = 0;
root.id = NextId;
root.depth = -1;
if(!items.ContainsKey(root.id))
items.Add(root.id, root);
// 构造Actor信息
var actorCount = BuildActorMap(root, 0);
if (actorCount == 0) // 没有actor
return AddEmptyChild(root);
return root;
}
/// <summary>
/// 重写每行GUI的显示方法
/// </summary>
/// <param name="args"></param>
protected override void RowGUI(RowGUIArgs args)
{
var item = (BulletAnalyzeTreeItem)args.item;
for (int i = 0; i < args.GetNumVisibleColumns(); ++i)
{
CellGUI(args.GetCellRect(i), item, (MyColumns)args.GetColumn(i), ref args);
}
Repaint();
}
/// <summary>
/// 增加新的单节点
/// </summary>
/// <param name="root">根节点</param>
/// <returns>返回树结构</returns>
BulletAnalyzeTreeItem AddEmptyChild(BulletAnalyzeTreeItem root)
{
BulletAnalyzeTreeItem leaf = new BulletAnalyzeTreeItem();
leaf.id = NextId;
leaf.depth = 0;
items.Add(leaf.id, leaf);
root.CanDraw = false;
root.ItemInvalid = true;
root.AddChild(leaf);
return root;
}
void CellGUI(Rect cellRect, BulletAnalyzeTreeItem item, MyColumns column, ref RowGUIArgs args)
{
// Center cell rect vertically (makes it easier to place controls, icons etc in the cells)
CenterRectUsingSingleLineHeight(ref cellRect);
switch (column)
{
case MyColumns.Icon:
{
if (item.enableTrace != GUI.Toggle(cellRect, item.enableTrace, "轨迹显示"))
{
item.enableTrace = !item.enableTrace;
var gameLevelMg = GetTempLevelMG();
var bullet = gameLevelMg.BulletManager.Get(item.ident);
if (bullet != null)
{
bullet.openTraceView = item.enableTrace;
}
}
}
break;
case MyColumns.Value:
{
GUI.Label(cellRect, item.createFrame);
}
break;
case MyColumns.Name:
{
//这里只需要调整一下显示位置,其他的就直接使用基类的显示方法
args.rowRect = cellRect;
base.RowGUI(args);
}
break;
}
}
protected override void SelectionChanged(IList<int> selectedIDs)
{
foreach (var id in selectedIDs)
{
BulletAnalyzeTreeItem item = FindItem(id, root) as BulletAnalyzeTreeItem;
}
}
#endregion
#region Sorting
void OnSortingChanged(MultiColumnHeader multiColumnHeader)
{
SortIfNeeded(rootItem, GetRows());
}
void SortIfNeeded(TreeViewItem root, IList<TreeViewItem> rows)
{
if (rows.Count <= 1)
return;
if (multiColumnHeader.sortedColumnIndex == -1)
{
return; // No column to sort for (just use the order the data are in)
}
// Sort the roots of the existing tree items
//SortByMultipleColumns();
//TreeToList(root, rows);
Repaint();
}
#endregion
#region 轨迹追踪显示
public BulletTrackTracer trackTracer = new BulletTrackTracer();
#endregion
#region 生命周期与碰撞
/// <summary>
/// 通过消息更新角色
/// </summary>
/// <param name="newActor"></param>
public void AddActor(Actor newActor)
{
GameLevelMG gameLevelMg = GetTempLevelMG();
//actorMap.Clear();
if (null != gameLevelMg)
{
ActorManager actorManager = gameLevelMg.ActorManager;
foreach (Actor actor in actorManager.GetAllActor())
{
if (actor == null) continue;
// 获取所有的actor信息
//如果已经存在,则直接结束
if (actorMap.ContainsKey(actor.InstanceId))
{
return;
}
}
BulletAnalyzeTreeItem_Actor item = new BulletAnalyzeTreeItem_Actor();
item.id = NextId;
item.CanDraw = true;
item.ItemInvalid = false;
item.displayName = newActor.name;
item.createTime = gameLevelMg.currentGame.Time.ToString();
item.createFrame = gameLevelMg.currentGame.LogicFrameCount.ToString();
actorMap.Add(newActor.InstanceId, item);
root.AddChild(item);
}
base.Repaint();
}
/// <summary>
/// 通过消息更新子弹
/// </summary>
/// <param name="bullet"></param>
public void AddBullet(Bullet bullet)
{
var actor = bullet.Actor;
if(actor == null)
{
return;
}
var actorNode = actorMap[actor.InstanceId];
if (actorNode == null)
{
return;
}
var gameLevelMg = GetTempLevelMG();
var pRoot = new BulletAnalyzeTreeItem_Bullet();
pRoot.id = NextId;
pRoot.CanDraw = true;
pRoot.ItemInvalid = false;
pRoot.configID = bullet.Config.ConfigID;
pRoot.ident = bullet.Ident;
pRoot.createTime = bullet.CreateTime.ToString();
pRoot.createFrame = gameLevelMg.currentGame.LogicFrameCount.ToString();
pRoot.createPos = bullet.m_bulletData.m_bornPos.ToString();
//pRoot.baseName = "子弹列表";
pRoot.displayName = bullet.name.ToString();
//actorNode.bulletRoot = pRoot;
if (actorNode.children != null)
actorNode.children.Insert(0, pRoot);
else
actorNode.AddChild(pRoot);
actorNode.bulletMap.Add(bullet.Ident, pRoot);
base.Repaint();
}
/// <summary>
/// 获得关卡信息
/// </summary>
/// <returns></returns>
static public GameLevelMG GetTempLevelMG()
{
GameLevelMG levelMG = null;
#if MS_SERVER
levelMG = GameLevelMG.UnsafeInstance;
#else
levelMG = GameLevelMG.Instance;
#endif
return levelMG;
}
/// <summary>
/// 构造角色目录
/// </summary>
/// <param name="root"></param>
/// <param name="depth"></param>
/// <returns></returns>
int BuildActorMap(BulletAnalyzeTreeItem root, int depth)
{
GameLevelMG gameLevelMg = GetTempLevelMG();
//actorMap.Clear();
if (null != gameLevelMg)
{
ActorManager actorManager = gameLevelMg.ActorManager;
foreach (Actor actor in actorManager.GetAllActor())
{
if (actor == null) continue;
// 获取所有的actor信息
//如果已经存在,则重新构建目录并跳过新生成
if (actorMap.ContainsKey(actor.InstanceId))
{
BulletAnalyzeTreeItem_Actor thisActor = actorMap[actor.InstanceId];
BuildActorCategory(actor, thisActor, depth + 1);
continue;
}
BulletAnalyzeTreeItem_Actor item = new BulletAnalyzeTreeItem_Actor();
item.id = NextId;
item.depth = depth;
item.CanDraw = true;
item.displayName = actor.name;
item.createTime = gameLevelMg.currentGame.Time.ToString();
item.createFrame = gameLevelMg.currentGame.LogicFrameCount.ToString();
//BuildActorCategory(actor, item, depth + 1);
actorMap.Add(actor.InstanceId, item);
root.AddChild(item);
}
}
Repaint();
return actorMap.Count;
}
#endregion
#region 外部调用
/// <summary>
/// 重新建树
/// </summary>
public void Refresh()
{
BuildActorMap(root, 0);
Repaint();
}
/// <summary>
/// 通过树id获取子弹结构
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public BulletAnalyzeTreeItem FindBulletAnalyzeItem(int id)
{
BulletAnalyzeTreeItem item = FindItem(id, root) as BulletAnalyzeTreeItem;
return item;
}
public void SetBulletItemSelection(int ident)
{
var bulletItem = GetBulletItemByIdent(ident);
List<int> selectid = new List<int>();
selectid.Add(bulletItem.id);
SetSelection(selectid);
}
public BulletAnalyzeTreeItem_Bullet GetBulletItemByIdent(int ident)
{
GameLevelMG gameLevelMg = GetTempLevelMG();
foreach (var actorItem in actorMap)
{
foreach (var bulletItem in actorItem.Value.bulletMap)
{
if (bulletItem.Value.ident == ident)
{
return bulletItem.Value;
}
}
}
return null;
}
/// <summary>
/// 通过ident获取子弹结构并修改信息
/// </summary>
/// <param name="ident"></param>
/// <param name="deadType"></param>
public void RefreshDeadInfo(int ident, BulletData bulletData)
{
var bulletItem = GetBulletItemByIdent(ident);
GameLevelMG gameLevelMg = GetTempLevelMG();
if(bulletItem!=null)
{
bulletItem.deadTime = gameLevelMg.currentGame.Time.ToString();
bulletItem.deadFrame = gameLevelMg.currentGame.UpdateFrameCount.ToString();
bulletItem.deadType = bulletData.m_DeadType.ToString();
bulletItem.deadPos = bulletData.m_lastFramePosition.ToString();
bulletItem.curState = "已销毁";
}
base.Repaint();
}
/// <summary>
/// 子弹碰撞信息刷新
/// </summary>
/// <param name="ident"></param>
/// <param name="bulletData"></param>
/// <param name="debugEvent"></param>
/// <param name="hitboxName"></param>
/// <param name="actor"></param>
public void RefreshCollisionInfo(int ident, BulletData bulletData,E_DmgDebugEvent debugEvent,string hitboxName,Actor actor)
{
GameLevelMG gameLevelMg = GetTempLevelMG();
foreach (var actorItem in actorMap)
{
foreach (var bulletItem in actorItem.Value.bulletMap)
{
if (bulletItem.Value.ident == ident)
{
if(debugEvent == E_DmgDebugEvent.BulletDead)
{
bulletItem.Value.deadTime = gameLevelMg.currentGame.Time.ToString();
bulletItem.Value.deadFrame = gameLevelMg.currentGame.LogicFrameCount.ToString();
bulletItem.Value.deadType = bulletData.m_DeadType.ToString();
bulletItem.Value.deadPos = bulletData.m_lastFramePosition.ToString();
bulletItem.Value.curState = "已销毁";
}
else
{
bulletItem.Value.hitBoxName = hitboxName;
bulletItem.Value.hitPos = bulletData.m_lastFramePosition.ToString();
switch (debugEvent)
{
case E_DmgDebugEvent.BulletHitBlock:
bulletItem.Value.hitBoxType = "打墙";
bulletItem.Value.actorName = "";
break;
case E_DmgDebugEvent.BulletHitColliderWithIgnoreTag:
bulletItem.Value.hitBoxType = "打含无视子弹Tag物体";
bulletItem.Value.actorName = "";
break;
case E_DmgDebugEvent.BulletHitThroughWall:
bulletItem.Value.hitBoxType = "穿墙打人";
bulletItem.Value.actorName = actor.name;
break;
case E_DmgDebugEvent.TriggerMultiHitBlock:
bulletItem.Value.hitBoxType = "命中但是被墙挡";
bulletItem.Value.actorName = actor.name;
break;
}
}
}
}
}
base.Repaint();
}
/// <summary>
/// 创建多列数据表头
/// </summary>
/// <param name="treeViewWidth"></param>
/// <returns></returns>
public static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState(float treeViewWidth)
{
var columns = new[]
{
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent(EditorGUIUtility.FindTexture("EchoFilter Icon"), "查看轨迹"),
contextMenuText = "Track",
headerTextAlignment = TextAlignment.Center, //中心对齐
sortedAscending = true,
sortingArrowAlignment = TextAlignment.Right, //箭头在右边对齐
width = 80,
minWidth = 40,
maxWidth = 100,
autoResize = false,
allowToggleVisibility = true
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("创建帧号"),
headerTextAlignment = TextAlignment.Left,
sortedAscending = true,
sortingArrowAlignment = TextAlignment.Center,
width = 100,
minWidth = 60,
autoResize = false,
allowToggleVisibility = false
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("子弹名称"),
headerTextAlignment = TextAlignment.Left,
sortedAscending = true,
sortingArrowAlignment = TextAlignment.Center,
width = 300,
minWidth = 100,
autoResize = false,
allowToggleVisibility = false
},
};
var state = new MultiColumnHeaderState(columns);
return state;
}
#endregion
}
}
Item
using UnityEditor;
using UnityEngine;
using UnityEditor.IMGUI.Controls;
using MS.GameLevel;
using System.Collections.Generic;
using MS.Framework.GameConfig;
using MS.Framework;
using PbProtocol;
public partial class BulletMonitorWindow : EditorWindow
{
public class BulletAnalyzeTreeItem : TreeViewItem
{
/// <summary>
/// 禁用行绘制
/// </summary>
public bool ItemInvalid;
/// <summary>
/// 禁用单元格绘制
/// </summary>
public bool CanDraw;
public bool enableTrace;
public bool isDead;
public int ident;
public string curState = "已创建";
public string createTime;
public string createFrame;
public string createPos;
public string deadTime;
public string deadFrame;
public string deadPos;
public string deadType;
public string hitBoxType;
public string hitBoxName;
public string hitPos;
public uint configID;
public string actorName;
public StatNode NodeValue;
public string baseName = "";
private string displayString;
}
class BulletAnalyzeTreeItem_Actor : BulletAnalyzeTreeItem
{
//public BulletAnalyzeTreeItem bulletRoot;
public NBDict<int, BulletAnalyzeTreeItem_Bullet> bulletMap = new NBDict<int, BulletAnalyzeTreeItem_Bullet>();
}
class BulletAnalyzeTreeItem_Bullet : BulletAnalyzeTreeItem
{
}
}
DamageDebugManager
//--------------------------------------------------------------------------
/**
@file : DamageDebuggerManager
@date : 2023/1/11 16:07
@author : eddiezhao
**/
//--------------------------------------------------------------------------
using DocumentFormat.OpenXml.Drawing.Charts;
using MS.CanvasNode;
using MS.Framework.Protocol.PbProtocol.AILab;
using MS.Tool;
using System.Collections.Generic;
using UnityEngine;
using static DmgDebugInfoDetail;
public class DamageDebuggerManager
{
#if UNITY_EDITOR && MS_ENABLE_DEBUG && !MS_SERVER
public struct DmgDebugTypeConfig
{
public bool Enable;
public Color LableColor;
public string TypeName;
public void SwitchEnable()
{
Enable = !Enable;
}
public void SetEnable(bool enable)
{
Enable = enable;
}
}
public NBDict<E_DmgDebugType, DmgDebugTypeConfig> TypeDebugConfigSet = new NBDict<E_DmgDebugType, DmgDebugTypeConfig>();
public static bool Enable()
{
return Instance != null;
}
public static void DamageDebuggerEnableSwitch()
{
if (Instance == null)
{
Instance = new DamageDebuggerManager();
}
else
{
Instance.Clear();
Instance = null;
}
MSCheckMenuItem.RefreshGroup("DmgDebug");
}
public void DamageDebugTypeSwitch(E_DmgDebugType debugType)
{
if (TypeDebugConfigSet.TryGetValue(debugType, out var typeConfig))
{
typeConfig.SwitchEnable();
TypeDebugConfigSet[debugType] = typeConfig;
}
}
private DamageDebuggerManager()
{
// 如果新增类型,需要在下面添加相关的配置,UI会基于这些配置来显示
TypeDebugConfigSet.Add(E_DmgDebugType.BulletDeadType, new DmgDebugTypeConfig()
{
Enable = false,
LableColor = new Color(255 / 255f, 255 / 255f, 255 / 255f, 255 / 255f),
TypeName = "子弹死亡信息",
});
TypeDebugConfigSet.Add(E_DmgDebugType.BulletCollision, new DmgDebugTypeConfig()
{
Enable = false,
LableColor = new Color(255 / 255f, 255 / 255f, 255 / 255f, 255 / 255f),
TypeName = "子弹碰撞信息",
});
TypeDebugConfigSet.Add(E_DmgDebugType.TriggerMultiHitConditionIgnore, new DmgDebugTypeConfig()
{
Enable = false,
LableColor = new Color(255 / 255f, 255 / 255f, 255 / 255f, 255 / 255f),
TypeName = "伤害框碰撞失效情况",
});
}
private void Clear()
{
TypeDebugConfigSet.Clear();
}
public static DamageDebuggerManager Instance;
public NBDict<E_DmgDebugType, NBDict<E_DmgDebugEvent, NBDict<ulong, DmgDebugInfoDetail>>> allDebugInfos = new NBDict<E_DmgDebugType, NBDict<E_DmgDebugEvent, NBDict<ulong, DmgDebugInfoDetail>>>();
// 目前只给 日志编辑器窗口 使用
private NBDict<E_DmgDebugType, NBList<DmgDebugInfoDetail>> dmgDebugInfoDetailLogSet = new NBDict<E_DmgDebugType, NBList<DmgDebugInfoDetail>>();
// 这里做一个静态回调,让编辑器代码主动监听对应的事件
public static event System.Action<E_DmgDebugType, DmgDebugInfoDetail> DamageDebugInfoChangeCallback;
public static event System.Action<E_DmgDebugType, DmgDebugInfoDetail> DamageDebugInfoNewAddCallback;
private void SendDmgDebugEvent(E_DmgDebugType debugType, DmgDebugInfoDetail dmgDebugInfoDetail)
{
//if (!typeDebugEnable[debugType])return;
if (!allDebugInfos.TryGetValue(debugType, out var dict))
{
dict = new NBDict<E_DmgDebugEvent, NBDict<ulong, DmgDebugInfoDetail>>();
allDebugInfos.Add(debugType, dict);
}
if (!dict.TryGetValue(dmgDebugInfoDetail.debugEvent, out var debugInfo))
{
debugInfo = new NBDict<ulong, DmgDebugInfoDetail>();
dict.Add(dmgDebugInfoDetail.debugEvent, debugInfo);
}
/// 这里需要做一下去重,因为有可能同一个事件,同一个对象,但是不同时间点,会触发多次
if (debugInfo.ContainsKey(dmgDebugInfoDetail.hash))
{
debugInfo[dmgDebugInfoDetail.hash] = dmgDebugInfoDetail;
if (TypeDebugConfigSet.TryGetValue(debugType, out var typeConfig) && typeConfig.Enable)
{
GetDmgDebugInfoDetailSet(debugType)
.Add(dmgDebugInfoDetail);
DamageDebugInfoChangeCallback?.Invoke(debugType, dmgDebugInfoDetail);
}
}
else
{
debugInfo.Add(dmgDebugInfoDetail.hash, dmgDebugInfoDetail);
if (TypeDebugConfigSet.TryGetValue(debugType, out var typeConfig) && typeConfig.Enable)
{
GetDmgDebugInfoDetailSet(debugType)
.Add(dmgDebugInfoDetail);
DamageDebugInfoNewAddCallback?.Invoke(debugType, dmgDebugInfoDetail);
}
}
//Logger.LogErrorP($"[伤害Debug工具][开发中调试信息][{dmgDebugInfoDetail.label}]{dmgDebugInfoDetail.info} {dmgDebugInfoDetail.hash}");
}
public void SendDmgDebugEvent_Bullet(E_DmgDebugType debugType, E_DmgDebugEvent debugEvent, Bullet bullet, string extraInfo = "", Actor actor = null)
{
SendDmgDebugEvent(debugType, new BulletDebugInfo(bullet, debugEvent, extraInfo, actor));
}
public void SendDmgDebugEvent_TriggerMultiHit(E_DmgDebugType debugType, E_DmgDebugEvent debugEvent, Actor hitActor, string extraInfo = "")
{
SendDmgDebugEvent(debugType, new TriggerMultiHitDebugInfo(debugEvent, hitActor, extraInfo));
}
/// <summary>
/// 通过类型获取对应的调试信息
/// </summary>
/// <param name="dmgDebugType"></param>
/// <returns></returns>
public NBList<DmgDebugInfoDetail> GetDmgDebugInfoDetailSet(E_DmgDebugType dmgDebugType)
{
if (dmgDebugInfoDetailLogSet.TryGetValue(dmgDebugType, out var dmgDebugInfoDetailSet) == false)
{
dmgDebugInfoDetailSet = new NBList<DmgDebugInfoDetail>(1000);
dmgDebugInfoDetailLogSet.Add(dmgDebugType, dmgDebugInfoDetailSet);
}
return dmgDebugInfoDetailSet;
}
#region 子弹监控工具
//-------------------点击反馈------------------
public static event System.Action<Bullet> bulletClickedCallback;
public void SendBulletClickedEvent(Bullet bullet)=>bulletClickedCallback?.Invoke(bullet);
//-------------------路径追踪------------------
public bool openTrace;
//-------------------生命周期------------------
//子弹监控工具使用(其实根本就不需要那么多东西,避免创建新对象直接传Bullet)
public static event System.Action<Bullet> bulletCreateCallback;
public static event System.Action<Actor> actorCreateCallback;
public static event System.Action<Bullet, E_DmgDebugEvent, string,Actor> bulletOnCollisionCallback;
public void SendBulletCollisionEvent(Bullet bullet, E_DmgDebugEvent debugEvent, string hitBoxName = "",Actor actor = null)
{
bulletOnCollisionCallback?.Invoke(bullet, debugEvent, hitBoxName,actor);
}
/// <summary>
/// 合并之前工具和现在工具需要的消息函数,相互兼容。
/// </summary>
/// <param name="debugType"></param>
/// <param name="debugEvent"></param>
/// <param name="bullet"></param>
/// <param name="extraInfo"></param>
/// <param name="actor"></param>
public void SendAllDebugEvent(E_DmgDebugType debugType, E_DmgDebugEvent debugEvent, Bullet bullet, string extraInfo = "", Actor actor = null)
{
bulletOnCollisionCallback?.Invoke(bullet, debugEvent, extraInfo, actor);
if(debugEvent != E_DmgDebugEvent.TriggerMultiHitBlock)
{
SendDmgDebugEvent(debugType, new BulletDebugInfo(bullet, debugEvent, extraInfo, actor));
}
else
{
SendDmgDebugEvent(debugType, new TriggerMultiHitDebugInfo(debugEvent, actor, extraInfo));
}
}
public void SendBulletCreateInfo(Bullet bullet)
{
bulletCreateCallback.Invoke(bullet);
}
public void SendActorCreateInfo(Actor actor)
{
actorCreateCallback.Invoke(actor);
}
#endregion
#region 子弹监控工具 -- 子弹追踪
#endregion
#endif
}
#if UNITY_EDITOR && MS_ENABLE_DEBUG && !MS_SERVER
public enum E_DmgDebugType
{
None = 0,
BulletDeadType = 1, //子弹死亡类型
BulletCollision = 2, //子弹碰撞
TriggerMultiHitConditionIgnore = 3, //伤害框碰撞失效情况
}
public enum E_DmgDebugEvent
{
None = 0,
BulletDead = 1,
BulletHitColliderWithIgnoreTag = 2,
BulletHitBlock = 3,
TriggerMultiHitBlock = 4,
BulletHitThroughWall = 5,
}
public abstract class DmgDebugInfoDetail
{
public E_DmgDebugEvent debugEvent;
public string label; //显示的标签文本
public Vector3 eventShowPos; //事件发生的位置 用于显示标签
public string info; //显示在log list里显示的内容
public ulong hash; //相同hash的代表 在逻辑上是同一个事件 要顶掉之前的
protected void SetInfoDetail(E_DmgDebugEvent debugEvent, string label, Vector3 eventShowPos, string info, ulong hash)
{
this.debugEvent = debugEvent;
this.info = info;
this.label = label;
this.eventShowPos = eventShowPos;
this.hash = hash;
}
}
public class BulletDebugInfo : DmgDebugInfoDetail
{
public BulletDebugInfo(Bullet bullet, E_DmgDebugEvent debugEvent, string extraInfo, Actor actor = null)
{
switch (debugEvent)
{
case E_DmgDebugEvent.BulletDead:
SetInfoDetail(debugEvent, bullet.m_bulletData.m_DeadType.ToString(), bullet.GetPosition(), $"子弹:{bullet.name} id:{bullet.m_ConfigID} 死亡 类型为{bullet.m_bulletData.m_DeadType}", (ulong)bullet.Ident);
break;
case E_DmgDebugEvent.BulletHitColliderWithIgnoreTag:
SetInfoDetail(debugEvent, "Ignore", bullet.GetPosition(), $"子弹:{bullet.name} id:{bullet.m_ConfigID} 碰到了带有无视子弹tag的 {extraInfo}", (ulong)(extraInfo.GetHashCode() + bullet.Ident));
break;
case E_DmgDebugEvent.BulletHitBlock:
SetInfoDetail(debugEvent, "打墙", bullet.GetPosition(), $"子弹:{bullet.name} id:{bullet.m_ConfigID} 撞到墙 {extraInfo} 了", (ulong)(extraInfo.GetHashCode() + bullet.Ident));
break;
case E_DmgDebugEvent.BulletHitThroughWall:
SetInfoDetail(debugEvent, "穿墙", bullet.GetPosition(), $"子弹:{bullet.name} id:{bullet.m_ConfigID} 穿墙 {extraInfo} 打人 {actor.name} 了", (ulong)(extraInfo.GetHashCode() + bullet.Ident + actor.Ident));
break;
default:
Logger.LogErrorP($"[伤害debug工具]该事件{debugEvent}不属于子弹debug类型 @eddiezhao");
break;
}
}
}
public class TriggerMultiHitDebugInfo : DmgDebugInfoDetail
{
public TriggerMultiHitDebugInfo(E_DmgDebugEvent debugEvent, Actor hitActor, string extraInfo)
{
switch (debugEvent)
{
case E_DmgDebugEvent.TriggerMultiHitBlock:
SetInfoDetail(debugEvent, "被墙挡", hitActor.BosomPoint == null? hitActor.ActorPos : hitActor.BosomPoint.transform.position, $"伤害框命中 {hitActor.name} 但被墙 {extraInfo} 挡了", (ulong)(extraInfo.GetHashCode() + hitActor.Ident));
break;
default:
Logger.LogErrorP($"[伤害debug工具]该事件{debugEvent}不属于伤害框debug类型 @eddiezhao");
break;
}
}
}
#endif
UIManager
#if MS_ENABLE_DEBUG && !MS_SERVER
using MS.GameLevel;
using MS.UI;
using MS.UI.UI3D;
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace GameCoreEditor.Tool.DamageDebugger
{
#region 对象池
internal interface IObjectPool<T>
{
T Allocate();
void Release(T _object);
void ClearPool();
}
internal class ObjectPool<T> : IObjectPool<T>
{
private Stack<T> objectStack;
public event Func<T> CreateObjectAction;
public event Action<T> DestroyObjectAction;
public event Action<T> GetObjectAction;
public event Action<T> ReleaseObjectAction;
/// <summary>
/// 池最大容量,超出的对象再回收以后立即删除
/// </summary>
private int poolMaxCount;
public void Initialize(Func<T> createObjectAction, Action<T> destroyObjectAction, Action<T> getObjectAction, Action<T> releaseObjectAction)
{
CreateObjectAction = createObjectAction;
DestroyObjectAction = destroyObjectAction;
GetObjectAction = getObjectAction;
ReleaseObjectAction = releaseObjectAction;
objectStack = new Stack<T>(10);
}
public T Allocate()
{
T _object;
if (objectStack.Count != 0)
{
_object = objectStack.Pop();
}
else
{
if (CreateObjectAction == null)
{
_object = default;
}
else
{
_object = this.CreateObjectAction.Invoke();
}
}
GetObjectAction?.Invoke(_object);
return _object;
}
public void Release(T _object)
{
ReleaseObjectAction?.Invoke(_object);
objectStack.Push(_object);
}
public void ClearPool()
{
if (DestroyObjectAction != null)
{
foreach (var _object in objectStack)
{
DestroyObjectAction.Invoke(_object);
}
}
objectStack.Clear();
}
}
#endregion
#region 时钟
internal interface IClock
{
event Action<float> TickCallback;
void Dispose();
}
internal class ColckMono : MonoBehaviour, IClock
{
public event Action<float> TickCallback;
private void Update()
{
if (GameLevelMG.Instance == null)
return;
if (GameLevelMG.Instance.isGamePaused)
return;
TickCallback?.Invoke(Time.deltaTime);
}
public void Dispose()
{
Destroy(gameObject);
}
public static IClock CreateColck(Transform parent = null)
{
var colckGameObject = new GameObject($"{nameof(ColckMono)}");
GameObject.DontDestroyOnLoad(colckGameObject);
colckGameObject.transform.parent = parent;
return colckGameObject.AddComponent<ColckMono>();
}
}
#endregion
#region UI
internal interface ITime
{
float Time { get; set; }
}
internal interface IUIDamageDebugInfo : ITime
{
E_DmgDebugType DmgDebugType { get; set; }
DmgDebugInfoDetail DebugInfoDetail { get; set; }
int MaxDisplayCount { set; }
void Display();
void Hide();
void UpdateUIPosition(Vector3 position);
void RefreshUI();
void Dispose();
}
#endregion
/// <summary>
/// 单局伤害调试UI管理
/// </summary>
internal class DamageDebuggerUIManager
{
public static bool IsShowInGame;
public static float UIDisplayTime = 3f;
public static int MaxDisplayCount = 10;
private static bool _init = false;
public static DamageDebuggerUIManager Instance { get; private set; }
private IClock _clock;
private IObjectPool<IUIDamageDebugInfo> _uiDamageDebugInfoPool;
public NBList<IUIDamageDebugInfo> _displayUISet;
private Transform _uiRoot;
private Camera _mainCamera;
private Camera _uiCamera;
private UIDamageDebugInfo _uiDamageDebugPrefab;
[RuntimeInitializeOnLoadMethod]
private static void RuntimeInitializeOnLoadMethod()
{
#if UNITY_PLAY_TIME_CHECK
UnityEngine.Debug.LogWarning($"InitializeOnLoadMethod1 {System.DateTime.Now.ToString("HH:mm:ss.fff")}");
#endif
// 每次进入播放模式 静态对象 会被重置,所以在运行时的时候注册下回调
UnregisterDamageDebugEvent(); // 防止多次注册
RegisterDamageDebugEvent();
#if UNITY_PLAY_TIME_CHECK
UnityEngine.Debug.LogWarning($"InitializeOnLoadMethod2 {System.DateTime.Now.ToString("HH:mm:ss.fff")}");
#endif
}
private static void RegisterDamageDebugEvent()
{
DamageDebuggerManager.DamageDebugInfoChangeCallback += DamageDebugInfoChangeCallback;
DamageDebuggerManager.DamageDebugInfoNewAddCallback += DamageDebugInfoNewAddCallback;
}
private static void UnregisterDamageDebugEvent()
{
DamageDebuggerManager.DamageDebugInfoChangeCallback -= DamageDebugInfoChangeCallback;
DamageDebuggerManager.DamageDebugInfoNewAddCallback -= DamageDebugInfoNewAddCallback;
}
private static void DamageDebugInfoChangeCallback(E_DmgDebugType dmgDebugType, DmgDebugInfoDetail dmgDebugInfoDetail)
{
if (IsShowInGame == false)
return;
TryInit();
foreach (var ui in Instance._displayUISet)
{
if (ui.DmgDebugType == dmgDebugType && ui.DebugInfoDetail.debugEvent == dmgDebugInfoDetail.debugEvent && ui.DebugInfoDetail.hash == dmgDebugInfoDetail.hash)
{
ui.Time = 0;
ui.MaxDisplayCount = MaxDisplayCount;
ui.DebugInfoDetail = dmgDebugInfoDetail;
Instance.UpdateDebugUIPosition(ui);
ui.RefreshUI();
return;
}
}
AddIUIDamageDebugInfo(dmgDebugType, dmgDebugInfoDetail);
}
private static void DamageDebugInfoNewAddCallback(E_DmgDebugType dmgDebugType, DmgDebugInfoDetail dmgDebugInfoDetail)
{
if (IsShowInGame == false)
return;
TryInit();
AddIUIDamageDebugInfo(dmgDebugType, dmgDebugInfoDetail);
}
private static void AddIUIDamageDebugInfo(E_DmgDebugType dmgDebugType, DmgDebugInfoDetail dmgDebugInfoDetail)
{
var ui = Instance._uiDamageDebugInfoPool.Allocate();
ui.Time = 0;
ui.MaxDisplayCount = MaxDisplayCount;
ui.DmgDebugType = dmgDebugType;
ui.DebugInfoDetail = dmgDebugInfoDetail;
Instance.UpdateDebugUIPosition(ui);
ui.RefreshUI();
var removeCount = (Instance._displayUISet.Count + 1) - MaxDisplayCount;
if (removeCount > 0)
{
for (int i = 0; i < removeCount; i++)
{
Instance._uiDamageDebugInfoPool.Release(Instance._displayUISet[i]);
}
Instance._displayUISet.RemoveRange(0, removeCount);
}
Instance._displayUISet.Add(ui);
}
private static void TryInit()
{
if (Application.isPlaying == false)
return;
if (_init)
return;
if (Instance != null)
Instance.Dispose();
Instance = new DamageDebuggerUIManager();
Instance.Init();
_init = true;
}
private void Init()
{
Application.quitting += Dispose;
// init UI
{
var rootGO = new GameObject("DamageDebugger UIRoot");
rootGO.AddComponent<Canvas3D>();
_uiRoot = rootGO.transform;
if (UGUIRoot.Instance)
{
_mainCamera = UGUIRoot.Instance.GetMainCamera;
_uiCamera = UGUIRoot.Instance.UI3DCamera;
if (UGUIRoot.Instance.UI3DRoot != null)
{
_uiRoot.parent = UGUIRoot.Instance.UI3DRoot;
}
else
{
Object.DontDestroyOnLoad(rootGO);
}
}
_uiDamageDebugPrefab = UIDamageDebugInfo.LoadPrefab(_uiRoot);
}
_clock = ColckMono.CreateColck(_uiRoot);
_clock.TickCallback += Tick;
var pool = new ObjectPool<IUIDamageDebugInfo>();
_uiDamageDebugInfoPool = pool;
pool.Initialize(
() => GameObject.Instantiate(_uiDamageDebugPrefab, _uiRoot),
(item) => item.Dispose(),
(item) => item.Display(),
(item) => item.Hide()
);
_displayUISet = new NBList<IUIDamageDebugInfo>();
}
private void Dispose()
{
UnregisterDamageDebugEvent();
Application.quitting -= Dispose;
_init = false;
_clock.TickCallback -= Tick;
var clock = _clock as ColckMono;
if (clock != null)
GameObject.Destroy(clock);
_clock = null;
_uiDamageDebugInfoPool.ClearPool();
_uiDamageDebugInfoPool = null;
foreach (var item in _displayUISet)
{
item.Dispose();
}
_displayUISet = null;
// Dispose UI
{
GameObject.Destroy(_uiRoot);
_uiRoot = null;
_mainCamera = null;
_uiCamera = null;
UIDamageDebugInfo.UnloadPrefab(_uiDamageDebugPrefab);
_uiDamageDebugPrefab = null;
}
}
private void Tick(float deltaTime)
{
for (var i = _displayUISet.Count - 1; i > -1; i--)
{
var ui = _displayUISet[i];
if (ui.Time >= UIDisplayTime)
{
_displayUISet.RemoveAt(i);
_uiDamageDebugInfoPool.Release(ui);
}
ui.Time += deltaTime;
UpdateDebugUIPosition(ui);
}
}
private void UpdateDebugUIPosition(IUIDamageDebugInfo damageDebug)
{
var wordPositon = damageDebug.DebugInfoDetail.eventShowPos;
var viewportPosition = _mainCamera.WorldToViewportPoint(wordPositon);
damageDebug.UpdateUIPosition(_uiCamera.ViewportToWorldPoint(viewportPosition));
}
}
}
#endif