254 lines
8.5 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 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 模式返回 SpriteRendererMesh 模式返回 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
}
}