Jump to content

Shape Animations [1.13.4]


ZigTheHedge

Recommended Posts

Hey, guys!

I decided to prettify my mods by adding animation to blocks. Making animation itself in VS Model Creator was easy, but the code part not so. Here's what I've done:

1. Added Animatable entityBehavior

2. Initialized animUtil

3. Start animating on GUI open

4. Stop animating on GUI closed

Everything works, but animation doesn't play itself backwards on GUI closing despite of "Rewind" action onActivityStopped.

Am I missing something important?

shape.json:

Spoiler

"animations": [
        {
            "name": "open",
            "code": "open",
            "quantityframes": 30,
            "onActivityStopped": "Rewind",
            "onAnimationEnd": "Hold",
            "keyframes": [
                {
                    "frame": 0,
                    "elements": {
                        "doorleft": { "rotationX": 0.0, "rotationY": 0.0, "rotationZ": 0.0 },
                        "doorright": { "rotationX": 0.0, "rotationY": 0.0, "rotationZ": 0.0 }
                    }
                },
                {
                    "frame": 29,
                    "elements": {
                        "doorleft": { "rotationX": 0.0, "rotationY": 135.0, "rotationZ": 0.0 },
                        "doorright": { "rotationX": 0.0, "rotationY": -135.0, "rotationZ": 0.0 }
                    }
                }
            ]
        }
    ]

BE:

Spoiler

private BlockEntityAnimationUtil animUtil => ((BEBehaviorAnimatable)GetBehavior<BEBehaviorAnimatable>())?.animUtil;

public override void Initialize(ICoreAPI api)
        {
            base.Initialize(api);

            if (api.World.Side == EnumAppSide.Client)
            {
                animUtil.InitializeAnimator("zeekea:tall_locker", new Vec3f(0, Block.Shape.rotateY, 0));
            }

        }

public override void OnReceivedServerPacket(int packetid, byte[] data)
        {
            base.OnReceivedServerPacket(packetid, data);

            if (packetid == (int)EnumBlockStovePacket.OpenGUI)
            {

                ...

                   if (nightstandDialog == null)

                    {
                        Api.World.PlaySoundAt(new AssetLocation("zeekea:sounds/locker_open.ogg"), Pos.X, Pos.Y, Pos.Z);

                        nightstandDialog = new GuiTwentyFourSlots(Lang.Get("zeekea:tall_locker-title"), Inventory, Pos, Api as ICoreClientAPI);
                        nightstandDialog.OnClosed += () =>
                        {
                            nightstandDialog = null;
                            Api.World.PlaySoundAt(new AssetLocation("zeekea:sounds/locker_close.ogg"), Pos.X, Pos.Y, Pos.Z);
                            animUtil.StopAnimation("open");

                        };
                    }

                    nightstandDialog.TryOpen();
                    animUtil.StartAnimation(new AnimationMetaData() { Animation = "open", Code = "open", AnimationSpeed = 0.2F});
                }
            }

        ...

        }

 

 

 

Edited by ZigTheHedge
Link to comment
Share on other sites

1 hour ago, Rhonen said:

but i have no experience by using "Rewind" in the Shape-json.

That's the main problem. Everything works beside it.

1 hour ago, Rhonen said:

Did you test with a breakpoint? is the animUtil object available on the Client-Side? Is OnClosed handled by client?

Yes to all questions. Shape state resets to initial upon call of animUtil.StopAnimation.

I think, I misunderstand the whole Animation behavior, should it really perform backwards animation upon call of StopAnimation, or it should actually stop (and rewind to beginning off camera)?

 

Link to comment
Share on other sites

Well... I've managed to get it to work the way I want, but I don't think that's how it supposed to be.

Spoiler

"animations": [
        {
            "name": "open",
            "code": "open",
            "quantityframes": 30,
            "onActivityStopped": "Rewind",
            "onAnimationEnd": "Hold",
            "keyframes": [
                {
                    "frame": 0,
                    "elements": {
                        "doorleft": { "rotationX": 0.0, "rotationY": 135.0, "rotationZ": 0.0 },
                        "doorright": { "rotationX": 0.0, "rotationY": -135.0, "rotationZ": 0.0 }
                    }
                }
            ]
        },
        {
            "name": "close",
            "code": "close",
            "quantityframes": 30,
            "onActivityStopped": "PlayTillEnd",
            "onAnimationEnd": "Stop",
            "keyframes": [
                {
                    "frame": 0,
                    "elements": {
                        "doorright": { "rotationX": 0.0, "rotationY": 0, "rotationZ": 0.0 },
                        "doorleft": { "rotationX": 0.0, "rotationY": 0, "rotationZ": 0.0 }
                    }
                }
            ]
        }
    ]

 

Spoiler

Close:

animUtil.StartAnimation(new AnimationMetaData() { Animation = "close", Code = "close", AnimationSpeed = 0.2F });
animUtil.StopAnimation("open");

Open:

animUtil.StartAnimation(new AnimationMetaData() { Animation = "open", Code = "open", AnimationSpeed = 0.2F});

I removed last keyframes, and set all necessary coordinates in first ones. Now for some reason it works... I don't get it and I don't like it :(

Link to comment
Share on other sites

And another issue. Models get doubled after animation. It seems, animation doesn't "stop" itself properly on "Stop" action. Here's the scenario (tested with static model hidden via "return true" from OnTesselation):

Model hidden by OnTesselation.

- StartAnimation("open"); // onAnimationEnd = "Hold";

Model visible. Animation on Hold.

- StopAnimation("open");

Model disappears. Animation stopped.

- StartAnimation("close"); // onAnimationEnd = "Stop";

Model visible despite of "Stop" action. animUtil.activeAnimationsByAnimCode.Count == 0.

It seems, I must explicitly call StopAnimation to stop the animated model from rendering, but how to know that moment?

Link to comment
Share on other sites

one more point:
- StopAnimation("open");

also stop rendering the animated model, so your OnTessaltion must deliver false then to render default shape.
may be rewind does not work correctly :(

i would prefer following animations:

closeIdle (like it had been placed) => this animation will be started when block placed

openAnim (OnAnimationEnd = hold)

closeAnim (OnAnimationEnd = hold)

--------------- switch animations --------
onOpenGui => stop closeIdle / closeAnim, start openAnim

onCloseGui => stop openAnim , start closeAnim

---------------------

may be this way can help when rewind not realy works

Edited by Rhonen
  • Like 1
Link to comment
Share on other sites

16 minutes ago, Rhonen said:

one more point:
- StopAnimation("open"); also stop rendering the animated model, so your OnTessaltion must deliver false then to render default shape.
may be rewind does not work correctly :(

Yeah, I know. The thing is OnAnimationEnd: "Stop" doesn't stop rendering the model. That's the problem. Only BlockEntityAnimationUtil::StopAnimation() does.

32 minutes ago, Rhonen said:

closeIdle (like it had been placed) => this animation will be started when block placed

That's the way I were thinking of, but trying to avoid, because I think rendering the animated model is much more expensive resource-wise than static cached version.

I think, I'll leave my shapes not animated until 1.14. It seems, Animation API has changed there.

 

Thanks for trying to help :)

Link to comment
Share on other sites

then this way could be possible

when placed => OnTesselation returns false

--------------- switch animations --------
onOpenGui =>

  • OnTessaltion returns true
  • start openAnim

now the complex part onCloseGui

  • calculate an estimated duration for your animation-duration
  • stop openAnim
  • start closeAnim

when animation-duration is elapsed

  • stop closeAnim, animation settings:
"onActivityStopped": "Stop",
"onAnimationEnd": "Hold",
  • OnTessaltion returns false
  • re-initialize the AnimUtil

 

This is the way i use to get the animated drawbridges / portcullis / gates.

Place it,

OnInteraction start animation and tessaltion = true

Calculate an estimated duration by the known frames

when duration is elapsed, stop animation, re-initialize the animUtil and let tessaltion return false


 

Edited by Rhonen
Link to comment
Share on other sites

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.