Files
Liubing\LB 3d8d4d18f3 first commit
先修复一下,错误的场景

删除不必要的钓场资产

修复into场景

update:更新meta文件,修复报错

update:修复资源

修复第一章节建造

修复建造第三章

update:删除多余内容

update:更新README.md

update:还原图标

update:更新README

update:更新配置
2026-04-28 02:17:22 +08:00

445 lines
16 KiB
C#

using System;
using Coffee.UISoftMaskInternal;
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.UI;
namespace Coffee.UISoftMask
{
[ExecuteAlways]
[Icon("Packages/com.coffee.softmask-for-ugui/Icons/SoftMaskIcon.png")]
public class SoftMaskable : MonoBehaviour, IMaterialModifier, IMaskable
{
#if UNITY_EDITOR
private static readonly int s_AlphaClipThreshold = Shader.PropertyToID("_AlphaClipThreshold");
private static readonly int s_MaskingShapeSubtract = Shader.PropertyToID("_MaskingShapeSubtract");
#endif
private static readonly int s_SoftMaskableStereo = Shader.PropertyToID("_SoftMaskableStereo");
private static readonly int s_SoftMaskOutsideColor = Shader.PropertyToID("_SoftMaskOutsideColor");
private static readonly int s_SoftMaskTex = Shader.PropertyToID("_SoftMaskTex");
private static readonly int s_SoftMaskColor = Shader.PropertyToID("_SoftMaskColor");
private static readonly int s_AllowDynamicResolution = Shader.PropertyToID("_AllowDynamicResolution");
private static readonly int s_AllowRenderScale = Shader.PropertyToID("_AllowRenderScale");
private static readonly int s_SoftMaskingPower = Shader.PropertyToID("_SoftMaskingPower");
private const float k_PowerMin = 0.5f;
private const float k_PowerMax = 5f;
[Tooltip("The graphic is ignored when soft-masking.")]
[SerializeField]
private bool m_IgnoreSelf;
[Tooltip("The child graphics are ignored when soft-masking.")]
[SerializeField]
private bool m_IgnoreChildren;
[Tooltip("Soft masking power.\n" +
"The higher this value, the faster it becomes transparent.\n" +
"If overlapping objects appear see-through, please adjust this value.")]
[SerializeField]
[PowerRange(k_PowerMin, k_PowerMax, 10)]
private float m_Power = 1f;
/// <summary>
/// The graphic is ignored when soft-masking.
/// </summary>
public bool ignoreSelf
{
get => m_IgnoreSelf;
set
{
if (m_IgnoreSelf == value) return;
m_IgnoreSelf = value;
UpdateHideFlags();
SetMaterialDirty();
}
}
/// <summary>
/// The child graphics are ignored when soft-masking.
/// </summary>
public bool ignoreChildren
{
get => m_IgnoreChildren;
set
{
if (m_IgnoreChildren == value) return;
m_IgnoreChildren = value;
UpdateHideFlags();
SetMaterialDirtyForChildren();
}
}
/// <summary>
/// The graphic is ignored when soft-masking.
/// </summary>
public bool ignored
{
get
{
if (m_IgnoreSelf) return true;
RecalculateStencilIfNeeded();
if (!_softMask || !_softMask.isActiveAndEnabled) return true;
var tr = transform.parent;
while (tr)
{
if (tr.TryGetComponent<SoftMaskable>(out var parent) && parent.ignoreChildren)
{
return true;
}
tr = tr.parent;
}
return false;
}
}
/// <summary>
/// Soft masking power.
/// The higher this value, the faster it becomes transparent.
/// If overlapping objects appear see-through, please adjust this value.
/// </summary>
public float power
{
get => m_Power;
set
{
value = Mathf.Clamp(value, k_PowerMin, k_PowerMax);
if (Mathf.Approximately(m_Power, value)) return;
m_Power = value;
SetMaterialDirty();
}
}
private Action _checkGraphic;
private MaskableGraphic _graphic;
private Material _maskableMaterial;
private bool _shouldRecalculateStencil;
private Mask _mask;
private SoftMask _softMask;
private int _softMaskDepth;
private int _stencilBits;
private bool isTerminal => _graphic is TerminalMaskingShape;
private void OnEnable()
{
UpdateHideFlags();
SoftMaskUtils.AddSoftMaskableOnChildren(this, false);
_shouldRecalculateStencil = true;
if (TryGetComponent(out _graphic))
{
// Check if the graphic is before this component.
var components = InternalListPool<Component>.Rent();
GetComponents(components);
var gIndex = components.IndexOf(_graphic);
var sIndex = components.IndexOf(this);
InternalListPool<Component>.Return(ref components);
if (sIndex < gIndex)
{
gameObject.AddComponent<SoftMaskable>();
Misc.Destroy(this);
return;
}
_graphic.SetMaterialDirty();
}
else
{
UIExtraCallbacks.onBeforeCanvasRebuild += _checkGraphic ?? (_checkGraphic = CheckGraphic);
}
#if UNITY_EDITOR
UIExtraCallbacks.onAfterCanvasRebuild +=
_updateSceneViewMatrix ?? (_updateSceneViewMatrix = UpdateSceneViewMatrix);
#endif
}
private void OnDisable()
{
UIExtraCallbacks.onBeforeCanvasRebuild -= _checkGraphic ?? (_checkGraphic = CheckGraphic);
if (_graphic)
{
_graphic.SetMaterialDirty();
}
_graphic = null;
_mask = null;
_softMask = null;
MaterialRepository.Release(ref _maskableMaterial);
#if UNITY_EDITOR
UIExtraCallbacks.onAfterCanvasRebuild -=
_updateSceneViewMatrix ?? (_updateSceneViewMatrix = UpdateSceneViewMatrix);
#endif
}
private void OnDestroy()
{
_graphic = null;
_maskableMaterial = null;
_mask = null;
_softMask = null;
_checkGraphic = null;
#if UNITY_EDITOR
_updateSceneViewMatrix = null;
#endif
}
private void OnTransformChildrenChanged()
{
SoftMaskUtils.AddSoftMaskableOnChildren(this, false);
}
private void OnTransformParentChanged()
{
_shouldRecalculateStencil = true;
}
void IMaskable.RecalculateMasking()
{
_shouldRecalculateStencil = true;
}
Material IMaterialModifier.GetModifiedMaterial(Material baseMaterial)
{
if (!UISoftMaskProjectSettings.softMaskEnabled)
{
MaterialRepository.Release(ref _maskableMaterial);
return baseMaterial;
}
if (!isActiveAndEnabled || !_graphic || !_graphic.canvas || !_graphic.maskable || isTerminal ||
baseMaterial == null || ignored)
{
MaterialRepository.Release(ref _maskableMaterial);
return baseMaterial;
}
RecalculateStencilIfNeeded();
var softMaskDepth = _softMask ? _softMask.softMaskDepth : -1;
if (softMaskDepth < 0 || 4 <= softMaskDepth)
{
_softMaskDepth = -1;
MaterialRepository.Release(ref _maskableMaterial);
return baseMaterial;
}
_softMaskDepth = softMaskDepth;
if (0 <= _softMaskDepth
&& _softMask == _mask
&& _softMask.SoftMaskingEnabled()
&& TryGetComponent<MaskingShape>(out var shape)
&& shape.isActiveAndEnabled
&& shape.maskingMethod == MaskingShape.MaskingMethod.Subtract)
{
_softMaskDepth -= 1;
}
Profiler.BeginSample("(SM4UI)[SoftMaskable] GetModifiedMaterial");
var isStereo = UISoftMaskProjectSettings.stereoEnabled && _graphic.canvas.IsStereoCanvas();
var useStencil = UISoftMaskProjectSettings.useStencilOutsideScreen;
var localId = (uint)(Mathf.InverseLerp(k_PowerMin, k_PowerMax, power) * (1 << 10));
#if UNITY_EDITOR
var threshold = 0f;
var subtract = false;
if (useStencil)
{
if (TryGetComponent(out MaskingShape s) && s.maskingMethod == MaskingShape.MaskingMethod.Subtract)
{
threshold = s.softnessRange.average;
subtract = true;
}
else
{
threshold = _softMask.softnessRange.average;
}
}
localId = (uint)(Mathf.Clamp01(threshold) * (1 << 8) + (subtract ? 1 << 9 : 0) + (localId << 10));
#endif
var hash = new Hash128(
(uint)baseMaterial.GetInstanceID(),
(uint)_softMask.softMaskBuffer.GetInstanceID(),
(uint)(_stencilBits + (isStereo ? 1 << 8 : 0) + (useStencil ? 1 << 9 : 0) + (_softMaskDepth << 10)),
localId);
if (!MaterialRepository.Valid(hash, _maskableMaterial))
{
Profiler.BeginSample("(SM4UI)[SoftMaskableMaterial] GetModifiedMaterial > Get or create material");
MaterialRepository.Get(hash, ref _maskableMaterial, x => new Material(x)
{
shader = UISoftMaskProjectSettings.shaderRegistry.FindOptionalShader(x.shader,
"(SoftMaskable)",
"Hidden/{0} (SoftMaskable)",
"Hidden/UI/Default (SoftMaskable)"),
hideFlags = HideFlags.HideAndDontSave
}, baseMaterial);
Profiler.EndSample();
}
Profiler.EndSample();
Profiler.BeginSample("(SM4UI)[SoftMaskableMaterial] Create > Set Properties");
_maskableMaterial.CopyPropertiesFromMaterial(baseMaterial);
_maskableMaterial.SetInt(s_AllowDynamicResolution, _softMask.allowDynamicResolution ? 1 : 0);
_maskableMaterial.SetInt(s_AllowRenderScale, _softMask.allowRenderScale ? 1 : 0);
_maskableMaterial.SetFloat(s_SoftMaskingPower, power);
_maskableMaterial.SetTexture(s_SoftMaskTex, _softMask.softMaskBuffer);
_maskableMaterial.SetInt(s_SoftMaskableStereo, isStereo ? 1 : 0);
_maskableMaterial.SetVector(s_SoftMaskColor, new Vector4(
0 <= _softMaskDepth ? 1 : 0,
1 <= _softMaskDepth ? 1 : 0,
2 <= _softMaskDepth ? 1 : 0,
3 <= _softMaskDepth ? 1 : 0
));
_maskableMaterial.EnableKeyword("SOFTMASKABLE");
#if UNITY_EDITOR
_maskableMaterial.SetFloat(s_AlphaClipThreshold, threshold);
_maskableMaterial.SetInt(s_MaskingShapeSubtract, subtract ? 1 : 0);
UISoftMaskProjectSettings.shaderRegistry.RegisterVariant(_maskableMaterial, "UI > Soft Mask");
_maskableMaterial.SetVector(s_SoftMaskOutsideColor,
UISoftMaskProjectSettings.useStencilOutsideScreen ? Vector4.one : Vector4.zero);
#endif
Profiler.EndSample();
return _maskableMaterial;
}
private void RecalculateStencilIfNeeded()
{
if (!isActiveAndEnabled)
{
_softMask = null;
_stencilBits = 0;
return;
}
if (!_shouldRecalculateStencil) return;
_shouldRecalculateStencil = false;
var useStencil = UISoftMaskProjectSettings.useStencilOutsideScreen;
_stencilBits = Utils.GetStencilBits(transform, false, useStencil, out _mask, out _softMask);
}
private void CheckGraphic()
{
if (_graphic || !TryGetComponent(out _graphic)) return;
UIExtraCallbacks.onBeforeCanvasRebuild -= _checkGraphic ?? (_checkGraphic = CheckGraphic);
gameObject.AddComponent<SoftMaskable>();
Misc.Destroy(this);
}
public void SetMaterialDirty()
{
if (!isActiveAndEnabled || !_graphic) return;
_graphic.SetMaterialDirty();
Misc.QueuePlayerLoopUpdate();
}
public void SetMaterialDirtyForChildren()
{
if (!isActiveAndEnabled || !_graphic) return;
var childCount = transform.childCount;
for (var i = 0; i < childCount; i++)
{
if (transform.GetChild(i).TryGetComponent<SoftMaskable>(out var child))
{
child.SetMaterialDirty();
child.SetMaterialDirtyForChildren();
}
}
}
internal void ReleaseMaterial()
{
MaterialRepository.Release(ref _maskableMaterial);
}
private void UpdateHideFlags()
{
hideFlags = ignoreSelf || ignoreChildren || !Mathf.Approximately(power, 1f)
? HideFlags.None
: HideFlags.DontSaveInEditor;
}
#if UNITY_EDITOR
private void OnValidate()
{
UpdateHideFlags();
}
private Action _updateSceneViewMatrix;
private static readonly int s_GameVp = Shader.PropertyToID("_GameVP");
private static readonly int s_GameVp2 = Shader.PropertyToID("_GameVP_2");
private void UpdateSceneViewMatrix()
{
if (!_graphic || !_graphic.canvas || !_maskableMaterial) return;
var mat = _graphic.GetMaterialForRendering();
if (!mat || FrameCache.TryGet(mat, nameof(UpdateSceneViewMatrix), out bool _))
{
return;
}
var canvas = _graphic.canvas.rootCanvas;
var isStereo = UISoftMaskProjectSettings.stereoEnabled && canvas.IsStereoCanvas();
if (!FrameCache.TryGet(canvas, "GameVp", out Matrix4x4 gameVp))
{
Profiler.BeginSample("(SM4UI)[SoftMaskable] (Editor) UpdateSceneViewMatrix > Calc GameVp");
var rt = canvas.transform as RectTransform;
var cam = canvas.worldCamera;
if (canvas && canvas.renderMode != RenderMode.ScreenSpaceOverlay && cam)
{
var eye = isStereo ? Camera.MonoOrStereoscopicEye.Left : Camera.MonoOrStereoscopicEye.Mono;
canvas.GetViewProjectionMatrix(eye, out var vMatrix, out var pMatrix);
gameVp = pMatrix * vMatrix;
}
else if (rt != null)
{
var pos = rt.position;
gameVp = Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity,
new Vector3(1 / pos.x, 1 / pos.y, -2 / 2000f)) * Matrix4x4.Translate(-pos);
}
else
{
gameVp = Matrix4x4.identity;
}
FrameCache.Set(canvas, "GameVp", gameVp);
Profiler.EndSample();
}
// Set view and projection matrices.
Profiler.BeginSample("(SM4UI)[SoftMaskable] (Editor) UpdateSceneViewMatrix > Set matrices");
mat.SetMatrix(s_GameVp, gameVp);
Profiler.EndSample();
// Calc Right eye matrices.
if (isStereo)
{
if (!FrameCache.TryGet(canvas, "GameVp2", out gameVp))
{
Profiler.BeginSample("(SM4UI)[SoftMaskable] (Editor) UpdateSceneViewMatrix > Calc GameVp2");
var eye = Camera.MonoOrStereoscopicEye.Right;
canvas.GetViewProjectionMatrix(eye, out var vMatrix, out var pMatrix);
gameVp = pMatrix * vMatrix;
FrameCache.Set(canvas, "GameVp2", gameVp);
Profiler.EndSample();
}
mat.SetMatrix(s_GameVp2, gameVp);
}
FrameCache.Set(mat, nameof(UpdateSceneViewMatrix), true);
}
#endif
}
}