mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 09:16:01 +08:00
[unity] SkeletonGraphic now supports automatic scaling based on RectTransform bounds. Closes #1640.
This commit is contained in:
parent
2179cd7c86
commit
91cc6ddfaa
@ -97,6 +97,7 @@
|
||||
* `SkeletonRenderTexture` and `SkeletonGraphicRenderTexture` components now support automatic down-scaling when required size on screen exceeds `Max Render Texture Size`.
|
||||
* Added `Spine/SkeletonGraphic Fill` shader to provide functionality of `Spine/Skeleton Fill` shader for `SkeletonGraphic`.
|
||||
* Lit Spine URP shaders (`Universal Render Pipeline/Spine/Sprite` and `Universal Render Pipeline/Spine/Skeleton Lit`) now support `Forward+` rendering path as introduced by Unity 2022.2 and URP version 14.
|
||||
* `SkeletonGraphic` now supports automatic scaling based on its `RectTransform` bounds. Automatic scaling can be enabled by setting the added `Layout Scale Mode` Inspector property to either `Width Controls Height`, `Height Controls Width`, `FitInParent` or `EnvelopeParent`. It is set to `None` by default to keep previous behaviour and avoid breaking existing projects. To modify the reference layout bounds, hit the additional `Edit Layout Bounds` toggle button to switch into edit mode, adjust the bounds or hit `Match RectTransform with Mesh`, and hit the button again when done adjusting. The skeleton will now be scaled accordingly to fit the reference layout bounds to the object's `RectTransform`.
|
||||
|
||||
* **Breaking changes**
|
||||
* Made `SkeletonGraphic.unscaledTime` parameter protected, use the new property `UnscaledTime` instead.
|
||||
|
||||
@ -57,7 +57,7 @@ namespace Spine.Unity.Editor {
|
||||
SerializedProperty additiveMaterial, multiplyMaterial, screenMaterial;
|
||||
SerializedProperty skeletonDataAsset, initialSkinName;
|
||||
SerializedProperty startingAnimation, startingLoop, timeScale, freeze,
|
||||
updateTiming, updateWhenInvisible, unscaledTime, tintBlack;
|
||||
updateTiming, updateWhenInvisible, unscaledTime, tintBlack, layoutScaleMode, editReferenceRect;
|
||||
SerializedProperty initialFlipX, initialFlipY;
|
||||
SerializedProperty meshGeneratorSettings;
|
||||
SerializedProperty allowMultipleCanvasRenderers, separatorSlotNames, enableSeparatorSlots, updateSeparatorPartLocation;
|
||||
@ -76,8 +76,8 @@ namespace Spine.Unity.Editor {
|
||||
protected bool TargetIsValid {
|
||||
get {
|
||||
if (serializedObject.isEditingMultipleObjects) {
|
||||
foreach (UnityEngine.Object o in targets) {
|
||||
SkeletonGraphic component = (SkeletonGraphic)o;
|
||||
foreach (UnityEngine.Object c in targets) {
|
||||
SkeletonGraphic component = (SkeletonGraphic)c;
|
||||
if (!component.IsValid)
|
||||
return false;
|
||||
}
|
||||
@ -129,6 +129,8 @@ namespace Spine.Unity.Editor {
|
||||
freeze = so.FindProperty("freeze");
|
||||
updateTiming = so.FindProperty("updateTiming");
|
||||
updateWhenInvisible = so.FindProperty("updateWhenInvisible");
|
||||
layoutScaleMode = so.FindProperty("layoutScaleMode");
|
||||
editReferenceRect = so.FindProperty("editReferenceRect");
|
||||
|
||||
meshGeneratorSettings = so.FindProperty("meshGenerator").FindPropertyRelative("settings");
|
||||
meshGeneratorSettings.isExpanded = SkeletonRendererInspector.advancedFoldout;
|
||||
@ -141,6 +143,13 @@ namespace Spine.Unity.Editor {
|
||||
separatorSlotNames.isExpanded = true;
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
foreach (UnityEngine.Object c in targets) {
|
||||
SkeletonGraphic component = (SkeletonGraphic)c;
|
||||
component.EditReferenceRect = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
|
||||
if (UnityEngine.Event.current.type == EventType.Layout) {
|
||||
@ -299,6 +308,20 @@ namespace Spine.Unity.Editor {
|
||||
EditorGUILayout.PropertyField(raycastTarget);
|
||||
if (maskable != null) EditorGUILayout.PropertyField(maskable);
|
||||
|
||||
EditorGUILayout.PropertyField(layoutScaleMode);
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(layoutScaleMode.intValue == 0)) {
|
||||
EditorGUILayout.BeginHorizontal(GUILayout.Height(EditorGUIUtility.singleLineHeight + 5));
|
||||
EditorGUILayout.PrefixLabel("Edit Layout Bounds");
|
||||
editReferenceRect.boolValue = GUILayout.Toggle(editReferenceRect.boolValue,
|
||||
EditorGUIUtility.IconContent("EditCollider"), EditorStyles.miniButton, GUILayout.Width(40f));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
if (layoutScaleMode.intValue == 0) {
|
||||
editReferenceRect.boolValue = false;
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(editReferenceRect.boolValue == false && layoutScaleMode.intValue != 0)) {
|
||||
EditorGUILayout.BeginHorizontal(GUILayout.Height(EditorGUIUtility.singleLineHeight + 5));
|
||||
EditorGUILayout.PrefixLabel("Match RectTransform with Mesh");
|
||||
if (GUILayout.Button("Match", EditorStyles.miniButton, GUILayout.Width(65f))) {
|
||||
@ -307,6 +330,7 @@ namespace Spine.Unity.Editor {
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
if (TargetIsValid && !isInspectingPrefab) {
|
||||
EditorGUILayout.Space();
|
||||
@ -320,7 +344,6 @@ namespace Spine.Unity.Editor {
|
||||
}
|
||||
|
||||
wasChanged |= EditorGUI.EndChangeCheck();
|
||||
|
||||
if (wasChanged) {
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
slotsReapplyRequired = true;
|
||||
@ -346,6 +369,18 @@ namespace Spine.Unity.Editor {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void OnSceneGUI () {
|
||||
SkeletonGraphic skeletonGraphic = (SkeletonGraphic)target;
|
||||
if (skeletonGraphic.EditReferenceRect) {
|
||||
SpineHandles.DrawRectTransformRect(skeletonGraphic, Color.gray);
|
||||
SpineHandles.DrawReferenceRect(skeletonGraphic, Color.green);
|
||||
} else {
|
||||
SpineHandles.DrawReferenceRect(skeletonGraphic, Color.blue);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected void AssignDefaultBlendModeMaterials () {
|
||||
foreach (UnityEngine.Object target in targets) {
|
||||
SkeletonGraphic skeletonGraphic = (SkeletonGraphic)target;
|
||||
|
||||
@ -424,6 +424,42 @@ namespace Spine.Unity.Editor {
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawReferenceRect (SkeletonGraphic skeletonGraphic, Color color) {
|
||||
RectTransform rectTransform = skeletonGraphic.rectTransform;
|
||||
Vector2 referenceRectSize = skeletonGraphic.GetReferenceRectSize();
|
||||
Vector3 position = rectTransform.position;
|
||||
Vector3 right = rectTransform.TransformVector(Vector3.right * referenceRectSize.x);
|
||||
Vector3 up = rectTransform.TransformVector(Vector3.up * referenceRectSize.y);
|
||||
|
||||
Vector3 cornerVertexBL = position - rectTransform.pivot.x * right - rectTransform.pivot.y * up;
|
||||
DrawRect(cornerVertexBL, right, up, color);
|
||||
}
|
||||
|
||||
public static void DrawRectTransformRect (SkeletonGraphic skeletonGraphic, Color color) {
|
||||
RectTransform rectTransform = skeletonGraphic.rectTransform;
|
||||
Vector2 rectTransformSize = skeletonGraphic.RectTransformSize;
|
||||
Vector3 position = rectTransform.position;
|
||||
Vector3 right = rectTransform.TransformVector(Vector3.right * rectTransformSize.x);
|
||||
Vector3 up = rectTransform.TransformVector(Vector3.up * rectTransformSize.y);
|
||||
|
||||
Vector3 cornerVertexBL = position - rectTransform.pivot.x * right - rectTransform.pivot.y * up;
|
||||
DrawRect(cornerVertexBL, right, up, color);
|
||||
}
|
||||
|
||||
public static void DrawRect (Vector3 cornerVertexBL, Vector3 right, Vector3 up, Color color) {
|
||||
Vector3 v0 = cornerVertexBL;
|
||||
Vector3 v1 = v0 + right;
|
||||
Vector3 v2 = v0 + right + up;
|
||||
Vector3 v3 = v0 + up;
|
||||
Color previousColor = UnityEditor.Handles.color;
|
||||
UnityEditor.Handles.color = color;
|
||||
UnityEditor.Handles.DrawLine(v0, v1);
|
||||
UnityEditor.Handles.DrawLine(v1, v2);
|
||||
UnityEditor.Handles.DrawLine(v2, v3);
|
||||
UnityEditor.Handles.DrawLine(v3, v0);
|
||||
UnityEditor.Handles.color = previousColor;
|
||||
}
|
||||
|
||||
static void DrawCrosshairs2D (Vector3 position, float scale, float skeletonRenderScale = 1f) {
|
||||
scale *= SpineEditorUtilities.Preferences.handleScale * skeletonRenderScale;
|
||||
Handles.DrawLine(position + new Vector3(-scale, 0), position + new Vector3(scale, 0));
|
||||
|
||||
@ -68,8 +68,29 @@ namespace Spine.Unity {
|
||||
public float timeScale = 1f;
|
||||
public bool freeze;
|
||||
|
||||
/// <summary>Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.</summary>
|
||||
public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
|
||||
public enum LayoutMode {
|
||||
None = 0,
|
||||
WidthControlsHeight,
|
||||
HeightControlsWidth,
|
||||
FitInParent,
|
||||
EnvelopeParent
|
||||
}
|
||||
public LayoutMode layoutScaleMode = LayoutMode.None;
|
||||
[SerializeField] protected Vector2 referenceSize = Vector2.one;
|
||||
[SerializeField] protected float referenceScale = 1f;
|
||||
#if UNITY_EDITOR
|
||||
protected LayoutMode previousLayoutScaleMode = LayoutMode.None;
|
||||
[SerializeField] protected Vector2 rectTransformSize = Vector2.zero;
|
||||
[SerializeField] protected bool editReferenceRect = false;
|
||||
protected bool previousEditReferenceRect = false;
|
||||
|
||||
public bool EditReferenceRect { get { return editReferenceRect; } set { editReferenceRect = value; } }
|
||||
public Vector2 RectTransformSize { get { return rectTransformSize; } }
|
||||
#else
|
||||
protected const bool EditReferenceRect = false;
|
||||
#endif
|
||||
/// <summary>Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.</summary>
|
||||
public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
|
||||
protected UpdateMode updateMode = UpdateMode.FullUpdate;
|
||||
|
||||
/// <summary>Update mode used when the MeshRenderer becomes invisible
|
||||
@ -273,6 +294,10 @@ namespace Spine.Unity {
|
||||
Initialize(false);
|
||||
Rebuild(CanvasUpdate.PreRender);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
InitLayoutScaleParameters();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void OnDestroy () {
|
||||
@ -297,6 +322,7 @@ namespace Spine.Unity {
|
||||
public virtual void Update () {
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying) {
|
||||
UpdateReferenceRectSizes();
|
||||
Update(0f);
|
||||
return;
|
||||
}
|
||||
@ -316,7 +342,6 @@ namespace Spine.Unity {
|
||||
wasUpdatedAfterInit = true;
|
||||
if (updateMode < UpdateMode.OnlyAnimationStatus)
|
||||
return;
|
||||
|
||||
UpdateAnimationStatus(deltaTime);
|
||||
|
||||
if (updateMode == UpdateMode.OnlyAnimationStatus) {
|
||||
@ -547,15 +572,26 @@ namespace Spine.Unity {
|
||||
0.5f - (center.y / size.y)
|
||||
);
|
||||
|
||||
this.rectTransform.sizeDelta = size;
|
||||
SetRectTransformSize(this, size);
|
||||
this.rectTransform.pivot = p;
|
||||
|
||||
foreach (SkeletonSubmeshGraphic submeshGraphic in submeshGraphics) {
|
||||
submeshGraphic.rectTransform.sizeDelta = size;
|
||||
SetRectTransformSize(submeshGraphic, size);
|
||||
submeshGraphic.rectTransform.pivot = p;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetRectTransformSize (Graphic target, Vector2 size) {
|
||||
Vector2 parentSize = Vector2.zero;
|
||||
if (target.rectTransform.parent != null) {
|
||||
RectTransform parentTransform = target.rectTransform.parent.GetComponent<RectTransform>();
|
||||
if (parentTransform)
|
||||
parentSize = parentTransform.rect.size;
|
||||
}
|
||||
Vector2 anchorAreaSize = target.rectTransform.anchorMax * parentSize - target.rectTransform.anchorMin * parentSize;
|
||||
target.rectTransform.sizeDelta = size - anchorAreaSize;
|
||||
}
|
||||
|
||||
/// <summary>OnAnimationRebuild is raised after the SkeletonAnimation component is successfully initialized.</summary>
|
||||
public event ISkeletonAnimationDelegate OnAnimationRebuild;
|
||||
public event UpdateBonesDelegate BeforeApply;
|
||||
@ -731,7 +767,13 @@ namespace Spine.Unity {
|
||||
meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
|
||||
}
|
||||
|
||||
if (canvas != null) meshGenerator.ScaleVertexData(canvas.referencePixelsPerUnit);
|
||||
float scale = (canvas == null) ? 100 : canvas.referencePixelsPerUnit;
|
||||
if (layoutScaleMode != LayoutMode.None) {
|
||||
scale *= referenceScale;
|
||||
if (!EditReferenceRect)
|
||||
scale *= GetLayoutScale(layoutScaleMode);
|
||||
}
|
||||
meshGenerator.ScaleVertexData(scale);
|
||||
if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers);
|
||||
|
||||
Mesh mesh = smartMesh.mesh;
|
||||
@ -803,9 +845,12 @@ namespace Spine.Unity {
|
||||
}
|
||||
|
||||
protected void UpdateMeshMultipleCanvasRenderers (SkeletonRendererInstruction currentInstructions) {
|
||||
Canvas c = canvas;
|
||||
float scale = (c == null) ? 100 : c.referencePixelsPerUnit;
|
||||
|
||||
float scale = (canvas == null) ? 100 : canvas.referencePixelsPerUnit;
|
||||
if (layoutScaleMode != LayoutMode.None) {
|
||||
scale *= referenceScale;
|
||||
if (!EditReferenceRect)
|
||||
scale *= GetLayoutScale(layoutScaleMode);
|
||||
}
|
||||
// Generate meshes.
|
||||
int submeshCount = currentInstructions.submeshInstructions.Count;
|
||||
Mesh[] meshesItems = meshes.Items;
|
||||
@ -893,14 +938,19 @@ namespace Spine.Unity {
|
||||
|
||||
for (int i = 0; i < submeshCount; i++) {
|
||||
CanvasRenderer canvasRenderer = canvasRenderers[i];
|
||||
if (i >= usedRenderersCount) {
|
||||
if (i >= usedRenderersCount)
|
||||
canvasRenderer.gameObject.SetActive(true);
|
||||
}
|
||||
if (canvasRenderer.transform.parent != parent.transform) {
|
||||
|
||||
if (canvasRenderer.transform.parent != parent.transform)
|
||||
canvasRenderer.transform.SetParent(parent.transform, false);
|
||||
canvasRenderer.transform.localPosition = Vector3.zero;
|
||||
}
|
||||
|
||||
canvasRenderer.transform.SetSiblingIndex(targetSiblingIndex++);
|
||||
RectTransform dstTransform = submeshGraphics[i].rectTransform;
|
||||
dstTransform.localPosition = Vector3.zero;
|
||||
dstTransform.pivot = rectTransform.pivot;
|
||||
dstTransform.anchorMin = Vector2.zero;
|
||||
dstTransform.anchorMax = Vector2.one;
|
||||
dstTransform.sizeDelta = Vector2.zero;
|
||||
|
||||
SubmeshInstruction submeshInstructionItem = currentInstructions.submeshInstructions.Items[i];
|
||||
if (submeshInstructionItem.forceSeparate) {
|
||||
@ -1027,6 +1077,87 @@ namespace Spine.Unity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void InitLayoutScaleParameters () {
|
||||
previousLayoutScaleMode = layoutScaleMode;
|
||||
}
|
||||
|
||||
protected void UpdateReferenceRectSizes () {
|
||||
if (rectTransformSize == Vector2.zero)
|
||||
rectTransformSize = GetCurrentRectSize();
|
||||
|
||||
HandleChangedEditReferenceRect();
|
||||
|
||||
if (layoutScaleMode != previousLayoutScaleMode) {
|
||||
if (layoutScaleMode != LayoutMode.None) {
|
||||
SetRectTransformSize(this, rectTransformSize);
|
||||
} else {
|
||||
rectTransformSize = referenceSize / referenceScale;
|
||||
referenceScale = 1f;
|
||||
SetRectTransformSize(this, rectTransformSize);
|
||||
}
|
||||
}
|
||||
if (editReferenceRect || layoutScaleMode == LayoutMode.None) {
|
||||
referenceSize = GetCurrentRectSize();
|
||||
}
|
||||
previousLayoutScaleMode = layoutScaleMode;
|
||||
}
|
||||
|
||||
protected void HandleChangedEditReferenceRect () {
|
||||
if (editReferenceRect == previousEditReferenceRect) return;
|
||||
previousEditReferenceRect = editReferenceRect;
|
||||
|
||||
if (editReferenceRect) {
|
||||
rectTransformSize = GetCurrentRectSize();
|
||||
ResetRectToReferenceRectSize();
|
||||
} else {
|
||||
SetRectTransformSize(this, rectTransformSize);
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetRectToReferenceRectSize () {
|
||||
referenceScale = referenceScale * GetLayoutScale(previousLayoutScaleMode);
|
||||
float referenceAspect = referenceSize.x / referenceSize.y;
|
||||
Vector2 newSize = GetCurrentRectSize();
|
||||
|
||||
LayoutMode mode = previousLayoutScaleMode;
|
||||
float frameAspect = newSize.x / newSize.y;
|
||||
if (mode == LayoutMode.FitInParent)
|
||||
mode = frameAspect > referenceAspect ? LayoutMode.HeightControlsWidth : LayoutMode.WidthControlsHeight;
|
||||
else if (mode == LayoutMode.EnvelopeParent)
|
||||
mode = frameAspect > referenceAspect ? LayoutMode.WidthControlsHeight : LayoutMode.HeightControlsWidth;
|
||||
|
||||
if (mode == LayoutMode.WidthControlsHeight)
|
||||
newSize.y = newSize.x / referenceAspect;
|
||||
else if (mode == LayoutMode.HeightControlsWidth)
|
||||
newSize.x = newSize.y * referenceAspect;
|
||||
SetRectTransformSize(this, newSize);
|
||||
}
|
||||
|
||||
public Vector2 GetReferenceRectSize () {
|
||||
return referenceSize * GetLayoutScale(layoutScaleMode);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected float GetLayoutScale (LayoutMode mode) {
|
||||
Vector2 currentSize = GetCurrentRectSize();
|
||||
float referenceAspect = referenceSize.x / referenceSize.y;
|
||||
float frameAspect = currentSize.x / currentSize.y;
|
||||
if (mode == LayoutMode.FitInParent)
|
||||
mode = frameAspect > referenceAspect ? LayoutMode.HeightControlsWidth : LayoutMode.WidthControlsHeight;
|
||||
else if (mode == LayoutMode.EnvelopeParent)
|
||||
mode = frameAspect > referenceAspect ? LayoutMode.WidthControlsHeight : LayoutMode.HeightControlsWidth;
|
||||
|
||||
if (mode == LayoutMode.WidthControlsHeight) {
|
||||
return currentSize.x / referenceSize.x;
|
||||
} else if (mode == LayoutMode.HeightControlsWidth) {
|
||||
return currentSize.y / referenceSize.y;
|
||||
}
|
||||
return 1f;
|
||||
}
|
||||
|
||||
private Vector2 GetCurrentRectSize () {
|
||||
return this.rectTransform.rect.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user