2024-10-23 17:55:55 +08:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Spine Runtimes License Agreement
2024-11-21 09:35:48 +08:00
* Last updated July 28 , 2023. Replaces all prior versions .
2024-10-23 17:55:55 +08:00
*
2024-11-21 09:35:48 +08:00
* Copyright ( c ) 2013 - 2023 , Esoteric Software LLC
2024-10-23 17:55:55 +08:00
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement :
* http : //esotericsoftware.com/spine-editor-license
*
2024-11-21 09:35:48 +08:00
* Otherwise , it is permitted to integrate the Spine Runtimes into software or
* otherwise create derivative works of the Spine Runtimes ( collectively ,
2024-10-23 17:55:55 +08:00
* "Products" ) , provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice .
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ,
* BUSINESS INTERRUPTION , OR LOSS OF USE , DATA , OR PROFITS ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
2024-11-21 09:35:48 +08:00
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THE
* SPINE RUNTIMES , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
2024-10-23 17:55:55 +08:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
#if UNITY_2018_3 | | UNITY_2019 | | UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
2024-11-21 09:35:48 +08:00
#if UNITY_2018_2_OR_NEWER
#define HAS_CULL_TRANSPARENT_MESH
#endif
#define SPINE_OPTIONAL_ON_DEMAND_LOADING
2024-10-23 17:55:55 +08:00
using System.Collections.Generic ;
using UnityEngine ;
using UnityEngine.UI ;
namespace Spine.Unity {
2024-11-21 09:35:48 +08:00
#if NEW_PREFAB_SYSTEM
2024-10-23 17:55:55 +08:00
[ExecuteAlways]
2024-11-21 09:35:48 +08:00
#else
2024-10-23 17:55:55 +08:00
[ExecuteInEditMode]
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
[RequireComponent(typeof(CanvasRenderer), typeof(RectTransform)), DisallowMultipleComponent]
[AddComponentMenu("Spine/SkeletonGraphic (Unity UI Canvas)")]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonGraphic-Component")]
public class SkeletonGraphic : MaskableGraphic , ISkeletonComponent , IAnimationStateComponent , ISkeletonAnimation , IHasSkeletonDataAsset {
#region Inspector
public SkeletonDataAsset skeletonDataAsset ;
public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset ; } }
2024-11-21 09:35:48 +08:00
public Material additiveMaterial ;
public Material multiplyMaterial ;
public Material screenMaterial ;
/// <summary>Own color to replace <c>Graphic.m_Color</c>.</summary>
[UnityEngine.Serialization.FormerlySerializedAs("m_Color")]
[SerializeField] protected Color m_SkeletonColor = Color . white ;
/// <summary>Sets the color of the skeleton. Does not call <see cref="Rebuild"/> and <see cref="UpdateMesh"/>
/// unnecessarily as <c>Graphic.color</c> would otherwise do.</summary>
override public Color color { get { return m_SkeletonColor ; } set { m_SkeletonColor = value ; } }
[SpineSkin(dataField: "skeletonDataAsset", defaultAsEmptyString: true)]
2024-10-23 17:55:55 +08:00
public string initialSkinName ;
public bool initialFlipX , initialFlipY ;
2024-11-21 09:35:48 +08:00
[SpineAnimation(dataField: "skeletonDataAsset")]
2024-10-23 17:55:55 +08:00
public string startingAnimation ;
public bool startingLoop ;
public float timeScale = 1f ;
public bool freeze ;
2024-11-21 09:35:48 +08:00
protected float meshScale = 1f ;
protected Vector2 meshOffset = Vector2 . zero ;
public float MeshScale { get { return meshScale ; } }
public Vector2 MeshOffset { get { return meshOffset ; } }
public enum LayoutMode {
None = 0 ,
WidthControlsHeight ,
HeightControlsWidth ,
FitInParent ,
EnvelopeParent
}
public LayoutMode layoutScaleMode = LayoutMode . None ;
[SerializeField] protected Vector2 referenceSize = Vector2 . one ;
/// <summary>Offset relative to the pivot position, before potential layout scale is applied.</summary>
[SerializeField] protected Vector2 pivotOffset = Vector2 . zero ;
[SerializeField] protected float referenceScale = 1f ;
[SerializeField] protected float layoutScale = 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
2024-10-23 17:55:55 +08:00
/// <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 ; } }
2024-11-21 09:35:48 +08:00
protected UpdateMode updateMode = UpdateMode . FullUpdate ;
2024-10-23 17:55:55 +08:00
/// <summary>Update mode used when the MeshRenderer becomes invisible
/// (when <c>OnBecameInvisible()</c> is called). Update mode is automatically
/// reset to <c>UpdateMode.FullUpdate</c> when the mesh becomes visible again.</summary>
public UpdateMode updateWhenInvisible = UpdateMode . FullUpdate ;
public bool allowMultipleCanvasRenderers = false ;
public List < CanvasRenderer > canvasRenderers = new List < CanvasRenderer > ( ) ;
2024-11-21 09:35:48 +08:00
protected List < SkeletonSubmeshGraphic > submeshGraphics = new List < SkeletonSubmeshGraphic > ( ) ;
protected int usedRenderersCount = 0 ;
2024-10-23 17:55:55 +08:00
// Submesh Separation
public const string SeparatorPartGameObjectName = "Part" ;
/// <summary>Slot names used to populate separatorSlots list when the Skeleton is initialized. Changing this after initialization does nothing.</summary>
[SerializeField] [ SpineSlot ] protected string [ ] separatorSlotNames = new string [ 0 ] ;
/// <summary>Slots that determine where the render is split. This is used by components such as SkeletonRenderSeparator so that the skeleton can be rendered by two separate renderers on different GameObjects.</summary>
[System.NonSerialized] public readonly List < Slot > separatorSlots = new List < Slot > ( ) ;
public bool enableSeparatorSlots = false ;
[SerializeField] protected List < Transform > separatorParts = new List < Transform > ( ) ;
public List < Transform > SeparatorParts { get { return separatorParts ; } }
public bool updateSeparatorPartLocation = true ;
2024-11-21 09:35:48 +08:00
public bool updateSeparatorPartScale = false ;
2024-10-23 17:55:55 +08:00
private bool wasUpdatedAfterInit = true ;
private Texture baseTexture = null ;
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
2024-10-23 17:55:55 +08:00
protected override void OnValidate ( ) {
// This handles Scene View preview.
2024-11-21 09:35:48 +08:00
base . OnValidate ( ) ;
2024-10-23 17:55:55 +08:00
if ( this . IsValid ) {
if ( skeletonDataAsset = = null ) {
Clear ( ) ;
} else if ( skeletonDataAsset . skeletonJSON = = null ) {
Clear ( ) ;
2024-11-21 09:35:48 +08:00
} else if ( skeletonDataAsset . GetSkeletonData ( true ) ! = skeleton . Data ) {
2024-10-23 17:55:55 +08:00
Clear ( ) ;
Initialize ( true ) ;
if ( ! allowMultipleCanvasRenderers & & ( skeletonDataAsset . atlasAssets . Length > 1 | | skeletonDataAsset . atlasAssets [ 0 ] . MaterialCount > 1 ) )
Debug . LogError ( "Unity UI does not support multiple textures per Renderer. Please enable 'Advanced - Multiple CanvasRenderers' to generate the required CanvasRenderer GameObjects. Otherwise your skeleton will not be rendered correctly." , this ) ;
} else {
if ( freeze ) return ;
2024-11-21 09:35:48 +08:00
if ( ! Application . isPlaying ) {
Initialize ( true ) ;
return ;
}
2024-10-23 17:55:55 +08:00
if ( ! string . IsNullOrEmpty ( initialSkinName ) ) {
2024-11-21 09:35:48 +08:00
Skin skin = skeleton . Data . FindSkin ( initialSkinName ) ;
2024-10-23 17:55:55 +08:00
if ( skin ! = null ) {
2024-11-21 09:35:48 +08:00
if ( skin = = skeleton . Data . DefaultSkin )
2024-10-23 17:55:55 +08:00
skeleton . SetSkin ( ( Skin ) null ) ;
else
skeleton . SetSkin ( skin ) ;
}
}
}
} else {
// Under some circumstances (e.g. sometimes on the first import) OnValidate is called
// before SpineEditorUtilities.ImportSpineContent, causing an unnecessary exception.
// The (skeletonDataAsset.skeletonJSON != null) condition serves to prevent this exception.
if ( skeletonDataAsset ! = null & & skeletonDataAsset . skeletonJSON ! = null )
Initialize ( true ) ;
}
}
protected override void Reset ( ) {
base . Reset ( ) ;
if ( material = = null | | material . shader ! = Shader . Find ( "Spine/SkeletonGraphic" ) )
Debug . LogWarning ( "SkeletonGraphic works best with the SkeletonGraphic material." ) ;
}
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
#endregion
#region Runtime Instantiation
/// <summary>Create a new GameObject with a SkeletonGraphic component.</summary>
/// <param name="material">Material for the canvas renderer to use. Usually, the default SkeletonGraphic material will work.</param>
public static SkeletonGraphic NewSkeletonGraphicGameObject ( SkeletonDataAsset skeletonDataAsset , Transform parent , Material material ) {
2024-11-21 09:35:48 +08:00
SkeletonGraphic sg = SkeletonGraphic . AddSkeletonGraphicComponent ( new GameObject ( "New Spine GameObject" ) , skeletonDataAsset , material ) ;
2024-10-23 17:55:55 +08:00
if ( parent ! = null ) sg . transform . SetParent ( parent , false ) ;
return sg ;
}
/// <summary>Add a SkeletonGraphic component to a GameObject.</summary>
/// <param name="material">Material for the canvas renderer to use. Usually, the default SkeletonGraphic material will work.</param>
public static SkeletonGraphic AddSkeletonGraphicComponent ( GameObject gameObject , SkeletonDataAsset skeletonDataAsset , Material material ) {
2024-11-21 09:35:48 +08:00
SkeletonGraphic skeletonGraphic = gameObject . AddComponent < SkeletonGraphic > ( ) ;
2024-10-23 17:55:55 +08:00
if ( skeletonDataAsset ! = null ) {
2024-11-21 09:35:48 +08:00
skeletonGraphic . material = material ;
skeletonGraphic . skeletonDataAsset = skeletonDataAsset ;
skeletonGraphic . Initialize ( false ) ;
2024-10-23 17:55:55 +08:00
}
2024-11-21 09:35:48 +08:00
#if HAS_CULL_TRANSPARENT_MESH
CanvasRenderer canvasRenderer = gameObject . GetComponent < CanvasRenderer > ( ) ;
if ( canvasRenderer ) canvasRenderer . cullTransparentMesh = false ;
#endif
return skeletonGraphic ;
2024-10-23 17:55:55 +08:00
}
#endregion
#region Overrides
2024-11-21 09:35:48 +08:00
// API for taking over rendering.
/// <summary>When true, no meshes and materials are assigned at CanvasRenderers if the used override
/// AssignMeshOverrideSingleRenderer or AssignMeshOverrideMultipleRenderers is non-null.</summary>
public bool disableMeshAssignmentOnOverride = true ;
/// <summary>Delegate type for overriding mesh and material assignment,
/// used when <c>allowMultipleCanvasRenderers</c> is false.</summary>
/// <param name="mesh">Mesh normally assigned at the main CanvasRenderer.</param>
/// <param name="graphicMaterial">Material normally assigned at the main CanvasRenderer.</param>
/// <param name="texture">Texture normally assigned at the main CanvasRenderer.</param>
public delegate void MeshAssignmentDelegateSingle ( Mesh mesh , Material graphicMaterial , Texture texture ) ;
/// <param name="meshCount">Number of meshes. Don't use <c>meshes.Length</c> as this might be higher
/// due to pre-allocated entries.</param>
/// <param name="meshes">Mesh array where each element is normally assigned to one of the <c>canvasRenderers</c>.</param>
/// <param name="graphicMaterials">Material array where each element is normally assigned to one of the <c>canvasRenderers</c>.</param>
/// <param name="textures">Texture array where each element is normally assigned to one of the <c>canvasRenderers</c>.</param>
public delegate void MeshAssignmentDelegateMultiple ( int meshCount , Mesh [ ] meshes , Material [ ] graphicMaterials , Texture [ ] textures ) ;
event MeshAssignmentDelegateSingle assignMeshOverrideSingle ;
event MeshAssignmentDelegateMultiple assignMeshOverrideMultiple ;
/// <summary>Allows separate code to take over mesh and material assignment for this SkeletonGraphic component.
/// Used when <c>allowMultipleCanvasRenderers</c> is false.</summary>
public event MeshAssignmentDelegateSingle AssignMeshOverrideSingleRenderer {
add {
assignMeshOverrideSingle + = value ;
if ( disableMeshAssignmentOnOverride & & assignMeshOverrideSingle ! = null ) {
Initialize ( false ) ;
}
}
remove {
assignMeshOverrideSingle - = value ;
if ( disableMeshAssignmentOnOverride & & assignMeshOverrideSingle = = null ) {
Initialize ( false ) ;
}
}
}
/// <summary>Allows separate code to take over mesh and material assignment for this SkeletonGraphic component.
/// Used when <c>allowMultipleCanvasRenderers</c> is true.</summary>
public event MeshAssignmentDelegateMultiple AssignMeshOverrideMultipleRenderers {
add {
assignMeshOverrideMultiple + = value ;
if ( disableMeshAssignmentOnOverride & & assignMeshOverrideMultiple ! = null ) {
Initialize ( false ) ;
}
}
remove {
assignMeshOverrideMultiple - = value ;
if ( disableMeshAssignmentOnOverride & & assignMeshOverrideMultiple = = null ) {
Initialize ( false ) ;
}
}
}
2024-10-23 17:55:55 +08:00
[System.NonSerialized] readonly Dictionary < Texture , Texture > customTextureOverride = new Dictionary < Texture , Texture > ( ) ;
/// <summary>Use this Dictionary to override a Texture with a different Texture.</summary>
public Dictionary < Texture , Texture > CustomTextureOverride { get { return customTextureOverride ; } }
[System.NonSerialized] readonly Dictionary < Texture , Material > customMaterialOverride = new Dictionary < Texture , Material > ( ) ;
/// <summary>Use this Dictionary to override the Material where the Texture was used at the original atlas.</summary>
public Dictionary < Texture , Material > CustomMaterialOverride { get { return customMaterialOverride ; } }
// This is used by the UI system to determine what to put in the MaterialPropertyBlock.
Texture overrideTexture ;
public Texture OverrideTexture {
get { return overrideTexture ; }
set {
overrideTexture = value ;
canvasRenderer . SetTexture ( this . mainTexture ) ; // Refresh canvasRenderer's texture. Make sure it handles null.
}
}
#endregion
#region Internals
public override Texture mainTexture {
get {
if ( overrideTexture ! = null ) return overrideTexture ;
return baseTexture ;
}
}
protected override void Awake ( ) {
2024-11-21 09:35:48 +08:00
base . Awake ( ) ;
this . onCullStateChanged . AddListener ( OnCullStateChanged ) ;
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
SyncSubmeshGraphicsWithCanvasRenderers ( ) ;
2024-10-23 17:55:55 +08:00
if ( ! this . IsValid ) {
#if UNITY_EDITOR
// workaround for special import case of open scene where OnValidate and Awake are
// called in wrong order, before setup of Spine assets.
if ( ! Application . isPlaying ) {
if ( this . skeletonDataAsset ! = null & & this . skeletonDataAsset . skeletonJSON = = null )
return ;
}
#endif
Initialize ( false ) ;
2024-11-21 09:35:48 +08:00
if ( this . IsValid ) Rebuild ( CanvasUpdate . PreRender ) ;
2024-10-23 17:55:55 +08:00
}
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
InitLayoutScaleParameters ( ) ;
#endif
}
protected override void OnDestroy ( ) {
Clear ( ) ;
base . OnDestroy ( ) ;
2024-10-23 17:55:55 +08:00
}
public override void Rebuild ( CanvasUpdate update ) {
base . Rebuild ( update ) ;
2024-11-21 09:35:48 +08:00
if ( ! this . IsValid ) return ;
2024-10-23 17:55:55 +08:00
if ( canvasRenderer . cull ) return ;
2024-11-21 09:35:48 +08:00
if ( update = = CanvasUpdate . PreRender ) {
PrepareInstructionsAndRenderers ( isInRebuild : true ) ;
UpdateMeshToInstructions ( ) ;
}
2024-10-23 17:55:55 +08:00
if ( allowMultipleCanvasRenderers ) canvasRenderer . Clear ( ) ;
}
protected override void OnDisable ( ) {
base . OnDisable ( ) ;
2024-11-21 09:35:48 +08:00
foreach ( CanvasRenderer canvasRenderer in canvasRenderers ) {
2024-10-23 17:55:55 +08:00
canvasRenderer . Clear ( ) ;
}
}
public virtual void Update ( ) {
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
UpdateReferenceRectSizes ( ) ;
2024-10-23 17:55:55 +08:00
if ( ! Application . isPlaying ) {
Update ( 0f ) ;
return ;
}
2024-11-21 09:35:48 +08:00
#endif
if ( freeze | | updateTiming ! = UpdateTiming . InUpdate ) return ;
Update ( unscaledTime ? Time . unscaledDeltaTime : Time . deltaTime ) ;
}
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
virtual protected void FixedUpdate ( ) {
if ( freeze | | updateTiming ! = UpdateTiming . InFixedUpdate ) return ;
2024-10-23 17:55:55 +08:00
Update ( unscaledTime ? Time . unscaledDeltaTime : Time . deltaTime ) ;
}
public virtual void Update ( float deltaTime ) {
if ( ! this . IsValid ) return ;
wasUpdatedAfterInit = true ;
if ( updateMode < UpdateMode . OnlyAnimationStatus )
return ;
UpdateAnimationStatus ( deltaTime ) ;
if ( updateMode = = UpdateMode . OnlyAnimationStatus )
return ;
ApplyAnimation ( ) ;
}
2024-11-21 09:35:48 +08:00
protected void SyncSubmeshGraphicsWithCanvasRenderers ( ) {
submeshGraphics . Clear ( ) ;
#if UNITY_EDITOR
if ( ! Application . isPlaying )
DestroyOldRawImages ( ) ;
#endif
foreach ( CanvasRenderer canvasRenderer in canvasRenderers ) {
SkeletonSubmeshGraphic submeshGraphic = canvasRenderer . GetComponent < SkeletonSubmeshGraphic > ( ) ;
if ( submeshGraphic = = null ) {
submeshGraphic = canvasRenderer . gameObject . AddComponent < SkeletonSubmeshGraphic > ( ) ;
submeshGraphic . maskable = this . maskable ;
submeshGraphic . raycastTarget = false ;
}
submeshGraphics . Add ( submeshGraphic ) ;
}
}
2024-10-23 17:55:55 +08:00
protected void UpdateAnimationStatus ( float deltaTime ) {
deltaTime * = timeScale ;
state . Update ( deltaTime ) ;
2024-11-21 09:35:48 +08:00
skeleton . Update ( deltaTime ) ;
ApplyTransformMovementToPhysics ( ) ;
if ( updateMode = = UpdateMode . OnlyAnimationStatus ) {
state . ApplyEventTimelinesOnly ( skeleton , issueEvents : false ) ;
return ;
}
2024-10-23 17:55:55 +08:00
}
2024-11-21 09:35:48 +08:00
public virtual void ApplyTransformMovementToPhysics ( ) {
if ( Application . isPlaying ) {
if ( physicsPositionInheritanceFactor ! = Vector2 . zero ) {
Vector2 position = GetPhysicsTransformPosition ( ) ;
Vector2 positionDelta = ( position - lastPosition ) / meshScale ;
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
positionDelta = transform . InverseTransformVector ( positionDelta ) ;
if ( physicsMovementRelativeTo ! = null ) {
positionDelta = physicsMovementRelativeTo . TransformVector ( positionDelta ) ;
}
positionDelta . x * = physicsPositionInheritanceFactor . x ;
positionDelta . y * = physicsPositionInheritanceFactor . y ;
skeleton . PhysicsTranslate ( positionDelta . x , positionDelta . y ) ;
lastPosition = position ;
}
if ( physicsRotationInheritanceFactor ! = 0f ) {
float rotation = GetPhysicsTransformRotation ( ) ;
skeleton . PhysicsRotate ( 0 , 0 , physicsRotationInheritanceFactor * ( rotation - lastRotation ) ) ;
lastRotation = rotation ;
}
}
}
protected Vector2 GetPhysicsTransformPosition ( ) {
if ( physicsMovementRelativeTo = = null ) {
return transform . position ;
} else {
if ( physicsMovementRelativeTo = = transform . parent )
return transform . localPosition ;
else
return physicsMovementRelativeTo . InverseTransformPoint ( transform . position ) ;
}
}
protected float GetPhysicsTransformRotation ( ) {
if ( physicsMovementRelativeTo = = null ) {
return this . transform . rotation . eulerAngles . z ;
} else {
if ( physicsMovementRelativeTo = = this . transform . parent )
return this . transform . localRotation . eulerAngles . z ;
else {
Quaternion relative = Quaternion . Inverse ( physicsMovementRelativeTo . rotation ) * this . transform . rotation ;
return relative . eulerAngles . z ;
}
}
}
public virtual void ApplyAnimation ( ) {
if ( BeforeApply ! = null )
BeforeApply ( this ) ;
if ( updateMode ! = UpdateMode . OnlyEventTimelines )
state . Apply ( skeleton ) ;
else
state . ApplyEventTimelinesOnly ( skeleton , issueEvents : true ) ;
AfterAnimationApplied ( ) ;
}
public virtual void AfterAnimationApplied ( ) {
2024-10-23 17:55:55 +08:00
if ( UpdateLocal ! = null )
UpdateLocal ( this ) ;
2024-11-21 09:35:48 +08:00
if ( UpdateWorld = = null ) {
UpdateWorldTransform ( Skeleton . Physics . Update ) ;
} else {
UpdateWorldTransform ( Skeleton . Physics . Pose ) ;
2024-10-23 17:55:55 +08:00
UpdateWorld ( this ) ;
2024-11-21 09:35:48 +08:00
UpdateWorldTransform ( Skeleton . Physics . Update ) ;
2024-10-23 17:55:55 +08:00
}
if ( UpdateComplete ! = null )
UpdateComplete ( this ) ;
}
2024-11-21 09:35:48 +08:00
protected void UpdateWorldTransform ( Skeleton . Physics physics ) {
skeleton . UpdateWorldTransform ( physics ) ;
}
2024-10-23 17:55:55 +08:00
public void LateUpdate ( ) {
2024-11-21 09:35:48 +08:00
if ( ! this . IsValid ) return ;
2024-10-23 17:55:55 +08:00
// instantiation can happen from Update() after this component, leading to a missing Update() call.
if ( ! wasUpdatedAfterInit ) Update ( 0 ) ;
if ( freeze ) return ;
2024-11-21 09:35:48 +08:00
if ( updateMode ! = UpdateMode . FullUpdate ) return ;
if ( updateTiming = = UpdateTiming . InLateUpdate )
Update ( unscaledTime ? Time . unscaledDeltaTime : Time . deltaTime ) ;
2024-10-23 17:55:55 +08:00
UpdateMesh ( ) ;
}
2024-11-21 09:35:48 +08:00
protected void OnCullStateChanged ( bool culled ) {
if ( culled )
OnBecameInvisible ( ) ;
else
OnBecameVisible ( ) ;
}
2024-10-23 17:55:55 +08:00
public void OnBecameVisible ( ) {
updateMode = UpdateMode . FullUpdate ;
}
public void OnBecameInvisible ( ) {
updateMode = updateWhenInvisible ;
}
public void ReapplySeparatorSlotNames ( ) {
if ( ! IsValid )
return ;
separatorSlots . Clear ( ) ;
for ( int i = 0 , n = separatorSlotNames . Length ; i < n ; i + + ) {
string slotName = separatorSlotNames [ i ] ;
if ( slotName = = "" )
continue ;
2024-11-21 09:35:48 +08:00
Slot slot = skeleton . FindSlot ( slotName ) ;
2024-10-23 17:55:55 +08:00
if ( slot ! = null ) {
separatorSlots . Add ( slot ) ;
}
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
else {
2024-10-23 17:55:55 +08:00
Debug . LogWarning ( slotName + " is not a slot in " + skeletonDataAsset . skeletonJSON . name ) ;
}
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
}
UpdateSeparatorPartParents ( ) ;
}
#endregion
#region API
protected Skeleton skeleton ;
2024-11-21 09:35:48 +08:00
public Skeleton Skeleton {
get {
Initialize ( false ) ;
return skeleton ;
}
set {
skeleton = value ;
}
}
public SkeletonData SkeletonData {
get {
Initialize ( false ) ;
return skeleton = = null ? null : skeleton . Data ;
}
}
2024-10-23 17:55:55 +08:00
public bool IsValid { get { return skeleton ! = null ; } }
public delegate void SkeletonRendererDelegate ( SkeletonGraphic skeletonGraphic ) ;
2024-11-21 09:35:48 +08:00
public delegate void InstructionDelegate ( SkeletonRendererInstruction instruction ) ;
2024-10-23 17:55:55 +08:00
/// <summary>OnRebuild is raised after the Skeleton is successfully initialized.</summary>
public event SkeletonRendererDelegate OnRebuild ;
2024-11-21 09:35:48 +08:00
/// <summary>OnInstructionsPrepared is raised at the end of <c>LateUpdate</c> after render instructions
/// are done, target renderers are prepared, and the mesh is ready to be generated.</summary>
public event InstructionDelegate OnInstructionsPrepared ;
/// <summary>OnMeshAndMaterialsUpdated is raised at the end of <c>Rebuild</c> after the Mesh and
/// all materials have been updated. Note that some Unity API calls are not permitted to be issued from
/// <c>Rebuild</c>, so you may want to subscribe to <see cref="OnInstructionsPrepared"/> instead
/// from where you can issue such preparation calls.</summary>
2024-10-23 17:55:55 +08:00
public event SkeletonRendererDelegate OnMeshAndMaterialsUpdated ;
protected Spine . AnimationState state ;
2024-11-21 09:35:48 +08:00
public Spine . AnimationState AnimationState {
get {
Initialize ( false ) ;
return state ;
}
}
/// <seealso cref="PhysicsPositionInheritanceFactor"/>
[SerializeField] protected Vector2 physicsPositionInheritanceFactor = Vector2 . one ;
/// <seealso cref="PhysicsRotationInheritanceFactor"/>
[SerializeField] protected float physicsRotationInheritanceFactor = 1.0f ;
/// <summary>Reference transform relative to which physics movement will be calculated, or null to use world location.</summary>
[SerializeField] protected Transform physicsMovementRelativeTo = null ;
/// <summary>Used for applying Transform translation to skeleton PhysicsConstraints.</summary>
protected Vector2 lastPosition ;
/// <summary>Used for applying Transform rotation to skeleton PhysicsConstraints.</summary>
protected float lastRotation ;
/// <summary>When set to non-zero, Transform position movement in X and Y direction
/// is applied to skeleton PhysicsConstraints, multiplied by this scale factor.
/// Typical values are <c>Vector2.one</c> to apply XY movement 1:1,
/// <c>Vector2(2f, 2f)</c> to apply movement with double intensity,
/// <c>Vector2(1f, 0f)</c> to apply only horizontal movement, or
/// <c>Vector2.zero</c> to not apply any Transform position movement at all.</summary>
public Vector2 PhysicsPositionInheritanceFactor {
get {
return physicsPositionInheritanceFactor ;
}
set {
if ( physicsPositionInheritanceFactor = = Vector2 . zero & & value ! = Vector2 . zero ) ResetLastPosition ( ) ;
physicsPositionInheritanceFactor = value ;
}
}
/// <summary>When set to non-zero, Transform rotation movement is applied to skeleton PhysicsConstraints,
/// multiplied by this scale factor. Typical values are <c>1</c> to apply movement 1:1,
/// <c>2</c> to apply movement with double intensity, or
/// <c>0</c> to not apply any Transform rotation movement at all.</summary>
public float PhysicsRotationInheritanceFactor {
get {
return physicsRotationInheritanceFactor ;
}
set {
if ( physicsRotationInheritanceFactor = = 0f & & value ! = 0f ) ResetLastRotation ( ) ;
physicsRotationInheritanceFactor = value ;
}
}
/// <summary>Reference transform relative to which physics movement will be calculated, or null to use world location.</summary>
public Transform PhysicsMovementRelativeTo {
get {
return physicsMovementRelativeTo ;
}
set {
physicsMovementRelativeTo = value ;
if ( physicsPositionInheritanceFactor ! = Vector2 . zero ) ResetLastPosition ( ) ;
if ( physicsRotationInheritanceFactor ! = 0f ) ResetLastRotation ( ) ;
}
}
public void ResetLastPosition ( ) {
lastPosition = GetPhysicsTransformPosition ( ) ;
}
public void ResetLastRotation ( ) {
lastRotation = GetPhysicsTransformRotation ( ) ;
}
public void ResetLastPositionAndRotation ( ) {
lastPosition = GetPhysicsTransformPosition ( ) ;
lastRotation = GetPhysicsTransformRotation ( ) ;
}
2024-10-23 17:55:55 +08:00
[SerializeField] protected Spine . Unity . MeshGenerator meshGenerator = new MeshGenerator ( ) ;
public Spine . Unity . MeshGenerator MeshGenerator { get { return this . meshGenerator ; } }
DoubleBuffered < Spine . Unity . MeshRendererBuffers . SmartMesh > meshBuffers ;
SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction ( ) ;
readonly ExposedList < Mesh > meshes = new ExposedList < Mesh > ( ) ;
2024-11-21 09:35:48 +08:00
readonly ExposedList < Material > usedMaterials = new ExposedList < Material > ( ) ;
readonly ExposedList < Texture > usedTextures = new ExposedList < Texture > ( ) ;
/// <summary>Returns the <see cref="SkeletonClipping"/> used by this renderer for use with e.g.
/// <see cref="Skeleton.GetBounds(out float, out float, out float, out float, ref float[], SkeletonClipping)"/>
/// </summary>
public SkeletonClipping SkeletonClipping { get { return meshGenerator . SkeletonClipping ; } }
public ExposedList < Mesh > MeshesMultipleCanvasRenderers { get { return meshes ; } }
public ExposedList < Material > MaterialsMultipleCanvasRenderers { get { return usedMaterials ; } }
public ExposedList < Texture > TexturesMultipleCanvasRenderers { get { return usedTextures ; } }
2024-10-23 17:55:55 +08:00
public Mesh GetLastMesh ( ) {
return meshBuffers . GetCurrent ( ) . mesh ;
}
public bool MatchRectTransformWithBounds ( ) {
2024-11-21 09:35:48 +08:00
if ( ! wasUpdatedAfterInit ) Update ( 0 ) ;
2024-10-23 17:55:55 +08:00
UpdateMesh ( ) ;
if ( ! this . allowMultipleCanvasRenderers )
return MatchRectTransformSingleRenderer ( ) ;
else
return MatchRectTransformMultipleRenderers ( ) ;
}
protected bool MatchRectTransformSingleRenderer ( ) {
Mesh mesh = this . GetLastMesh ( ) ;
if ( mesh = = null ) {
return false ;
}
2024-11-21 09:35:48 +08:00
if ( mesh . vertexCount = = 0 | | mesh . bounds . size = = Vector3 . zero ) {
2024-10-23 17:55:55 +08:00
this . rectTransform . sizeDelta = new Vector2 ( 50f , 50f ) ;
this . rectTransform . pivot = new Vector2 ( 0.5f , 0.5f ) ;
return false ;
}
mesh . RecalculateBounds ( ) ;
SetRectTransformBounds ( mesh . bounds ) ;
return true ;
}
protected bool MatchRectTransformMultipleRenderers ( ) {
bool anyBoundsAdded = false ;
Bounds combinedBounds = new Bounds ( ) ;
for ( int i = 0 ; i < canvasRenderers . Count ; + + i ) {
2024-11-21 09:35:48 +08:00
CanvasRenderer canvasRenderer = canvasRenderers [ i ] ;
2024-10-23 17:55:55 +08:00
if ( ! canvasRenderer . gameObject . activeSelf )
continue ;
Mesh mesh = meshes . Items [ i ] ;
if ( mesh = = null | | mesh . vertexCount = = 0 )
continue ;
mesh . RecalculateBounds ( ) ;
2024-11-21 09:35:48 +08:00
Bounds bounds = mesh . bounds ;
2024-10-23 17:55:55 +08:00
if ( anyBoundsAdded )
combinedBounds . Encapsulate ( bounds ) ;
else {
anyBoundsAdded = true ;
combinedBounds = bounds ;
}
}
2024-11-21 09:35:48 +08:00
if ( ! anyBoundsAdded | | combinedBounds . size = = Vector3 . zero ) {
2024-10-23 17:55:55 +08:00
this . rectTransform . sizeDelta = new Vector2 ( 50f , 50f ) ;
this . rectTransform . pivot = new Vector2 ( 0.5f , 0.5f ) ;
return false ;
}
SetRectTransformBounds ( combinedBounds ) ;
return true ;
}
private void SetRectTransformBounds ( Bounds combinedBounds ) {
2024-11-21 09:35:48 +08:00
Vector3 size = combinedBounds . size ;
Vector3 center = combinedBounds . center ;
Vector2 p = new Vector2 (
2024-10-23 17:55:55 +08:00
0.5f - ( center . x / size . x ) ,
0.5f - ( center . y / size . y )
) ;
2024-11-21 09:35:48 +08:00
SetRectTransformSize ( this , size ) ;
2024-10-23 17:55:55 +08:00
this . rectTransform . pivot = p ;
2024-11-21 09:35:48 +08:00
foreach ( Transform separatorPart in separatorParts ) {
RectTransform separatorTransform = separatorPart . GetComponent < RectTransform > ( ) ;
if ( separatorTransform ) {
SetRectTransformSize ( separatorTransform , size ) ;
separatorTransform . pivot = p ;
}
}
foreach ( SkeletonSubmeshGraphic submeshGraphic in submeshGraphics ) {
SetRectTransformSize ( submeshGraphic , size ) ;
submeshGraphic . rectTransform . pivot = p ;
}
this . referenceSize = size ;
referenceScale = referenceScale * layoutScale ;
layoutScale = 1f ;
}
public static void SetRectTransformSize ( Graphic target , Vector2 size ) {
SetRectTransformSize ( target . rectTransform , size ) ;
}
public static void SetRectTransformSize ( RectTransform targetRectTransform , Vector2 size ) {
Vector2 parentSize = Vector2 . zero ;
if ( targetRectTransform . parent ! = null ) {
RectTransform parentTransform = targetRectTransform . parent . GetComponent < RectTransform > ( ) ;
if ( parentTransform )
parentSize = parentTransform . rect . size ;
}
Vector2 anchorAreaSize = Vector2 . Scale ( targetRectTransform . anchorMax - targetRectTransform . anchorMin , parentSize ) ;
targetRectTransform . sizeDelta = size - anchorAreaSize ;
2024-10-23 17:55:55 +08:00
}
2024-11-21 09:35:48 +08:00
/// <summary>OnAnimationRebuild is raised after the SkeletonAnimation component is successfully initialized.</summary>
public event ISkeletonAnimationDelegate OnAnimationRebuild ;
public event UpdateBonesDelegate BeforeApply ;
2024-10-23 17:55:55 +08:00
public event UpdateBonesDelegate UpdateLocal ;
public event UpdateBonesDelegate UpdateWorld ;
public event UpdateBonesDelegate UpdateComplete ;
2024-11-21 09:35:48 +08:00
[SerializeField] protected UpdateTiming updateTiming = UpdateTiming . InUpdate ;
public UpdateTiming UpdateTiming { get { return updateTiming ; } set { updateTiming = value ; } }
[SerializeField] protected bool unscaledTime ;
public bool UnscaledTime { get { return unscaledTime ; } set { unscaledTime = value ; } }
2024-10-23 17:55:55 +08:00
/// <summary> Occurs after the vertex data populated every frame, before the vertices are pushed into the mesh.</summary>
public event Spine . Unity . MeshGeneratorDelegate OnPostProcessVertices ;
public void Clear ( ) {
skeleton = null ;
canvasRenderer . Clear ( ) ;
for ( int i = 0 ; i < canvasRenderers . Count ; + + i )
canvasRenderers [ i ] . Clear ( ) ;
2024-11-21 09:35:48 +08:00
DestroyMeshes ( ) ;
usedMaterials . Clear ( ) ;
usedTextures . Clear ( ) ;
DisposeMeshBuffers ( ) ;
2024-10-23 17:55:55 +08:00
}
public void TrimRenderers ( ) {
2024-11-21 09:35:48 +08:00
List < CanvasRenderer > newList = new List < CanvasRenderer > ( ) ;
foreach ( CanvasRenderer canvasRenderer in canvasRenderers ) {
2024-10-23 17:55:55 +08:00
if ( canvasRenderer . gameObject . activeSelf ) {
newList . Add ( canvasRenderer ) ;
2024-11-21 09:35:48 +08:00
} else {
2024-10-23 17:55:55 +08:00
if ( Application . isEditor & & ! Application . isPlaying )
DestroyImmediate ( canvasRenderer . gameObject ) ;
else
Destroy ( canvasRenderer . gameObject ) ;
}
}
canvasRenderers = newList ;
2024-11-21 09:35:48 +08:00
SyncSubmeshGraphicsWithCanvasRenderers ( ) ;
2024-10-23 17:55:55 +08:00
}
public void Initialize ( bool overwrite ) {
if ( this . IsValid & & ! overwrite ) return ;
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
if ( BuildUtilities . IsInSkeletonAssetBuildPreProcessing )
return ;
#endif
2024-10-23 17:55:55 +08:00
if ( this . skeletonDataAsset = = null ) return ;
2024-11-21 09:35:48 +08:00
SkeletonData skeletonData = this . skeletonDataAsset . GetSkeletonData ( false ) ;
2024-10-23 17:55:55 +08:00
if ( skeletonData = = null ) return ;
if ( skeletonDataAsset . atlasAssets . Length < = 0 | | skeletonDataAsset . atlasAssets [ 0 ] . MaterialCount < = 0 ) return ;
this . skeleton = new Skeleton ( skeletonData ) {
ScaleX = this . initialFlipX ? - 1 : 1 ,
ScaleY = this . initialFlipY ? - 1 : 1
} ;
InitMeshBuffers ( ) ;
baseTexture = skeletonDataAsset . atlasAssets [ 0 ] . PrimaryMaterial . mainTexture ;
canvasRenderer . SetTexture ( this . mainTexture ) ; // Needed for overwriting initializations.
2024-11-21 09:35:48 +08:00
ResetLastPositionAndRotation ( ) ;
2024-10-23 17:55:55 +08:00
// Set the initial Skin and Animation
if ( ! string . IsNullOrEmpty ( initialSkinName ) )
skeleton . SetSkin ( initialSkinName ) ;
separatorSlots . Clear ( ) ;
for ( int i = 0 ; i < separatorSlotNames . Length ; i + + )
separatorSlots . Add ( skeleton . FindSlot ( separatorSlotNames [ i ] ) ) ;
2024-11-21 09:35:48 +08:00
if ( OnRebuild ! = null )
OnRebuild ( this ) ;
2024-10-23 17:55:55 +08:00
wasUpdatedAfterInit = false ;
2024-11-21 09:35:48 +08:00
this . state = new Spine . AnimationState ( skeletonDataAsset . GetAnimationStateData ( ) ) ;
if ( state = = null ) {
Clear ( ) ;
return ;
}
2024-10-23 17:55:55 +08:00
if ( ! string . IsNullOrEmpty ( startingAnimation ) ) {
2024-11-21 09:35:48 +08:00
Spine . Animation animationObject = skeletonDataAsset . GetSkeletonData ( false ) . FindAnimation ( startingAnimation ) ;
2024-10-23 17:55:55 +08:00
if ( animationObject ! = null ) {
state . SetAnimation ( 0 , animationObject , startingLoop ) ;
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
2024-10-23 17:55:55 +08:00
if ( ! Application . isPlaying )
Update ( 0f ) ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
}
}
2024-11-21 09:35:48 +08:00
if ( OnAnimationRebuild ! = null )
OnAnimationRebuild ( this ) ;
}
public void PrepareInstructionsAndRenderers ( bool isInRebuild = false ) {
if ( ! this . allowMultipleCanvasRenderers ) {
MeshGenerator . GenerateSingleSubmeshInstruction ( currentInstructions , skeleton , null ) ;
if ( canvasRenderers . Count > 0 )
DisableUnusedCanvasRenderers ( usedCount : 0 , isInRebuild : isInRebuild ) ;
usedRenderersCount = 0 ;
} else {
MeshGenerator . GenerateSkeletonRendererInstruction ( currentInstructions , skeleton , null ,
enableSeparatorSlots ? separatorSlots : null ,
enableSeparatorSlots ? separatorSlots . Count > 0 : false ,
false ) ;
int submeshCount = currentInstructions . submeshInstructions . Count ;
EnsureCanvasRendererCount ( submeshCount ) ;
EnsureMeshesCount ( submeshCount ) ;
EnsureUsedTexturesAndMaterialsCount ( submeshCount ) ;
EnsureSeparatorPartCount ( ) ;
PrepareRendererGameObjects ( currentInstructions , isInRebuild ) ;
}
if ( OnInstructionsPrepared ! = null )
OnInstructionsPrepared ( this . currentInstructions ) ;
2024-10-23 17:55:55 +08:00
}
public void UpdateMesh ( ) {
2024-11-21 09:35:48 +08:00
PrepareInstructionsAndRenderers ( ) ;
UpdateMeshToInstructions ( ) ;
}
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
public void UpdateMeshToInstructions ( ) {
if ( ! this . IsValid | | currentInstructions . rawVertexCount < 0 ) return ;
2024-10-23 17:55:55 +08:00
skeleton . SetColor ( this . color ) ;
if ( ! this . allowMultipleCanvasRenderers ) {
2024-11-21 09:35:48 +08:00
UpdateMeshSingleCanvasRenderer ( currentInstructions ) ;
} else {
UpdateMaterialsMultipleCanvasRenderers ( currentInstructions ) ;
2024-10-23 17:55:55 +08:00
UpdateMeshMultipleCanvasRenderers ( currentInstructions ) ;
}
if ( OnMeshAndMaterialsUpdated ! = null )
OnMeshAndMaterialsUpdated ( this ) ;
}
public bool HasMultipleSubmeshInstructions ( ) {
if ( ! IsValid )
return false ;
return MeshGenerator . RequiresMultipleSubmeshesByDrawOrder ( skeleton ) ;
}
#endregion
protected void InitMeshBuffers ( ) {
if ( meshBuffers ! = null ) {
meshBuffers . GetNext ( ) . Clear ( ) ;
meshBuffers . GetNext ( ) . Clear ( ) ;
2024-11-21 09:35:48 +08:00
} else {
2024-10-23 17:55:55 +08:00
meshBuffers = new DoubleBuffered < MeshRendererBuffers . SmartMesh > ( ) ;
}
}
2024-11-21 09:35:48 +08:00
protected void DisposeMeshBuffers ( ) {
if ( meshBuffers ! = null ) {
meshBuffers . GetNext ( ) . Dispose ( ) ;
meshBuffers . GetNext ( ) . Dispose ( ) ;
meshBuffers = null ;
}
}
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
protected void UpdateMeshSingleCanvasRenderer ( SkeletonRendererInstruction currentInstructions ) {
MeshRendererBuffers . SmartMesh smartMesh = meshBuffers . GetNext ( ) ;
2024-10-23 17:55:55 +08:00
bool updateTriangles = SkeletonRendererInstruction . GeometryNotEqual ( currentInstructions , smartMesh . instructionUsed ) ;
meshGenerator . Begin ( ) ;
2024-11-21 09:35:48 +08:00
bool useAddSubmesh = currentInstructions . hasActiveClipping & & currentInstructions . submeshInstructions . Count > 0 ;
if ( useAddSubmesh ) {
2024-10-23 17:55:55 +08:00
meshGenerator . AddSubmesh ( currentInstructions . submeshInstructions . Items [ 0 ] , updateTriangles ) ;
2024-11-21 09:35:48 +08:00
} else {
2024-10-23 17:55:55 +08:00
meshGenerator . BuildMeshWithArrays ( currentInstructions , updateTriangles ) ;
}
2024-11-21 09:35:48 +08:00
meshScale = ( canvas = = null ) ? 100 : canvas . referencePixelsPerUnit ;
if ( layoutScaleMode ! = LayoutMode . None ) {
meshScale * = referenceScale ;
layoutScale = GetLayoutScale ( layoutScaleMode ) ;
if ( ! EditReferenceRect ) {
meshScale * = layoutScale ;
}
meshOffset = pivotOffset * layoutScale ;
} else {
meshOffset = pivotOffset ;
}
if ( meshOffset = = Vector2 . zero )
meshGenerator . ScaleVertexData ( meshScale ) ;
else
meshGenerator . ScaleAndOffsetVertexData ( meshScale , meshOffset ) ;
2024-10-23 17:55:55 +08:00
if ( OnPostProcessVertices ! = null ) OnPostProcessVertices . Invoke ( this . meshGenerator . Buffers ) ;
2024-11-21 09:35:48 +08:00
Mesh mesh = smartMesh . mesh ;
2024-10-23 17:55:55 +08:00
meshGenerator . FillVertexData ( mesh ) ;
if ( updateTriangles ) meshGenerator . FillTriangles ( mesh ) ;
meshGenerator . FillLateVertexData ( mesh ) ;
smartMesh . instructionUsed . Set ( currentInstructions ) ;
2024-11-21 09:35:48 +08:00
if ( assignMeshOverrideSingle ! = null )
assignMeshOverrideSingle ( mesh , this . canvasRenderer . GetMaterial ( ) , this . mainTexture ) ;
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
bool assignAtCanvasRenderer = ( assignMeshOverrideSingle = = null | | ! disableMeshAssignmentOnOverride ) ;
if ( assignAtCanvasRenderer )
canvasRenderer . SetMesh ( mesh ) ;
else
canvasRenderer . SetMesh ( null ) ;
bool assignTexture = false ;
2024-10-23 17:55:55 +08:00
if ( currentInstructions . submeshInstructions . Count > 0 ) {
2024-11-21 09:35:48 +08:00
Material material = currentInstructions . submeshInstructions . Items [ 0 ] . material ;
2024-10-23 17:55:55 +08:00
if ( material ! = null & & baseTexture ! = material . mainTexture ) {
baseTexture = material . mainTexture ;
2024-11-21 09:35:48 +08:00
if ( overrideTexture = = null & & assignAtCanvasRenderer )
assignTexture = true ;
2024-10-23 17:55:55 +08:00
}
}
2024-11-21 09:35:48 +08:00
#if SPINE_OPTIONAL_ON_DEMAND_LOADING
if ( Application . isPlaying )
HandleOnDemandLoading ( ) ;
#endif
if ( assignTexture )
canvasRenderer . SetTexture ( this . mainTexture ) ;
2024-10-23 17:55:55 +08:00
}
2024-11-21 09:35:48 +08:00
protected void UpdateMaterialsMultipleCanvasRenderers ( SkeletonRendererInstruction currentInstructions ) {
2024-10-23 17:55:55 +08:00
int submeshCount = currentInstructions . submeshInstructions . Count ;
2024-11-21 09:35:48 +08:00
bool useOriginalTextureAndMaterial = ( customMaterialOverride . Count = = 0 & & customTextureOverride . Count = = 0 ) ;
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
BlendModeMaterials blendModeMaterials = skeletonDataAsset . blendModeMaterials ;
bool hasBlendModeMaterials = blendModeMaterials . RequiresBlendModeMaterials ;
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
bool pmaVertexColors = meshGenerator . settings . pmaVertexColors ;
Material [ ] usedMaterialItems = usedMaterials . Items ;
Texture [ ] usedTextureItems = usedTextures . Items ;
for ( int i = 0 ; i < submeshCount ; i + + ) {
SubmeshInstruction submeshInstructionItem = currentInstructions . submeshInstructions . Items [ i ] ;
Material submeshMaterial = submeshInstructionItem . material ;
if ( useOriginalTextureAndMaterial ) {
if ( submeshMaterial = = null ) {
usedMaterialItems [ i ] = null ;
usedTextureItems [ i ] = null ;
continue ;
}
usedTextureItems [ i ] = submeshMaterial . mainTexture ;
if ( ! hasBlendModeMaterials ) {
usedMaterialItems [ i ] = this . materialForRendering ;
} else {
BlendMode blendMode = blendModeMaterials . BlendModeForMaterial ( submeshMaterial ) ;
Material usedMaterial = this . materialForRendering ;
if ( blendMode = = BlendMode . Additive & & ! pmaVertexColors & & additiveMaterial ) {
usedMaterial = additiveMaterial ;
} else if ( blendMode = = BlendMode . Multiply & & multiplyMaterial )
usedMaterial = multiplyMaterial ;
else if ( blendMode = = BlendMode . Screen & & screenMaterial )
usedMaterial = screenMaterial ;
usedMaterialItems [ i ] = submeshGraphics [ i ] . GetModifiedMaterial ( usedMaterial ) ;
}
} else {
Texture originalTexture = submeshMaterial . mainTexture ;
Material usedMaterial ;
Texture usedTexture ;
if ( ! customMaterialOverride . TryGetValue ( originalTexture , out usedMaterial ) )
usedMaterial = material ;
if ( ! customTextureOverride . TryGetValue ( originalTexture , out usedTexture ) )
usedTexture = originalTexture ;
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
usedMaterialItems [ i ] = submeshGraphics [ i ] . GetModifiedMaterial ( usedMaterial ) ;
usedTextureItems [ i ] = usedTexture ;
2024-10-23 17:55:55 +08:00
}
}
2024-11-21 09:35:48 +08:00
}
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
protected void UpdateMeshMultipleCanvasRenderers ( SkeletonRendererInstruction currentInstructions ) {
meshScale = ( canvas = = null ) ? 100 : canvas . referencePixelsPerUnit ;
if ( layoutScaleMode ! = LayoutMode . None ) {
meshScale * = referenceScale ;
layoutScale = GetLayoutScale ( layoutScaleMode ) ;
if ( ! EditReferenceRect ) {
meshScale * = layoutScale ;
}
meshOffset = pivotOffset * layoutScale ;
} else {
meshOffset = pivotOffset ;
}
// Generate meshes.
int submeshCount = currentInstructions . submeshInstructions . Count ;
Mesh [ ] meshesItems = meshes . Items ;
bool useOriginalTextureAndMaterial = ( customMaterialOverride . Count = = 0 & & customTextureOverride . Count = = 0 ) ;
BlendModeMaterials blendModeMaterials = skeletonDataAsset . blendModeMaterials ;
bool hasBlendModeMaterials = blendModeMaterials . RequiresBlendModeMaterials ;
#if HAS_CULL_TRANSPARENT_MESH
bool mainCullTransparentMesh = this . canvasRenderer . cullTransparentMesh ;
#endif
bool pmaVertexColors = meshGenerator . settings . pmaVertexColors ;
Material [ ] usedMaterialItems = usedMaterials . Items ;
Texture [ ] usedTextureItems = usedTextures . Items ;
2024-10-23 17:55:55 +08:00
for ( int i = 0 ; i < submeshCount ; i + + ) {
2024-11-21 09:35:48 +08:00
SubmeshInstruction submeshInstructionItem = currentInstructions . submeshInstructions . Items [ i ] ;
2024-10-23 17:55:55 +08:00
meshGenerator . Begin ( ) ;
meshGenerator . AddSubmesh ( submeshInstructionItem ) ;
2024-11-21 09:35:48 +08:00
Mesh targetMesh = meshesItems [ i ] ;
if ( meshOffset = = Vector2 . zero )
meshGenerator . ScaleVertexData ( meshScale ) ;
else
meshGenerator . ScaleAndOffsetVertexData ( meshScale , meshOffset ) ;
2024-10-23 17:55:55 +08:00
if ( OnPostProcessVertices ! = null ) OnPostProcessVertices . Invoke ( this . meshGenerator . Buffers ) ;
meshGenerator . FillVertexData ( targetMesh ) ;
meshGenerator . FillTriangles ( targetMesh ) ;
meshGenerator . FillLateVertexData ( targetMesh ) ;
2024-11-21 09:35:48 +08:00
CanvasRenderer canvasRenderer = canvasRenderers [ i ] ;
if ( assignMeshOverrideSingle = = null | | ! disableMeshAssignmentOnOverride )
canvasRenderer . SetMesh ( targetMesh ) ;
else
canvasRenderer . SetMesh ( null ) ;
SkeletonSubmeshGraphic submeshGraphic = submeshGraphics [ i ] ;
if ( useOriginalTextureAndMaterial & & hasBlendModeMaterials ) {
bool allowCullTransparentMesh = true ;
BlendMode materialBlendMode = blendModeMaterials . BlendModeForMaterial ( usedMaterialItems [ i ] ) ;
if ( ( materialBlendMode = = BlendMode . Normal & & submeshInstructionItem . hasPMAAdditiveSlot ) | |
( materialBlendMode = = BlendMode . Additive & & pmaVertexColors ) ) {
allowCullTransparentMesh = false ;
}
#if HAS_CULL_TRANSPARENT_MESH
canvasRenderer . cullTransparentMesh = allowCullTransparentMesh ?
mainCullTransparentMesh : false ;
#endif
}
2024-10-23 17:55:55 +08:00
canvasRenderer . materialCount = 1 ;
2024-11-21 09:35:48 +08:00
}
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
#if SPINE_OPTIONAL_ON_DEMAND_LOADING
if ( Application . isPlaying )
HandleOnDemandLoading ( ) ;
#endif
bool assignAtCanvasRenderer = ( assignMeshOverrideSingle = = null | | ! disableMeshAssignmentOnOverride ) ;
if ( assignAtCanvasRenderer ) {
for ( int i = 0 ; i < submeshCount ; i + + ) {
CanvasRenderer canvasRenderer = canvasRenderers [ i ] ;
canvasRenderer . SetMaterial ( usedMaterialItems [ i ] , usedTextureItems [ i ] ) ;
2024-10-23 17:55:55 +08:00
}
2024-11-21 09:35:48 +08:00
}
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
if ( assignMeshOverrideMultiple ! = null )
assignMeshOverrideMultiple ( submeshCount , meshesItems , usedMaterialItems , usedTextureItems ) ;
}
#if SPINE_OPTIONAL_ON_DEMAND_LOADING
void HandleOnDemandLoading ( ) {
foreach ( AtlasAssetBase atlasAsset in skeletonDataAsset . atlasAssets ) {
if ( atlasAsset . TextureLoadingMode ! = AtlasAssetBase . LoadingMode . Normal ) {
atlasAsset . BeginCustomTextureLoading ( ) ;
if ( ! this . allowMultipleCanvasRenderers ) {
Texture loadedTexture = null ;
atlasAsset . RequireTextureLoaded ( this . mainTexture , ref loadedTexture , null ) ;
if ( loadedTexture )
this . baseTexture = loadedTexture ;
} else {
Texture [ ] textureItems = usedTextures . Items ;
for ( int i = 0 , count = usedTextures . Count ; i < count ; + + i ) {
Texture loadedTexture = null ;
atlasAsset . RequireTextureLoaded ( textureItems [ i ] , ref loadedTexture , null ) ;
if ( loadedTexture )
usedTextures . Items [ i ] = loadedTexture ;
}
}
atlasAsset . EndCustomTextureLoading ( ) ;
2024-10-23 17:55:55 +08:00
}
}
}
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
protected void EnsureCanvasRendererCount ( int targetCount ) {
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
2024-10-23 17:55:55 +08:00
RemoveNullCanvasRenderers ( ) ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
int currentCount = canvasRenderers . Count ;
for ( int i = currentCount ; i < targetCount ; + + i ) {
2024-11-21 09:35:48 +08:00
GameObject go = new GameObject ( string . Format ( "Renderer{0}" , i ) , typeof ( RectTransform ) ) ;
2024-10-23 17:55:55 +08:00
go . transform . SetParent ( this . transform , false ) ;
go . transform . localPosition = Vector3 . zero ;
2024-11-21 09:35:48 +08:00
CanvasRenderer canvasRenderer = go . AddComponent < CanvasRenderer > ( ) ;
2024-10-23 17:55:55 +08:00
canvasRenderers . Add ( canvasRenderer ) ;
2024-11-21 09:35:48 +08:00
SkeletonSubmeshGraphic submeshGraphic = go . AddComponent < SkeletonSubmeshGraphic > ( ) ;
submeshGraphic . maskable = this . maskable ;
submeshGraphic . raycastTarget = false ;
submeshGraphic . rectTransform . pivot = rectTransform . pivot ;
submeshGraphic . rectTransform . anchorMin = Vector2 . zero ;
submeshGraphic . rectTransform . anchorMax = Vector2 . one ;
submeshGraphic . rectTransform . sizeDelta = Vector2 . zero ;
submeshGraphics . Add ( submeshGraphic ) ;
2024-10-23 17:55:55 +08:00
}
}
2024-11-21 09:35:48 +08:00
protected void PrepareRendererGameObjects ( SkeletonRendererInstruction currentInstructions ,
bool isInRebuild = false ) {
int submeshCount = currentInstructions . submeshInstructions . Count ;
DisableUnusedCanvasRenderers ( usedCount : submeshCount , isInRebuild : isInRebuild ) ;
Transform parent = this . separatorParts . Count = = 0 ? this . transform : this . separatorParts [ 0 ] ;
if ( updateSeparatorPartLocation ) {
for ( int p = 0 ; p < this . separatorParts . Count ; + + p ) {
Transform separatorPart = separatorParts [ p ] ;
if ( separatorPart = = null ) continue ;
separatorPart . position = this . transform . position ;
separatorPart . rotation = this . transform . rotation ;
}
}
if ( updateSeparatorPartScale ) {
Vector3 targetScale = this . transform . lossyScale ;
for ( int p = 0 ; p < this . separatorParts . Count ; + + p ) {
Transform separatorPart = separatorParts [ p ] ;
if ( separatorPart = = null ) continue ;
Transform partParent = separatorPart . parent ;
Vector3 parentScale = partParent = = null ? Vector3 . one : partParent . lossyScale ;
separatorPart . localScale = new Vector3 (
parentScale . x = = 0f ? 1f : targetScale . x / parentScale . x ,
parentScale . y = = 0f ? 1f : targetScale . y / parentScale . y ,
parentScale . z = = 0f ? 1f : targetScale . z / parentScale . z ) ;
}
}
int separatorSlotGroupIndex = 0 ;
int targetSiblingIndex = 0 ;
for ( int i = 0 ; i < submeshCount ; i + + ) {
CanvasRenderer canvasRenderer = canvasRenderers [ i ] ;
if ( canvasRenderer ! = null ) {
if ( i > = usedRenderersCount )
canvasRenderer . gameObject . SetActive ( true ) ;
if ( canvasRenderer . transform . parent ! = parent . transform & & ! isInRebuild )
canvasRenderer . transform . SetParent ( parent . transform , false ) ;
canvasRenderer . transform . SetSiblingIndex ( targetSiblingIndex + + ) ;
}
SkeletonSubmeshGraphic submeshGraphic = submeshGraphics [ i ] ;
if ( submeshGraphic ! = null ) {
RectTransform dstTransform = submeshGraphic . 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 ) {
targetSiblingIndex = 0 ;
parent = separatorParts [ + + separatorSlotGroupIndex ] ;
}
}
usedRenderersCount = submeshCount ;
}
protected void DisableUnusedCanvasRenderers ( int usedCount , bool isInRebuild = false ) {
#if UNITY_EDITOR
2024-10-23 17:55:55 +08:00
RemoveNullCanvasRenderers ( ) ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
for ( int i = usedCount ; i < canvasRenderers . Count ; i + + ) {
canvasRenderers [ i ] . Clear ( ) ;
2024-11-21 09:35:48 +08:00
if ( ! isInRebuild ) // rebuild does not allow disabling Graphic and thus removing it from rebuild list.
canvasRenderers [ i ] . gameObject . SetActive ( false ) ;
2024-10-23 17:55:55 +08:00
}
}
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
2024-10-23 17:55:55 +08:00
private void RemoveNullCanvasRenderers ( ) {
if ( Application . isEditor & & ! Application . isPlaying ) {
for ( int i = canvasRenderers . Count - 1 ; i > = 0 ; - - i ) {
if ( canvasRenderers [ i ] = = null ) {
canvasRenderers . RemoveAt ( i ) ;
2024-11-21 09:35:48 +08:00
submeshGraphics . RemoveAt ( i ) ;
2024-10-23 17:55:55 +08:00
}
}
}
}
2024-11-21 09:35:48 +08:00
private void DestroyOldRawImages ( ) {
foreach ( CanvasRenderer canvasRenderer in canvasRenderers ) {
RawImage oldRawImage = canvasRenderer . GetComponent < RawImage > ( ) ;
if ( oldRawImage ! = null ) {
DestroyImmediate ( oldRawImage ) ;
}
}
}
#endif
2024-10-23 17:55:55 +08:00
protected void EnsureMeshesCount ( int targetCount ) {
int oldCount = meshes . Count ;
meshes . EnsureCapacity ( targetCount ) ;
for ( int i = oldCount ; i < targetCount ; i + + )
2024-11-21 09:35:48 +08:00
meshes . Add ( SpineMesh . NewSkeletonMesh ( ) ) ;
}
protected void EnsureUsedTexturesAndMaterialsCount ( int targetCount ) {
int oldCount = usedMaterials . Count ;
usedMaterials . EnsureCapacity ( targetCount ) ;
usedTextures . EnsureCapacity ( targetCount ) ;
for ( int i = oldCount ; i < targetCount ; i + + ) {
usedMaterials . Add ( null ) ;
usedTextures . Add ( null ) ;
}
}
protected void DestroyMeshes ( ) {
foreach ( Mesh mesh in meshes ) {
#if UNITY_EDITOR
if ( Application . isEditor & & ! Application . isPlaying )
UnityEngine . Object . DestroyImmediate ( mesh ) ;
else
UnityEngine . Object . Destroy ( mesh ) ;
#else
UnityEngine . Object . Destroy ( mesh ) ;
#endif
}
meshes . Clear ( ) ;
2024-10-23 17:55:55 +08:00
}
protected void EnsureSeparatorPartCount ( ) {
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
2024-10-23 17:55:55 +08:00
RemoveNullSeparatorParts ( ) ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
int targetCount = separatorSlots . Count + 1 ;
if ( targetCount = = 1 )
return ;
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
2024-10-23 17:55:55 +08:00
if ( Application . isEditor & & ! Application . isPlaying ) {
2024-11-21 09:35:48 +08:00
for ( int i = separatorParts . Count - 1 ; i > = 0 ; - - i ) {
2024-10-23 17:55:55 +08:00
if ( separatorParts [ i ] = = null ) {
separatorParts . RemoveAt ( i ) ;
}
}
}
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
int currentCount = separatorParts . Count ;
for ( int i = currentCount ; i < targetCount ; + + i ) {
2024-11-21 09:35:48 +08:00
GameObject go = new GameObject ( string . Format ( "{0}[{1}]" , SeparatorPartGameObjectName , i ) , typeof ( RectTransform ) ) ;
2024-10-23 17:55:55 +08:00
go . transform . SetParent ( this . transform , false ) ;
2024-11-21 09:35:48 +08:00
RectTransform dstTransform = go . transform . GetComponent < RectTransform > ( ) ;
dstTransform . localPosition = Vector3 . zero ;
dstTransform . pivot = rectTransform . pivot ;
dstTransform . anchorMin = Vector2 . zero ;
dstTransform . anchorMax = Vector2 . one ;
dstTransform . sizeDelta = Vector2 . zero ;
2024-10-23 17:55:55 +08:00
separatorParts . Add ( go . transform ) ;
}
}
protected void UpdateSeparatorPartParents ( ) {
int usedCount = separatorSlots . Count + 1 ;
if ( usedCount = = 1 ) {
usedCount = 0 ; // placed directly at the SkeletonGraphic parent
for ( int i = 0 ; i < canvasRenderers . Count ; + + i ) {
2024-11-21 09:35:48 +08:00
CanvasRenderer canvasRenderer = canvasRenderers [ i ] ;
2024-10-23 17:55:55 +08:00
if ( canvasRenderer . transform . parent . name . Contains ( SeparatorPartGameObjectName ) ) {
canvasRenderer . transform . SetParent ( this . transform , false ) ;
canvasRenderer . transform . localPosition = Vector3 . zero ;
}
}
}
for ( int i = 0 ; i < separatorParts . Count ; + + i ) {
bool isUsed = i < usedCount ;
separatorParts [ i ] . gameObject . SetActive ( isUsed ) ;
}
}
2024-11-21 09:35:48 +08:00
#if UNITY_EDITOR
2024-10-23 17:55:55 +08:00
private void RemoveNullSeparatorParts ( ) {
if ( Application . isEditor & & ! Application . isPlaying ) {
for ( int i = separatorParts . Count - 1 ; i > = 0 ; - - i ) {
if ( separatorParts [ i ] = = null ) {
separatorParts . RemoveAt ( i ) ;
}
}
}
}
2024-11-21 09:35:48 +08:00
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 = GetEffectiveLayoutMode ( previousLayoutScaleMode ) ;
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 ) ;
}
public Vector2 GetPivotOffset ( ) {
return pivotOffset ;
}
public Vector2 GetScaledPivotOffset ( ) {
return pivotOffset * GetLayoutScale ( layoutScaleMode ) ;
}
public void SetScaledPivotOffset ( Vector2 pivotOffsetScaled ) {
pivotOffset = pivotOffsetScaled / GetLayoutScale ( layoutScaleMode ) ;
}
#endif
protected float GetLayoutScale ( LayoutMode mode ) {
Vector2 currentSize = GetCurrentRectSize ( ) ;
mode = GetEffectiveLayoutMode ( mode ) ;
if ( mode = = LayoutMode . WidthControlsHeight ) {
return currentSize . x / referenceSize . x ;
} else if ( mode = = LayoutMode . HeightControlsWidth ) {
return currentSize . y / referenceSize . y ;
}
return 1f ;
}
/// <summary>
/// <c>LayoutMode FitInParent</c> and <c>EnvelopeParent</c> actually result in
/// <c>HeightControlsWidth</c> or <c>WidthControlsHeight</c> depending on the actual vs reference aspect ratio.
/// This method returns the respective <c>LayoutMode</c> of the two for any given input <c>mode</c>.
/// </summary>
protected LayoutMode GetEffectiveLayoutMode ( 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 ;
return mode ;
}
private Vector2 GetCurrentRectSize ( ) {
return this . rectTransform . rect . size ;
}
2024-10-23 17:55:55 +08:00
}
}