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