Jump to content

Partial Tick stuttering (1.19.3)


Povstalec

Recommended Posts

Alright, so I have a Block Entity renderer and I want it to have a smooth animation. My current animations work well, other than having a lower framerate, which is obvious. However, when I try interpolating using Partial Ticks, the results vary greatly. Sometimes the animation is perfectly smooth, other times it stutters a lot. From testing I know the varying results are caused by me pausing the game and coming back. When I pause the game, the rendering does indeed stop, but Partial Ticks still do their thing and when I unpause it, Partial Ticks are ahead/behind where they were, which causes the Block Entity animation to start stuttering.

How do I get around this issue?

Here's my code: (it's basically raising and then lowering five ring models one by one)

public class TransportRingsRenderer implements BlockEntityRenderer<TransportRingsEntity>
{
	private static final ResourceLocation TRANSPORT_RINGS_TEXTURE = new ResourceLocation(StargateJourney.MODID, "textures/block/transport_rings.png");
	private final ModelPart first_ring;
	private final ModelPart second_ring;
	private final ModelPart third_ring;
	private final ModelPart fourth_ring;
	private final ModelPart fifth_ring;
	
	public TransportRingsRenderer(BlockEntityRendererProvider.Context p_173554_)
	{
		ModelPart modelpart = p_173554_.bakeLayer(LayerInit.TRANSPORT_RING_LAYER);
		this.first_ring = modelpart.getChild("first_ring");
		this.second_ring = modelpart.getChild("second_ring");
		this.third_ring = modelpart.getChild("third_ring");
		this.fourth_ring = modelpart.getChild("fourth_ring");
		this.fifth_ring = modelpart.getChild("fifth_ring");
	}
	
	private float getHeight(int ringNumber, int emptySpace, int transportHeight, int ticks, int progress, float partialTicks)
	{
		float ringHeight = 0;
		
		int startTicks = 6 * (ringNumber - 1);
		int movingHeight = progress - 6 * (ringNumber - 1);
		int staticHeight = transportHeight - 2 * (ringNumber - 1);
		
		int stopHeight = transportHeight + 17 - 4 * (5 - ringNumber);
		
		if(ticks == progress && progress > startTicks && progress < stopHeight)
			ringHeight = (movingHeight + partialTicks) * 4;
		else if(progress >= stopHeight)
			ringHeight = staticHeight * 4;
		else if(ticks != progress && progress > startTicks && progress < stopHeight)
			ringHeight = (movingHeight - partialTicks) * 4;
		
		if(emptySpace > 0)
				return ringHeight;
		else if(emptySpace < 0)
			return -ringHeight;
		
		return 0;
	}
	
	@Override
	public void render(TransportRingsEntity rings, float partialTick, PoseStack stack,
			MultiBufferSource source, int combinedLight, int combinedOverlay)
	{
		VertexConsumer vertexconsumer = source.getBuffer(RenderType.entitySolid(TRANSPORT_RINGS_TEXTURE));
		// It takes the rings 29 ticks to get into position if there is empty space right above them
		
		if(rings.progress > 0)
		{
			this.first_ring.y = getHeight(1, rings.emptySpace, rings.transportHeight, rings.ticks, rings.progress, partialTick);
			this.first_ring.render(stack, vertexconsumer, rings.transportLight, combinedOverlay);
		}
		if(rings.progress > 6)
		{
			this.second_ring.y = getHeight(2, rings.emptySpace, rings.transportHeight, rings.ticks, rings.progress, partialTick);
		    this.second_ring.render(stack, vertexconsumer, rings.transportLight, combinedOverlay);
		}
		if(rings.progress > 12)
		{
			this.third_ring.y = getHeight(3, rings.emptySpace, rings.transportHeight, rings.ticks, rings.progress, partialTick);
		    this.third_ring.render(stack, vertexconsumer, rings.transportLight, combinedOverlay);
		}
		if(rings.progress > 18)
		{
			this.fourth_ring.y = getHeight(4, rings.emptySpace, rings.transportHeight, rings.ticks, rings.progress, partialTick);
		    this.fourth_ring.render(stack, vertexconsumer, rings.transportLight, combinedOverlay);
		}
		this.fifth_ring.y = getHeight(5, rings.emptySpace, rings.transportHeight, rings.ticks, rings.progress, partialTick);
	    if(rings.progress <= 24)
	    	this.fifth_ring.render(stack, vertexconsumer, combinedLight, combinedOverlay);
	    else
	    	this.fifth_ring.render(stack, vertexconsumer, rings.transportLight, combinedOverlay);
	}
}

 

Link to comment
Share on other sites

I know it's a value between 0 and 1, that's why I'm multiplying it by four after adding the previous position to get my preffered outcome.

As for the Mth#lerp I've essentially copied the code from that and changed it to fit me needs

Mth#lerp code:

public static float lerp(float partialTick, float progressOld, float progress)
{
	return progressOld + partialTick * (progress - progressOld);
}

my code:

ringHeight = (movingHeight + partialTicks) * 4;

I just removed the initial multiplication of partialTicks by (progress - progressOld) because it always changes by 1 pixel anyway. The movingHeight corresponds to progressOld. Then I multiplied everything by 4 to make it go higher. Math#lerp has the same effect, I just removed it from my code so that I don't have to call the method every time. No other reason really.

If it works sometimes and sometimes doesn't, I can't be using it completely incorrectly, right? Or is there perhaps something more Math#lerp does other than the lerping math?

Edited by Povstalec
Link to comment
Share on other sites

22 hours ago, Povstalec said:

If it works sometimes and sometimes doesn't, I can't be using it completely incorrectly, right? Or is there perhaps something more Math#lerp does other than the lerping math?

As long as you are using it to handle the interpolation between ticks such that the value is between the previous movement height and the next one, there should be no issue. I assume you know this, so I'll also assume the math is right.

In that case, it may have to do with what data is synced to the client or handled by the client. You could try printing out the parameters of the height method and see if there are any unusual values that appear when you pause and then unpause the game.

Link to comment
Share on other sites

Alright, so for the sake of testing, I've made another renderer which adds progress in the render method itself and that animation is perfectly smooth (although the progress obviously doesn't stop when I pause the game). That means the problem really is in syncing. I'll try printing out the height like you suggested, but in the meanwhile, do you know how I could fix this? I'm fairly sure my packets are being sent properly and I can't really think of any other place in the code where desync could happen.

Link to comment
Share on other sites

Alright, so using the other renderer for printing out the values, I got this:

Rotation: 16 P-Rotation: 17.39999 Tick: 131 P-Tick: 0.6999946
Rotation: 16 P-Rotation: 16.35999 Tick: 131 P-Tick: 0.17999458
Rotation: 18 P-Rotation: 19.319988 Tick: 132 P-Tick: 0.6599946
Rotation: 18 P-Rotation: 18.159988 Tick: 132 P-Tick: 0.07999456
Rotation: 20 P-Rotation: 21.19999 Tick: 133 P-Tick: 0.59999454
Rotation: 20 P-Rotation: 20.079988 Tick: 133 P-Tick: 0.03999448

Rotation is the progress of the block entity.
P-Rotation is progress with partial ticks (multiplied by two) added.
Tick is tick.
P-Tick is partial tick.

I can see a pattern of the partial ticks reaching value 1 before the tick ends, which means they go back down to 0 during the same tick and that causes the animation to stutter.

EDIT:

And this is how it looks after pausing the game a few times to get a smooth animation:

Rotation: 182 P-Rotation: 182.12 Tick: 185 P-Tick: 0.060000777
Rotation: 182 P-Rotation: 182.76 Tick: 185 P-Tick: 0.38000077
Rotation: 182 P-Rotation: 183.44 Tick: 185 P-Tick: 0.72000074
Rotation: 184 P-Rotation: 184.12 Tick: 186 P-Tick: 0.060000777
Rotation: 184 P-Rotation: 184.76 Tick: 186 P-Tick: 0.38000077
Rotation: 184 P-Rotation: 185.44 Tick: 186 P-Tick: 0.72000074
Rotation: 186 P-Rotation: 186.12 Tick: 187 P-Tick: 0.060000777
Rotation: 186 P-Rotation: 186.76 Tick: 187 P-Tick: 0.38000077
Rotation: 186 P-Rotation: 187.44 Tick: 187 P-Tick: 0.72000074

I'm not sure why it prints 3 values when it was only able to print 2 values per tick just a few minutes ago.

Edited by Povstalec
Link to comment
Share on other sites

29 minutes ago, Povstalec said:

I can see a pattern of the partial ticks reaching value 1 before the tick ends, which means they go back down to 0 during the same tick and that causes the animation to stutter.

So partial tick should only rise from 0 to 1. The fact that the value is decreasing on the same tick should not be possible. The fact that the value is jumping down probably means you are handling the client logic and then syncing the server logic at a later point which still has an old value loaded. In that case, I would suggest just doing the render all client side and not doing any of the rendering logic on the server, just syncing any specific key values that determine the animation state.

32 minutes ago, Povstalec said:

I'm not sure why it prints 3 values when it was only able to print 2 values per tick just a few minutes ago.

Yeah, this looks more correct. The amount of values per tick is generally in correspondence to your refresh rate. I would assume you have the game running at 60fps so there would be three partial ticks for every tick.

Link to comment
Share on other sites

Alright, I see what you mean. But there's a slight problem: I'm not doing any logic with the second renderer. I just made an arbitrary value that is raised by one at the end of each tick and gets sent to the client via a packet, nothing else.

Any other ideas?

Edited by Povstalec
Link to comment
Share on other sites

So I've come to the conclusion that Packets simply aren't sending the info fast enough and removed the use of packets altogether. Instead I'm just doing the same thing on Client as I am on Server, which does result in a smooth animation, but I can't really control the animation, since most of the stuff the animation depends on is server only.

Either way, thanks a lot for your help.

Edited by Povstalec
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.