nngafook

  • 7 янв 2022
  • Регистрация: 27 фев 2016
    Harald написал

    You need to understand what the Lighting Occlusion Shader shader does to combine it with the Spine shader, not only concatenate both.

    The Lighting Occlusion Shader relies on the Stencil buffer being setup beforehand with lit and occluded pixels, encoded by the stencil value 4 when occluded. The shader basically just needs to do the following:

    if (stencil == occludedStencilValue) // occludedStencilValue == 4
       renderOccluded
    else
      renderNormal
    

    Unfortunately you cannot query the stencil value in a shader function, so you need two render passes with the corresponding stencil tests stencil == 4 and stencil != 4.
    If you look at the shader code, it renders in two passes and uses a different stencil test to draw the first normal pass in the condition stencil not equal to 4:

     Pass
    {
        Stencil
        {
            Ref 4
            Comp NotEqual
        }
        // code to draw the geometry normally follows below
    
    

    and a second pass for drawing the same mesh in the occluded color:

    Pass
    {
        Stencil
        {
            Ref 4
            Comp Equal
        }
        // code to draw the geometry with occlusion color overlaid follows below
    
    

    So what you need to do is:
    1) Remove the existing Spine stencil code, as your asset works differently:

    Stencil {
                Ref[_StencilRef]
                Comp[_StencilComp]
                Pass Keep
            }
    

    2) You just need a duplicated "Normal" pass of the Spine shader:

    Pass {
             Name "Normal"

    one with stencil test for "lit" and one for "occluded", as described above:

    Pass
    {
        Name "Normal"
        Stencil
        {
            Ref 4
            Comp NotEqual
        }
        // add Spine shader code to draw the geometry normally here, copy from Spine shader
    }
    
    Pass
    {
        Name "Normal Occluded"
        Stencil
        {
            Ref 4
            Comp Equal
        }
        // add Spine shader code to draw the geometry with occlusion color overlaid here
    }
    

    Harald... you are a god. EXCELLENT explanation and guidance. I floundered around a bit and eventually got it working. One thing I missed was adding a second _Color as it was being applied when rendering normally AND occluded.

    Thanks so much again. If I knew you in person, and there wasn't a major epidemic going on, I'd buy you a beer. 🙂

    Cheers!

    Sure thing. Here it is. As I said, I just combined the two and made it compile. Best I could do with my lack of knowledge 😃

    Shader "Custom/OccludedSkeleton" {
        Properties {
            _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1
            [NoScaleOffset] _MainTex ("Main Texture", 2D) = "black" {}
            [Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
            [HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
            [HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default
    
        // Outline properties are drawn via custom editor.
        [HideInInspector] _OutlineWidth("Outline Width", Range(0,8)) = 3.0
        [HideInInspector] _OutlineColor("Outline Color", Color) = (1,1,0,1)
        [HideInInspector] _OutlineReferenceTexWidth("Reference Texture Width", Int) = 1024
        [HideInInspector] _ThresholdEnd("Outline Threshold", Range(0,1)) = 0.25
        [HideInInspector] _OutlineSmoothness("Outline Smoothness", Range(0,1)) = 1.0
        [HideInInspector][MaterialToggle(_USE8NEIGHBOURHOOD_ON)] _Use8Neighbourhood("Sample 8 Neighbours", Float) = 1
        [HideInInspector] _OutlineMipLevel("Outline Mip Level", Range(0,3)) = 0
    
        _Color ( "Shadowed Tint", Color ) = ( 1, 1, 1, 1 )
        _OccludedColor ( "Lit Tint", Color ) = ( 0, 0, 0, 0.5 )
    }
    
    CGINCLUDE
        struct appdata_t  
        {
            float4 vertex   : POSITION;
            float4 color    : COLOR;
            float2 texcoord : TEXCOORD0;
        };
    
        struct v2f  
        {
            float4 vertex   : SV_POSITION;
            fixed4 color    : COLOR;
            half2 texcoord  : TEXCOORD0;
        };
    
    
        fixed4 _Color; 
    
        v2f vert( appdata_t IN )  
        {
            v2f OUT;
            OUT.vertex = UnityObjectToClipPos( IN.vertex );
            OUT.texcoord = IN.texcoord;
            OUT.color = IN.color * _Color;
            #ifdef PIXELSNAP_ON
            //OUT.vertex = UnityPixelSnap( OUT.vertex );
            #endif
    
            return OUT;
        }
    ENDCG
    
    SubShader {
        Tags { 
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas" = "True"
        }
    
        Fog { Mode Off }
        Cull Off
        ZWrite Off
        Blend One OneMinusSrcAlpha
        Lighting Off
    
        Stencil {
            Ref[_StencilRef]
            Comp[_StencilComp]
            Pass Keep
        }
    
        Pass
        {
            Stencil
            {
                Ref 4
                Comp NotEqual
            }
    
    
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
            // #pragma multi_compile _ PIXELSNAP_ON
                #include "UnityCG.cginc"
                sampler2D _MainTex;
    
                fixed4 frag( v2f IN ) : SV_Target
                {
                    fixed4 c = tex2D( _MainTex, IN.texcoord ) * IN.color;
                    c.rgb *= c.a;
                    return c;
                }
            ENDCG
        }
    
        Pass
        {
            Stencil
            {
                Ref 4
                Comp Equal
            }
    
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                //#pragma multi_compile _ PIXELSNAP_ON
                #include "UnityCG.cginc"
                sampler2D _MainTex;
    
                fixed4 _OccludedColor;
    
    
                fixed4 frag( v2f IN ) : SV_Target
                {
                    fixed4 c = tex2D( _MainTex, IN.texcoord );
                    return _OccludedColor * c * c.a;
                }
            ENDCG
        }
    
        Pass {
            Name "Normal"
    
            CGPROGRAM
            #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _MainTex;
    
            struct VertexInput {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float4 vertexColor : COLOR;
            };
    
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 vertexColor : COLOR;
            };
    
            VertexOutput vert (VertexInput v) {
                VertexOutput o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.vertexColor = v.vertexColor;
                return o;
            }
    
            float4 frag (VertexOutput i) : SV_Target {
                float4 texColor = tex2D(_MainTex, i.uv);
    
                #if defined(_STRAIGHT_ALPHA_INPUT)
                texColor.rgb *= texColor.a;
                #endif
    
                return (texColor * i.vertexColor);
            }
            ENDCG
        }
    
        Pass {
            Name "Caster"
            Tags { "LightMode"="ShadowCaster" }
            Offset 1, 1
            ZWrite On
            ZTest LEqual
    
            Fog { Mode Off }
            Cull Off
            Lighting Off
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #pragma fragmentoption ARB_precision_hint_fastest
            #include "UnityCG.cginc"
            sampler2D _MainTex;
            fixed _Cutoff;
    
            struct VertexOutput {
                V2F_SHADOW_CASTER;
                float4 uvAndAlpha : TEXCOORD1;
            };
    
            VertexOutput vert (appdata_base v, float4 vertexColor : COLOR) {
                VertexOutput o;
                o.uvAndAlpha = v.texcoord;
                o.uvAndAlpha.a = vertexColor.a;
                TRANSFER_SHADOW_CASTER(o)
                return o;
            }
    
            float4 frag (VertexOutput i) : SV_Target {
                fixed4 texcol = tex2D(_MainTex, i.uvAndAlpha.xy);
                clip(texcol.a * i.uvAndAlpha.a - _Cutoff);
                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG
        }
    }
    }
    

    Harald написал
    nngafook написал

    I have 0 shader skills, and I did my best to move the occlusion pass into the skeleton tint shader, but it does not achieve the desired effect. Instead, if occluded they render normally, but if within the light, all frames are being rendered, regardless of the skin applied.

    Could you please post your current non-working version of your combined shader? Then we can help you out by pointing out any obvious mistakes.

    Any tips Harald?

    • Изменено

    I'm currently trying to implement a lighting asset that does occlusion and dynamic lighting, but I can't get it to occlude my skeletons as I need to apply a specific shader to the skeleton to render if within the lighting field of view, or occlude if not.
    I have 0 shader skills, and I did my best to move the occlusion pass into the skeleton tint shader, but it does not achieve the desired effect. Instead, if occluded they render normally, but if within the light, all frames are being rendered, regardless of the skin applied. It's very odd, but probably makes complete sense to someone who understands shaders.

    I'm hoping someone here can help combine the shaders? I believe that's all that's needed... I hope. I've attached both shaders.

    Thanks!

    Lighting Occlusion Shader

    // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    
    Shader "2D Dynamic Lights/Masks/Occluded"
    {
        Properties
        {
            [PerRendererData] _MainTex ( "Sprite Texture", 2D ) = "white" {}
            _Color ( "Shadowed Tint", Color ) = ( 1, 1, 1, 1 )
           // [MaterialToggle] PixelSnap ( "Pixel snap", Float ) = 0
            _OccludedColor ( "Lit Tint", Color ) = ( 0, 0, 0, 0.5 )
        }
    
    
    CGINCLUDE
    
    struct appdata_t  
    { float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; }; struct v2f
    { float4 vertex : SV_POSITION; fixed4 color : COLOR; half2 texcoord : TEXCOORD0; }; fixed4 _Color;
    sampler2D _MainTex; v2f vert( appdata_t IN )
    { v2f OUT; OUT.vertex = UnityObjectToClipPos( IN.vertex ); OUT.texcoord = IN.texcoord; OUT.color = IN.color * _Color; #ifdef PIXELSNAP_ON //OUT.vertex = UnityPixelSnap( OUT.vertex ); #endif return OUT; } ENDCG SubShader { Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" "CanUseSpriteAtlas" = "True" } Cull Off Lighting Off ZWrite Off Blend One OneMinusSrcAlpha Pass { Stencil { Ref 4 Comp NotEqual } CGPROGRAM #pragma vertex vert #pragma fragment frag // #pragma multi_compile _ PIXELSNAP_ON #include "UnityCG.cginc" fixed4 frag( v2f IN ) : SV_Target { fixed4 c = tex2D( _MainTex, IN.texcoord ) * IN.color; c.rgb *= c.a; return c; } ENDCG } Pass { Stencil { Ref 4 Comp Equal } CGPROGRAM #pragma vertex vert #pragma fragment frag //#pragma multi_compile _ PIXELSNAP_ON #include "UnityCG.cginc" fixed4 _OccludedColor; fixed4 frag( v2f IN ) : SV_Target { fixed4 c = tex2D( _MainTex, IN.texcoord ); return _OccludedColor * c * c.a; } ENDCG } } }

    Spine Skeleton Tint

    // Spine/Skeleton Tint
    // - Two color tint
    // - unlit
    // - Premultiplied alpha blending (Optional straight alpha input)
    // - No depth, no backface culling, no fog.
    
    Shader "Spine/Skeleton Tint" {
       Properties {
          _Color ("Tint Color", Color) = (1,1,1,1)
          _Black ("Black Point", Color) = (0,0,0,0)
          [NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {}
          [Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
          _Cutoff("Shadow alpha cutoff", Range(0,1)) = 0.1
          [HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
          [HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default
    
      // Outline properties are drawn via custom editor.
      [HideInInspector] _OutlineWidth("Outline Width", Range(0,8)) = 3.0
      [HideInInspector] _OutlineColor("Outline Color", Color) = (1,1,0,1)
      [HideInInspector] _OutlineReferenceTexWidth("Reference Texture Width", Int) = 1024
      [HideInInspector] _ThresholdEnd("Outline Threshold", Range(0,1)) = 0.25
      [HideInInspector] _OutlineSmoothness("Outline Smoothness", Range(0,1)) = 1.0
      [HideInInspector][MaterialToggle(_USE8NEIGHBOURHOOD_ON)] _Use8Neighbourhood("Sample 8 Neighbours", Float) = 1
      [HideInInspector] _OutlineMipLevel("Outline Mip Level", Range(0,3)) = 0
       }
    
       SubShader {
          Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
    
      Fog { Mode Off }
      Cull Off
      ZWrite Off
      Blend One OneMinusSrcAlpha
      Lighting Off
    
      Stencil {
         Ref[_StencilRef]
         Comp[_StencilComp]
         Pass Keep
      }
    
      Pass {
         Name "Normal"
    
         CGPROGRAM
         #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
         #pragma vertex vert
         #pragma fragment frag
         #include "UnityCG.cginc"
         sampler2D _MainTex;
         float4 _Color;
         float4 _Black;
    
         struct VertexInput {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
            float4 vertexColor : COLOR;
         };
    
         struct VertexOutput {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
            float4 vertexColor : COLOR;
         };
    
         VertexOutput vert (VertexInput v) {
            VertexOutput o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = v.uv;
            o.vertexColor = v.vertexColor * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor.
            return o;
         }
    
         float4 frag (VertexOutput i) : SV_Target {
            float4 texColor = tex2D(_MainTex, i.uv);
    
            #if defined(_STRAIGHT_ALPHA_INPUT)
            texColor.rgb *= texColor.a;
            #endif
    
            return (texColor * i.vertexColor) + float4(((1-texColor.rgb) * _Black.rgb * texColor.a[i]_Color.a[/i]i.vertexColor.a), 0);
         }
         ENDCG
      }
    
      Pass {
         Name "Caster"
         Tags { "LightMode"="ShadowCaster" }
         Offset 1, 1
         ZWrite On
         ZTest LEqual
    
         Fog { Mode Off }
         Cull Off
         Lighting Off
    
         CGPROGRAM
         #pragma vertex vert
         #pragma fragment frag
         #pragma multi_compile_shadowcaster
         #pragma fragmentoption ARB_precision_hint_fastest
         #include "UnityCG.cginc"
         sampler2D _MainTex;
         fixed _Cutoff;
    
         struct VertexOutput {
            V2F_SHADOW_CASTER;
            float4 uvAndAlpha : TEXCOORD1;
         };
    
         VertexOutput vert (appdata_base v, float4 vertexColor : COLOR) {
            VertexOutput o;
            o.uvAndAlpha = v.texcoord;
            o.uvAndAlpha.a = vertexColor.a;
            TRANSFER_SHADOW_CASTER(o)
            return o;
         }
    
         float4 frag (VertexOutput i) : SV_Target {
            fixed4 texcol = tex2D(_MainTex, i.uvAndAlpha.xy);
            clip(texcol.a * i.uvAndAlpha.a - _Cutoff);
            SHADOW_CASTER_FRAGMENT(i)
         }
         ENDCG
      }
       }
       CustomEditor "SpineShaderWithOutlineGUI"
    }
    
    • Изменено

    Hi there!

    I want to have multiple skins in one skeleton, but I need to add a collider on each skin, or at least have the active collider change based on which skin is currently being used in Unity, i.e. I don't need ALL of the colliders active at any one time, but I need the correct one active.

    How can I achieve this?

    Thanks so much for all the resources Erikari!
    Right now I have a really small concept, that hasn't been flushed out, and I'm just messing around with the artstyle more than programming, which is opposite to how I normally end up working on projects. Normally I have most of the idea fleshed out in my mind, and cause I'm a stronger programmer, I end up working on gameplay way more than art.
    But I always have SO much fun playing around with art, probably cause it's not really what I do for a living, so there's more learning and more opportunity for fulfillment compared to programming things that I probably have programmed in the past. So learning more of the art side of game dev has been some of the most fun I've had in a long time.

    I'm hoping to add some of the pieces of game mechanics that I do have figured out in this weekend, and if I get it to a point where it's not just a bunch of skeletons and png's in a Unity scene, then I'll definitely share it with you! But you have to excuse the poor art, as I'm sure there's SOO much wrong with it 😛 🙁

    This is my first attempt at trying to make an isometric game (well second if I'm being honest) and because I've never really been able to create my own assets in the past, I always defaulted to side scrolling or top down as it was easier to find assets that I could "steal" from google as developer art. But I'm hoping that challenging myself into isometric for this game idea, I can not only learn more about programming an isometric game, but also about the art pipeline.

    Anyways! Thanks again for the information! As much information and resources as there are out there for Spine, I find it hard to find ones that really teach me things that I need to learn about. There's a lot of speed rigging, or speed animating, or animating for a specific pipeline... but the windmill video taught me sooo many things, a lot of them so small and simple, you wouldn't even believe!

    Cheers!

    Amazing! I'll definitely check out the video in a bit.
    Thanks for the advice!


    Erikari написал

    Hello! A method I like to use is to create the bone, animate it, and only then duplicate it, because Spine will ask you if you want to also copy the keys, to which you'll reply yes. The next step is to simply offset the animation using the offset tool: Dopesheet - Spine User Guide: Key Offset

    You can see I do a continuous smoke trail coming out of a chimney with this method in this video tutorial:

    Windmill example: Smoke

    @Erikari That video was the most helpful video I've watched in a long time. I actually learned SOOO much. Thank you!!!

    I do have one question though... In the video you mention that isometric can be attained by adding a scale of 0.86 and a shear of 30 degrees. Is this a rule of isometric in general? Do you have more information about that by chance? Cause I'm working on an isometric game right now, and I'm predominantly a programmer, so everything art-wise is a learning, and the basic rules and concepts are often lost on me. So any "tricks" or rules that would help me easier achieve the isometric look, would be greatly appreciated!!

    Thanks again!

    Hey all!
    I'm not exactly sure how to search for this particular issue as the terms are very broad, so I'm hoping I'm not asking a question that's been asked 20 times before...

    I've attached a gif (excuse the "poor" quality) of an animation I made in Spine. The part of the animation in question, is the arcs coming out of the satellite dish.
    The arc is one png, and I made three copies of the bone with the attachment image in it. And had to animate each one (albeit the same values just triggering at different times in the timeline) separately. I was wondering if there is some easier way to do that without having to triplicate (or maybe a lot more depending on the animation needs) the bones and manually copy the values to each bone. (WTB a way to copy translate values of a bone and paste to another bone)

    And to take it one step further... I did this process with another animation, but this is a repeating one. So imagine these arcs coming out, but they never stop coming out. So bone 1, would reset to the initial position, scale and alpha, and when bone 3 was finishing it's animation, bone 1 would start, and so on and so on. So that was kind of annoying cause I had to line up so that they repeat in perfect time with the rest of the animation (Hope that makes sense, I can attach another gif if further clarification is needed for that one).

    Hope there's an easier way...
    Thanks!

    (Edit: Decided to just add a gif of the looping issue noted above)

    • Изменено
    • Изменено

    Hey there.
    Might be a silly question, but I can't seem to find anything. Can spine tween an image into another while animating to the frame? I'm thinking like how flash does it with their shapes.

    Thanks!

    Awesome! That'll help out a lot!

    Thanks Pharan!

    Hey Pharan.

    I mean, honestly nothing is stopping me from unsubscribing at death, or anything else that might happen mid attack. I'm just worried that that's a rabbit hole that might never end, depending on animations and characters. Example, what if enemies can be teleported or something, that's a new thing that can interrupt the attack, and the more things that get introduced, is just more things to have to try to remember, for any enemy that it might happen to basically.

    You're right, I think I am tangling myself up, especially with character states sometimes depending on animation and vice versa. In this particular case though, if the attack animation is playing and it dies, I immediately set the character state to dead, but the problem is that the attack complete event still occurs while the dying animation is playing. So variable resets that happen when attack completes assumes that the character is still alive. So again, yeah I can just do a check in the animation complete if the character is still alive, but still feels like a fix, and not the "right" way if that makes sense?

    Another question, can you clarify at different points in an animation cycle, when would I want to use the trackEntry.animation.name vs the skeletonAnimation.AnimationName. i.e. when setting an animation and I don't want set it if it's already playing, I know I can check the skeleton, but in complete events, when should I check the skeleton and when should I check the trackEntry. Like in that attack complete callback in my original post, using one gives a different result than the other.

    Thanks once again Pharan. As usual, you are my saviour!


    08 Sep 2017, 14:36


    Another quick question on top of all the others :S

    Is there any way to have a complete event not fire after it's been "interrupted". And I say interrupted with quotes, because what I really mean is, I call SetAnimation("Attack"), and then before it's done, I can call SetAnimation("Die"), and it plays the death animation, it stays dead (invisible), but I still get the attack complete event. I get that the event fires after every loop, that's fine, but when it's not "playing", is there a way to have it not fire anymore besides manually removing the listener when changing the event.

    Thanks!

    • Изменено

    Hi!

    I have an issue keeping my event handling clean. I'll provide code examples of what I do below. An example of the issue is...
    The enemy plays the attack animation, and on animation complete I reset some variables and do other things. But it's possible that it can die mid attack animation. And if timed right, I get the animation complete event fired still. Normally I do checks against the skeletonAnimation.AnimationName to make sure the correct animation is playing when events fire, and only recently did I realize I can/should use the trackEntry passed into the complete event as well (my bad for not knowing). So in this example, I changed the check in the attack complete event callback, to make sure the skeleton's current animation is the attack, which is fine in theory, but the next problem is, I currently add the listener for the event when the animation begins, so if that complete never occurs (cause the skeleton's animation was not attack), I don't remove the event listener. Which makes me have to add a sanity check to the death logic, i.e. if killed mid attack, remove events and reset variables, which I'd rather not do, and let the events kind of be contained within their own callbacks if that makes sense.

    I thought I could use the interrupt event, but because that gets called on complete as well, I would have to monitor every interrupt and have a case for any interrupts that occur and what interrupted it... i.e. if attack was interrupted by death and nothing else.

    Anyway, I think the more I try to explain, the more confusing it may get. Hopefully the code helps with clarity.

    So basically all I'm wondering is how best to manage events and callbacks. Seems like it's probably pretty easy for most, but for some reason I think I'm confusing myself the more I dig into the systems.

    Let me know if anything is unclear.

    P.S. side question. I used to set animations by skeletonAnimation.AnimationName = attackAnimation; And set the looping separately, but I found the SetAnimation() today... is there any difference between the two, and if not, I assume it's better to use SetAnimation()?

    
    public void Attack() {
         skeletonAnimation.state.SetAnimation(0, ATTACK_ANIMATION, false);
         skeletonAnimation.state.Complete += OnAttackComplete;
    }
    
    // With this version, we [i]do[/i] get into the if check if the enemy dies while attacking
    private void OnAttackComplete() {
         if (trackEntry.animation.name == ATTACK_ANIMATION) {
                // Do things
                skeletonAnimation.state.Complete -= OnAttackComplete;
         }
    }
    
    // With this version, we do [i]not[/i] get into the if check if the enemy dies while attacking
    private void OnAttackComplete() {
         if (skeletonAnimation.AnimationName == ATTACK_ANIMATION) {
                // Do things
                skeletonAnimation.state.Complete -= OnAttackComplete;
         }
    }
    
    
    

    Oh very interesting!

    I'll definitely check that out!

    An in terms of the Inkscape (or other image editing/creating software) to Spine to Unity workflow, should I be exporting the sprites/parts of the skeleton at the native 1:1 size that it'll be in Unity, or bigger and then use a smaller scale in the skeleton data object, or another approach. What would you suggest?

    Thanks as always Pharan!

    • Изменено

    Hi all! I have a question about sprite sizes and what size I should my be exporting my skeleton parts to Spine as, as it relates to eventually getting it into Unity.

    I know typically I want to aim for the biggest resolution size, and then scale down as you don't lose (as much) quality scaling sprites down.
    So for example, if I want my character to be 128x128, should I export the parts the size that they are when they're all together to make the full character, or should I export them bigger, and then mess with the scale of the spine data object in Unity?
    Currently I have a skeleton rigged, but I exported the sprites much bigger than 128, and I don't want to re-export all the sprites and set the skeleton back up if there's a way to still get it sized right on the export to Unity.

    Sorry if that got a little ramble-y and confusing. This subject as simple as it is, even just using regular sprites and not spine skeletons, always seems to mess with my brain a bit, cause no matter what I do, sprites never look as sharp as they do in Inkscape when I import them to Unity. So I'm constantly on the search for getting crisp looking sprites (not pixel art). So if there's some proper way to do it in the Spine workflow, I would love to be able to lock that down in my mind.

    Thanks!

    Alrighty! Thanks for the help!
    Cheers!