254 lines
8.5 KiB
C#
254 lines
8.5 KiB
C#
using UnityEngine;
|
||
|
||
namespace OctoberStudio.Rendering
|
||
{
|
||
/// <summary>
|
||
/// 渲染器模式:Sprite (2D) 或 Mesh (3D)
|
||
/// </summary>
|
||
public enum RendererMode
|
||
{
|
||
Sprite = 0,
|
||
Mesh = 1,
|
||
}
|
||
|
||
/// <summary>
|
||
/// 统一渲染适配器。
|
||
/// 让 OctoberStudio 的业务逻辑无需关心使用 <see cref="SpriteRenderer"/> 还是 <see cref="MeshRenderer"/>。
|
||
/// 在 Prefab 上挂载此组件,选择 Mode,勾选 Auto Fetch 即可让组件自动绑定子级 Renderer。
|
||
///
|
||
/// 典型用法:
|
||
/// 2D Prefab → Mode=Sprite,自动找 SpriteRenderer,行为与原版完全一致
|
||
/// 3D Prefab → Mode=Mesh,自动找 MeshRenderer,所有 Color/Flip/Sorting 操作映射到 Mesh 上
|
||
///
|
||
/// 业务代码统一通过适配器属性 (Color/FlipX/SortingOrder/...) 操作,
|
||
/// 切换 2D/3D 时无需重写逻辑层。
|
||
/// </summary>
|
||
[DisallowMultipleComponent]
|
||
[ExecuteAlways]
|
||
[AddComponentMenu("OctoberStudio/Rendering/Renderer Adapter")]
|
||
public class RendererAdapter : MonoBehaviour
|
||
{
|
||
[Tooltip("渲染模式:Sprite=2D 模式 (默认), Mesh=3D 模式")]
|
||
[SerializeField] private RendererMode mode = RendererMode.Sprite;
|
||
|
||
[Tooltip("启用后会在 OnValidate / Reset 自动查找并绑定子级 Renderer 组件")]
|
||
[SerializeField] private bool autoFetch = true;
|
||
|
||
[Tooltip("自动查找时的搜索根 (留空 = 自己)")]
|
||
[SerializeField] private Transform fetchRoot;
|
||
|
||
[Tooltip("是否在 mode 切换时自动启用/禁用对应的 Renderer (推荐勾选避免两个同时显示)")]
|
||
[SerializeField] private bool applyEnabledOnModeChange = true;
|
||
|
||
[Header("Bound Renderers (可手动指定,否则自动 fetch)")]
|
||
[SerializeField] private SpriteRenderer spriteRenderer;
|
||
[SerializeField] private MeshRenderer meshRenderer;
|
||
|
||
#region Public Read-only
|
||
|
||
public RendererMode Mode => mode;
|
||
public bool IsSpriteMode => mode == RendererMode.Sprite;
|
||
public bool IsMeshMode => mode == RendererMode.Mesh;
|
||
public SpriteRenderer Sprite => spriteRenderer;
|
||
public MeshRenderer Mesh => meshRenderer;
|
||
|
||
/// <summary>
|
||
/// 当前激活的 Renderer (Sprite 模式返回 SpriteRenderer,Mesh 模式返回 MeshRenderer)
|
||
/// </summary>
|
||
public Renderer Active => mode == RendererMode.Sprite ? (Renderer)spriteRenderer : meshRenderer;
|
||
|
||
#endregion
|
||
|
||
#region Unified API
|
||
|
||
public bool IsVisible => Active != null && Active.isVisible;
|
||
|
||
public Bounds Bounds => Active != null
|
||
? Active.bounds
|
||
: new Bounds(transform.position, Vector3.zero);
|
||
|
||
public bool Enabled
|
||
{
|
||
get => Active != null && Active.enabled;
|
||
set { if (Active != null) Active.enabled = value; }
|
||
}
|
||
|
||
public Color Color
|
||
{
|
||
get
|
||
{
|
||
if (mode == RendererMode.Sprite) return spriteRenderer != null ? spriteRenderer.color : Color.white;
|
||
return meshRenderer != null && meshRenderer.sharedMaterial != null && meshRenderer.sharedMaterial.HasProperty(MainColorPropID)
|
||
? meshRenderer.material.GetColor(MainColorPropID)
|
||
: Color.white;
|
||
}
|
||
set
|
||
{
|
||
if (mode == RendererMode.Sprite)
|
||
{
|
||
if (spriteRenderer != null) spriteRenderer.color = value;
|
||
}
|
||
else
|
||
{
|
||
if (meshRenderer != null && meshRenderer.material != null && meshRenderer.material.HasProperty(MainColorPropID))
|
||
meshRenderer.material.SetColor(MainColorPropID, value);
|
||
}
|
||
}
|
||
}
|
||
|
||
public float Alpha
|
||
{
|
||
get => Color.a;
|
||
set { var c = Color; c.a = value; Color = c; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 水平翻转。
|
||
/// Sprite 模式:spriteRenderer.flipX
|
||
/// Mesh 模式:transform.localScale.x 符号
|
||
/// </summary>
|
||
public bool FlipX
|
||
{
|
||
get
|
||
{
|
||
if (mode == RendererMode.Sprite) return spriteRenderer != null && spriteRenderer.flipX;
|
||
return transform.localScale.x < 0;
|
||
}
|
||
set
|
||
{
|
||
if (mode == RendererMode.Sprite)
|
||
{
|
||
if (spriteRenderer != null) spriteRenderer.flipX = value;
|
||
}
|
||
else
|
||
{
|
||
var s = transform.localScale;
|
||
s.x = Mathf.Abs(s.x) * (value ? -1f : 1f);
|
||
transform.localScale = s;
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool FlipY
|
||
{
|
||
get
|
||
{
|
||
if (mode == RendererMode.Sprite) return spriteRenderer != null && spriteRenderer.flipY;
|
||
return transform.localScale.y < 0;
|
||
}
|
||
set
|
||
{
|
||
if (mode == RendererMode.Sprite)
|
||
{
|
||
if (spriteRenderer != null) spriteRenderer.flipY = value;
|
||
}
|
||
else
|
||
{
|
||
var s = transform.localScale;
|
||
s.y = Mathf.Abs(s.y) * (value ? -1f : 1f);
|
||
transform.localScale = s;
|
||
}
|
||
}
|
||
}
|
||
|
||
public int SortingOrder
|
||
{
|
||
get => Active != null ? Active.sortingOrder : 0;
|
||
set { if (Active != null) Active.sortingOrder = value; }
|
||
}
|
||
|
||
public string SortingLayerName
|
||
{
|
||
get => Active != null ? Active.sortingLayerName : "Default";
|
||
set { if (Active != null) Active.sortingLayerName = value; }
|
||
}
|
||
|
||
public int SortingLayerID
|
||
{
|
||
get => Active != null ? Active.sortingLayerID : 0;
|
||
set { if (Active != null) Active.sortingLayerID = value; }
|
||
}
|
||
|
||
public Material Material
|
||
{
|
||
get => Active != null ? Active.material : null;
|
||
set { if (Active != null) Active.material = value; }
|
||
}
|
||
|
||
public Material SharedMaterial
|
||
{
|
||
get => Active != null ? Active.sharedMaterial : null;
|
||
set { if (Active != null) Active.sharedMaterial = value; }
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Lifecycle
|
||
|
||
private static readonly int MainColorPropID = Shader.PropertyToID("_Color");
|
||
|
||
private void Reset()
|
||
{
|
||
FetchComponents();
|
||
ApplyMode();
|
||
}
|
||
|
||
private void OnValidate()
|
||
{
|
||
if (autoFetch) FetchComponents();
|
||
if (applyEnabledOnModeChange) ApplyMode();
|
||
}
|
||
|
||
private void Awake()
|
||
{
|
||
if (autoFetch) FetchComponents();
|
||
if (applyEnabledOnModeChange) ApplyMode();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Public Operations
|
||
|
||
/// <summary>
|
||
/// 在子级中查找并绑定对应类型的 Renderer (优先用根节点上的,再退到子级)。
|
||
/// </summary>
|
||
[ContextMenu("Fetch Renderer Components")]
|
||
public void FetchComponents()
|
||
{
|
||
var root = fetchRoot != null ? fetchRoot : transform;
|
||
if (spriteRenderer == null)
|
||
{
|
||
spriteRenderer = root.GetComponent<SpriteRenderer>();
|
||
if (spriteRenderer == null) spriteRenderer = root.GetComponentInChildren<SpriteRenderer>(true);
|
||
}
|
||
if (meshRenderer == null)
|
||
{
|
||
meshRenderer = root.GetComponent<MeshRenderer>();
|
||
if (meshRenderer == null) meshRenderer = root.GetComponentInChildren<MeshRenderer>(true);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据当前 mode 启用/禁用对应 Renderer (避免两个同时显示)。
|
||
/// </summary>
|
||
[ContextMenu("Apply Mode")]
|
||
public void ApplyMode()
|
||
{
|
||
if (spriteRenderer != null) spriteRenderer.enabled = (mode == RendererMode.Sprite);
|
||
if (meshRenderer != null) meshRenderer.enabled = (mode == RendererMode.Mesh);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 运行时切换 mode (推荐通过这个方法切换,避免直接改 Inspector 字段不生效)。
|
||
/// </summary>
|
||
public void SetMode(RendererMode newMode)
|
||
{
|
||
if (mode == newMode) return;
|
||
mode = newMode;
|
||
if (applyEnabledOnModeChange) ApplyMode();
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|