using System.Collections.Generic; using Base; using Managers; using Prefab; using UnityEngine; using UnityEngine.Events; using Utils; namespace Entity { public class Outline : MonoBehaviour { // 边界的最小尺寸 public static readonly Vector2 MinimumBoundsSize = new(0.5f, 0.5f); // 实体身体的游戏对象 public GameObject body; // 描边渲染器 public SpriteRenderer outlineRenderer; // 描边碰撞体 public CapsuleCollider2D outlineCollider; // 进度条预制件 public ProgressBarPrefab progressBarPrefab; // 关联的实体 public Entity entity; // 缓存身体纹理大小 private Vector2 _cachedBodyTextureSize; // 缓存碰撞体大小 private Vector2 _cachedColliderSize; // 缓存碰撞体偏移 private Vector2 _cachedOffset; // 缓存是否已初始化标志 (明确它管理的是数据缓存状态) private bool _isDataCacheInitialized; // 修改部分1:变量更名 // 是否可以显示描边 public bool CanShow => Setting.Instance.CurrentSettings.developerMode && entity.canSelect && !UIInputControl.Instance.HasWindowOpen; /// /// 当鼠标进入描边区域时调用。 /// protected virtual void OnMouseEnter() { if (!CanShow) return; Show(); } /// /// 当鼠标离开描边区域时调用。 /// protected virtual void OnMouseExit() { Hide(); } /// /// 当鼠标停留在描边区域时每帧调用。 /// protected virtual void OnMouseOver() { if (!Program.Instance.CanOpenRightMenu || !CanShow) return; // 检测是否按下的是鼠标右键 if (Input.GetMouseButtonDown(1)) RightMenuManager.GenerateRightMenu(GetMenu(), Input.mousePosition); } /// /// 确保描边尺寸相关数据缓存已初始化。如果未初始化,则会计算并缓存。 /// 此方法保证内部的尺寸计算只执行一次。 /// private void EnsureDataCacheInitialized() // 修改部分2:新增私有方法 { if (_isDataCacheInitialized) return; // 如果数据缓存已初始化,则直接返回 // 计算并缓存所有必要尺寸 _cachedBodyTextureSize = CalculateBodyTextureSize(); _cachedColliderSize = CalculateColliderSize(); _cachedOffset = CalculateOffset(); _isDataCacheInitialized = true; // 标记数据缓存已初始化 } /// /// 初始化描边,将计算出的尺寸应用于描边渲染器、碰撞体和进度条。 /// 此方法确保数据缓存已初始化,并可以被重复调用以重新应用设置。 /// public virtual void Init() // 修改部分4:修改 Init 方法 { // 首先确保尺寸数据是可用的。这个调用会立即返回如果数据缓存已初始化。 EnsureDataCacheInitialized(); // 使用缓存值设置OutlineRenderer outlineRenderer.size = _cachedBodyTextureSize; // 使用缓存值设置OutlineCollider var colliderSize = _cachedColliderSize; // 使用缓存值 outlineCollider.direction = colliderSize.x > colliderSize.y ? CapsuleDirection2D.Horizontal : CapsuleDirection2D.Vertical; outlineCollider.size = colliderSize; outlineCollider.offset = _cachedOffset; // 使用缓存值 // 使用缓存值调整ProgressBarPrefab if (progressBarPrefab) { // 确保每次 Init 调用都能正确设置位置而不重复累加。 progressBarPrefab.transform.localPosition = new Vector3(0f, colliderSize.y * 2 / 3, 0f); // 逻辑修改:将 += 改为 = progressBarPrefab.transform.localScale = new Vector3(colliderSize.x, 1f / 10f, 1); } } /// /// 显示描边。 /// public void Show() { if (CanShow) outlineRenderer.enabled = true; } /// /// 隐藏描边。 /// public void Hide() { outlineRenderer.enabled = false; } /// /// 获取当前实体的碰撞体大小。在首次调用时自动计算并缓存。 /// /// 碰撞体大小的Vector2。 public Vector2 GetColliderSize() // 修改部分3:修改 GetColliderSize 方法 { EnsureDataCacheInitialized(); // 确保数据缓存已初始化 return _cachedColliderSize; } /// /// 获取身体的纹理总大小。在首次调用时自动计算并缓存。 /// /// 身体纹理大小的Vector2。 public Vector2 GetBodyTextureSize() // 修改部分3:修改 GetBodyTextureSize 方法 { EnsureDataCacheInitialized(); // 确保数据缓存已初始化 return _cachedBodyTextureSize; } /// /// 获取当前实体的碰撞体偏移。在首次调用时自动计算并缓存。 /// /// 碰撞体偏移的Vector2。 public Vector2 GetOffset() // 修改部分3:修改 GetOffset 方法 { EnsureDataCacheInitialized(); // 确保数据缓存已初始化 return _cachedOffset; } /// /// 计算并返回碰撞体大小。此方法是内部实现细节。 /// /// 碰撞体大小的Vector2。 private Vector2 CalculateColliderSize() { return !string.IsNullOrEmpty(entity.entityDef?.colliderSize) ? StringUtils.StringToVector2(entity.entityDef.colliderSize) : CalculateBodyTextureSize(); } /// /// 计算并返回身体纹理的总大小。此方法是内部实现细节。 /// /// 身体纹理总大小的Vector2。 private Vector2 CalculateBodyTextureSize() { // 获取所有子对象的 Renderer 组件 var renderers = body.GetComponentsInChildren(true); // 如果没有找到任何 Renderer,返回一个默认值 if (renderers.Length == 0) return MinimumBoundsSize; // 初始化 totalBounds 为第一个 Renderer 的 bounds var totalBounds = renderers[0].bounds; // 遍历剩余的 Renderer,将它们的 bounds 合并到 totalBounds 中 for (var i = 1; i < renderers.Length; i++) totalBounds.Encapsulate(renderers[i].bounds); // 获取合并后的包围盒的XY大小 var size = new Vector2(totalBounds.size.x, totalBounds.size.y); // 确保每个维度的大小都不小于最小限制 size.x = Mathf.Max(size.x, MinimumBoundsSize.x); size.y = Mathf.Max(size.y, MinimumBoundsSize.y); return size; } /// /// 计算并返回碰撞体偏移。此方法是内部实现细节。 /// /// 碰撞体偏移的Vector2。 private Vector2 CalculateOffset() { return string.IsNullOrEmpty(entity.entityDef?.colliderPosition) ? Vector2.zero : StringUtils.StringToVector2(entity.entityDef.colliderPosition); } /// /// 获取右键菜单项列表。 /// /// 包含菜单项名称和对应回调函数的列表。 protected virtual List<(string name, UnityAction callback)> GetMenu() { var result = new List<(string name, UnityAction callback)>(); if (entity.PlayerControlled) result.Add(("结束操控", EndControl)); else result.Add(("手动操控", StartControl)); result.Add(("杀死", () => entity.Kill())); result.Add(("变成笨蛋", BecomeDefault)); return result; } /// /// 将当前实体变为默认实体。 /// protected void BecomeDefault() { entity?.Kill(); if (entity) EntityManager.Instance.GenerateDefaultEntity(Program.Instance.FocusedDimensionId, entity.Position); } /// /// 开始玩家对实体的操控。 /// protected void StartControl() { entity.PlayerControlled = true; } /// /// 结束玩家对实体的操控。 /// protected void EndControl() { entity.PlayerControlled = false; } } }