bernie-g / geckolib

GeckoLib is an animation engine for Minecraft mods, with support for complex 3D keyframe-based animations, numerous easings, concurrent animation support, sound and particle keyframes, event keyframes, math-based animations, and more. Available for all major modloaders.
https://geckolib.com
MIT License
609 stars 127 forks source link

Off-screen animations play from the start once the animating entity is rendered, regardless of timing #615

Closed BobMowzie closed 2 months ago

BobMowzie commented 2 months ago

If an animation starts while a mob is not rendered, the animation will play from the beginning. Instead, the animation should play as though it had started already, taking into account the time spent not rendered. If the animation's full duration had passed while not rendered, the animation should not play at all.

This gremlin example plays an interact animation when given an item. However, if the player looks away before the animation begins, the animation won't play until the player looks back at the Gremlin. Not very consequential for an interact animation, but for an attack animation that players need to dodge or block with correct timing, this would make a big difference.

https://github.com/user-attachments/assets/00a109ec-a852-40b5-8386-951fb5c0da19

Tslat commented 2 months ago

This involves a fundamental change in the signalling of Geckolib's animations, which poses problems

It's on the cards for eventually, but it's not an easy proble mto solve gracefully

in the meantime, if you have mobs this is critical on, I'd recommend setting noCulling = true on that mob so that it doesn't cull

BobMowzie commented 2 months ago

Understood. Thanks for holding on to this. If it helps at all, I was able to solve it specifically for Mowzie's Mobs needs by using a combination of this new playAnimation function and a rewritten adjustTick. But I'm fairly certain this only works for specifically how I use Geckolib. And I have no idea why it works or how I arrived at this solution.

    public void playAnimation(T animatable, RawAnimation animation) {
        forceAnimationReset();
        setAnimation(animation);
        currentAnimation = this.animationQueue.poll();
        isJustStarting = true;
        adjustTick(animatable.getTick(animatable) + Minecraft.getInstance().getPartialTick());
        transitionLength = 0;
    }

    @Override
    protected double adjustTick(double tick) {
        if (this.shouldResetTick) {
            if (getAnimationState() == State.TRANSITIONING) {
                this.tickOffset = tick;
            }
            else if (getAnimationState() != State.STOPPED) {
                this.tickOffset += transitionLength;
            }
            this.shouldResetTick = false;
        }

        double adjustedTick = this.animationSpeedModifier.apply(this.animatable) * Math.max(tick - this.tickOffset, 0) + timingOffset;
        if (this.currentAnimation != null && this.currentAnimation.loopType() == Animation.LoopType.LOOP) adjustedTick = adjustedTick % this.currentAnimation.animation().length();
        if (adjustedTick == timingOffset) isJustStarting = true;
        return adjustedTick;
    }
Tslat commented 2 months ago

Try doing this via the death ticks query in 4.6+