Forum rules - please read before posting.

Spine Skins

Hi Chris,

Is it possible to have a spine player with 8 separate GameObjects each with their own SkeletonAnimation, and then handle disabling/enabling them as-needed (based on the direction my character is facing)? I want to swap skins in and out and as my player has 8 directions I will need to swap in and out 8 different children spine skeletons. If that makes sense?

«13

Comments

  • The Spine Integration on the AC wiki allows for this.

  • Can you elaborate how please?
  • We are already discussing this in your other thread.

  • Hi Chris,

    I am trying to use a custom action script to change the skin. But I am getting an error:

    Assets/Sleepytime Village/Scripts/CustomActions/ChangeSkinAction.cs(29,13): error CS0246: The type or namespace name 'SkeletonAnimator' could not be found (are you missing a using directive or an assembly reference?)

    Here is my code, can you help at all?

    using UnityEngine;
    #if UNITY_EDITOR
    using UnityEditor;
    #endif
    using Spine;
    using Spine.Unity;
    
    namespace AC
    {
    
        [System.Serializable]
        public class ChangeSkinAction : Action
        {
    
            public override ActionCategory Category { get { return ActionCategory.Custom; } }
            public override string Title { get { return "Switch Spine Skin"; } }
            public override string Description { get { return "This switches the Spine skins"; } }
    
            public string SkinName;
            public GameObject PlayerGameObject;
    
    
            public override float Run()
            {
                if( PlayerGameObject == null ){
                    Debug.LogError( "PlayerGameObject is Null" );
                }
    
                SkeletonAnimator sa = PlayerGameObject.GetComponent<SkeletonAnimator>();
    
                if( sa == null ){
                    Debug.LogError( "PlayerGameObject does not have the SkeletonAnimator component" );
                }
    
                PlayerGameObject.GetComponent<SkeletonAnimator>().initialSkinName = SkinName;
                return 0f;
            }
    
    
            public override void Skip ()
            {
                Run ();
            }
    
    
            #if UNITY_EDITOR
    
            public override void ShowGUI ()
            {
                PlayerGameObject = (GameObject) EditorGUILayout.ObjectField( "PlayerGameObject", PlayerGameObject, typeof (GameObject), true);
                SkinName = EditorGUILayout.TextField (SkinName );
            }
    
            #endif
        }
    }
    
  • in fact, i think this code, but i would need this as a custom script if possible? Ignore code above:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using Spine;
    using Spine.Unity;
    
    public class SkinExample : MonoBehaviour {
    
        // Drag your skeleton onto this component slot in Unity:
        public SkeletonAnimation skeletonAnimation;
    
        // Used for setting animations:
        private Spine.AnimationState spineAnimationState;
    
        // Used for accessing skeleton for flipping on x or y, setting the skin, etc:
        private Spine.Skeleton skeleton;
    
        // Start is called before the first frame update
        void Start() {
    
            try {
                skeletonAnimation = GetComponent<SkeletonAnimation>();
            } catch (MissingComponentException e) {
                Debug.LogWarning(e.Message);
                skeletonAnimation = gameObject.AddComponent<SkeletonAnimation>();
            }
    
            if(skeletonAnimation != null) {
                spineAnimationState = skeletonAnimation.AnimationState;
                spineAnimationState.Data.DefaultMix = 0.01f;
                skeleton = skeletonAnimation.Skeleton;
    
                //Debug.Log((skeleton == null) ? name + " skeleton null!" : name + " skeleton here!");
            }
        }
    
        /// <summary>
        /// Info found here: http://en.esotericsoftware.com/spine-runtime-skins#Skin-changes
        /// </summary>
        public void ChangeSkin() {
    
            skeleton.SetSkin("Clothes/Suit");
            skeleton.SetSlotsToSetupPose(); // 2. Use setup pose to set base attachments.
            spineAnimationState.Apply(skeleton); // 3. Use AnimationState to set attachments active in the current movement.
            // 4. Set attachments that were manually changed.
        }
    }
    
  • In what way do you want to customise it? The ChangeSkin method is public - you can call it from AC Actions.

  • edited April 2022
    For the player prefab rather than a scene instance? Would be great to have the option to choose Active Player or NPC like other character actions
  • edited April 2022

    Update, so I have made it into a custom action successfully, but get two errors:

    PlayerGameObject is Null
    UnityEngine.Debug:LogError (object)
    

    The Spine GameObject is missing once the scene is stopped.

    and

    MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
    Your script should either check if it is null or you should not destroy the object.
    

    Plus, it isn't changing the skin, and I wonder if it is because the skin is actually Clothes/(skinname) - see screenshots.

    https://www.dropbox.com/sh/r7x1oxds8jm3k16/AADoYzE8dSn34odF81TBopE0a?dl=0

    Updated code below:

    using UnityEngine;
    #if UNITY_EDITOR
    using UnityEditor;
    #endif
    using Spine;
    using Spine.Unity;
    
    namespace AC
    {
        [System.Serializable]
        public class ChangeSkinAction : Action
        {
    
            public override ActionCategory Category { get { return ActionCategory.Custom; } }
            public override string Title { get { return "Change Skin"; } }
            public override string Description { get { return "This changes the skin"; } }
    
            public string SkinName;
            public GameObject PlayerGameObject;
    
            public override float Run()
            {
                if( PlayerGameObject == null ){
                    Debug.LogError( "PlayerGameObject is Null" );
                }
    
                SkeletonAnimation sa = PlayerGameObject.GetComponent<SkeletonAnimation>();
    
                if( sa == null ){
                    Debug.LogError( "PlayerGameObject does not have the SkeletonAnimation component" );
                }
    
                PlayerGameObject.GetComponent<SkeletonAnimation>().initialSkinName = SkinName;
                return 0f;
            }
    
    
            public override void Skip ()
            {
                Run ();
            }
    
    
            #if UNITY_EDITOR
    
            public override void ShowGUI ()
            {
                PlayerGameObject = (GameObject) EditorGUILayout.ObjectField( "PlayerGameObject", PlayerGameObject, typeof (GameObject), true);
                SkinName = EditorGUILayout.TextField (SkinName );
            }
    
            #endif
        }
    }
    
  • edited April 2022

    If your Action references a Player prefab that's spawned in at runtime, you need to access the Player with KickStarter.player - any reference made to the GameObject itself won't work at runtime.

    e.g.:

    using UnityEngine;
    #if UNITY_EDITOR
    using UnityEditor;
    #endif
    using Spine;
    using Spine.Unity;
    
    namespace AC
    {
        [System.Serializable]
        public class ChangeSkinAction : Action
        {
    
            public override ActionCategory Category { get { return ActionCategory.Custom; } }
            public override string Title { get { return "Change Skin"; } }
            public override string Description { get { return "This changes the skin"; } }
    
            public bool isPlayer;
            public string SkinName;
            public GameObject gameObject;
    
            public override float Run()
            {
                if (isPlayer && KickStarter.player)
                {
                    gameObject = KickStarter.player.gameObject;
                }
                if( gameObject == null ){
                    LogWarning( "GameObject is Null" );
                    return 0f;
                }
    
                SkeletonAnimation sa = gameObject.GetComponent<SkeletonAnimation>();
    
                if( sa == null ){
                    LogWarning( "PlayerGameObject does not have the SkeletonAnimation component" );
                    return 0f;
                }
    
                sa.initialSkinName = SkinName;
                return 0f;
            }
    
    
            public override void Skip ()
            {
                Run ();
            }
    
    
            #if UNITY_EDITOR
    
            public override void ShowGUI ()
            {
                isPlayer = EditorGUILayout.Toggle ("Is Player?", isPlayer);
                if (!isPlayer)
                {
                    gameObject = (GameObject) EditorGUILayout.ObjectField("Game Object", gameObject, typeof (GameObject), true);
                }
                SkinName = EditorGUILayout.TextField (SkinName );
            }
    
            #endif
        }
    }
    

    Plus, it isn't changing the skin, and I wonder if it is because the skin is actually Clothes/(skinname) - see screenshots.

    This is not an AC issue - the SkinExample script demonstrates how to change a Spine skin.

  • edited April 2022

    updated code to what you updated but getting this error:

    Assets/Sleepytime Village/Scripts/CustomActions/ChangeSkinAction.cs(3,1): error CS1529: A using clause must precede all other elements defined in the namespace except extern alias declarations

    Plus, SpineExample only works on player instance in scene, and in trying it out the SkinExample script (empty game opbject in scene, dragging skeleton instance in from scene) it is removed from the scene on play instantly.

  • updated code to what you updated but getting this error:

    Be sure to replace all code in the script with the code above.

  • Can you help me understand how to incorporate the SpineExample script into a custom action script so I can insert the skin name into the action for the player prefab spineGameObject like in ChangeSkinAction (that currently seems to not do anything :) )

  • edited May 2022

    The Action and SkinExample script are doing two different things.

    The SkinExample calls SetSkin to change the skin name, different to your Action which calls initialSkinName.

    Adapt SkinExample to behave the way you want it to before attempting to convert it into an Action.

  • Problem is I cannot get the SkinExample to work or do anything currently, See video and PDF detailing everything.

    PDF:

    https://www.dropbox.com/s/y0krbbgslmcea18/Switching Skins.pdf?dl=0

    Video:

    https://www.dropbox.com/s/q99zv8op15r7ptk/SpineSkinHeadache.mov?dl=0

    Thanks Chris!

  • The SkinExample script is unrelated to AC. It will not change the skin automatically, however - it's a case of calling its ChangeSkin function manually.

  • edited May 2022

    Ok, no probs, tried that and got an error, I appreciate it is not related to AC, but any further help you can spare would be greatly received :

    NullReferenceException: Object reference not set to an instance of an object
    SkinExample.ChangeSkin () (at Assets/Sleepytime Village/Scripts/SkinExample.cs:42)
    UnityEngine.Events.InvokableCall.Invoke () (at /Users/bokken/buildslave/unity/build/Runtime/Export/UnityEvent/UnityEvent.cs:180)
    UnityEngine.Events.UnityEvent.Invoke () (at /Users/bokken/buildslave/unity/build/Runtime/Export/UnityEvent/UnityEvent/UnityEvent_0.cs:58)
    AC.ActionEvent.Run () (at Assets/AdventureCreator/Scripts/Actions/ActionEvent.cs:39)
    AC.ActionList+<RunAction>d__41.MoveNext () (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:429)
    UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at /Users/bokken/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    AC.ActionList:ProcessAction(Int32) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:379)
    AC.ActionList:ProcessActionEnd(ActionEnd, Int32, Boolean) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:571)
    AC.ActionList:EndAction(Action) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:530)
    AC.<RunAction>d__41:MoveNext() (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:494)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    AC.ActionList:ProcessAction(Int32) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:379)
    AC.ActionList:ProcessActionEnd(ActionEnd, Int32, Boolean) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:571)
    AC.ActionList:EndAction(Action) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:530)
    AC.<RunAction>d__41:MoveNext() (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:494)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    AC.ActionList:ProcessAction(Int32) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:379)
    AC.ActionList:ProcessActionEnd(ActionEnd, Int32, Boolean) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:571)
    AC.ActionList:EndActionParallel(Action) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:653)
    AC.<RunAction>d__41:MoveNext() (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:490)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    AC.ActionList:ProcessAction(Int32) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:379)
    AC.ActionList:BeginActionList(Int32, Boolean) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:346)
    AC.ActionList:Interact(Int32, Boolean) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:247)
    AC.ActionList:Interact() (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:210)
    AC.SceneSettings:PlayStartCutscene() (at Assets/AdventureCreator/Scripts/Game engine/SceneSettings.cs:346)
    AC.SceneSettings:OnStart() (at Assets/AdventureCreator/Scripts/Game engine/SceneSettings.cs:110)
    AC.SaveSystem:InitAfterLoad() (at Assets/AdventureCreator/Scripts/Save system/SaveSystem.cs:692)
    AC.MultiSceneChecker:Start() (at Assets/AdventureCreator/Scripts/Game engine/MultiSceneChecker.cs:62)
    
  • Uncomment the Debug.Log in the script - that'll help reveal the issue.

  • only thing revealed in Console is the same error:

    NullReferenceException: Object reference not set to an instance of an object
    SkinExample.ChangeSkin () (at Assets/Sleepytime Village/Scripts/SkinExample.cs:42)
    UnityEngine.Events.InvokableCall.Invoke () (at /Users/bokken/buildslave/unity/build/Runtime/Export/UnityEvent/UnityEvent.cs:180)
    UnityEngine.Events.UnityEvent.Invoke () (at /Users/bokken/buildslave/unity/build/Runtime/Export/UnityEvent/UnityEvent/UnityEvent_0.cs:58)
    AC.ActionEvent.Run () (at Assets/AdventureCreator/Scripts/Actions/ActionEvent.cs:39)
    AC.ActionList+d__41.MoveNext () (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:429)
    UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at /Users/bokken/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    AC.ActionList:ProcessAction(Int32) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:379)
    AC.ActionList:ProcessActionEnd(ActionEnd, Int32, Boolean) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:571)
    AC.ActionList:EndAction(Action) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:530)
    AC.d__41:MoveNext() (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:494)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    AC.ActionList:ProcessAction(Int32) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:379)
    AC.ActionList:ProcessActionEnd(ActionEnd, Int32, Boolean) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:571)
    AC.ActionList:EndAction(Action) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:530)
    AC.d__41:MoveNext() (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:494)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    AC.ActionList:ProcessAction(Int32) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:379)
    AC.ActionList:ProcessActionEnd(ActionEnd, Int32, Boolean) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:571)
    AC.ActionList:EndActionParallel(Action) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:653)
    AC.d__41:MoveNext() (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:490)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    AC.ActionList:ProcessAction(Int32) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:379)
    AC.ActionList:BeginActionList(Int32, Boolean) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:346)
    AC.ActionList:Interact(Int32, Boolean) (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:247)
    AC.ActionList:Interact() (at Assets/AdventureCreator/Scripts/ActionList/ActionList.cs:210)
    AC.SceneSettings:PlayStartCutscene() (at Assets/AdventureCreator/Scripts/Game engine/SceneSettings.cs:346)
    AC.SceneSettings:OnStart() (at Assets/AdventureCreator/Scripts/Game engine/SceneSettings.cs:110)
    AC.SaveSystem:InitAfterLoad() (at Assets/AdventureCreator/Scripts/Save system/SaveSystem.cs:692)
    AC.MultiSceneChecker:Start() (at Assets/AdventureCreator/Scripts/Game engine/MultiSceneChecker.cs:62)

  • I just need to be able to access the SkeletonAnimation, initial Skin dropdown choices in a custom action script so at the start of a scene i can set player prefab to be Clothes/Suit eg or Clothes/UnTuck eg - these work if i manually choose the dropdown on the Player prefab, but I want to access on scene start for my prefab.

  • It's unrelated to AC. Leave the Player prefab in the scene so that it's not being spawned in at runtime, and focus on getting the SkinExample script working. Once you've debugged it, revisit this topic and we can see about converting it into an action.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.