Skip to content

PulseLib Blocks

Ancient edited this page Apr 15, 2026 · 3 revisions

Pre-word

Blocks in Minecraft are static by default, which means they cannot be animated on their own.

PulseLib leverages the BlockEntityRenderer system to render animated blocks. Therefore, any PulseLib-animated block must have a BlockEntity.

For instructions on creating a BlockEntity in NeoForge, see NeoForge

Important

The BlockEntity itself should implement PAnimatable, not the block.

Steps to Create a PulseLib Block

Creating an animated PulseLib block involves five main steps:

Steps #1 and #2 will not be covered on this page, instead visit their respective links for info. This page will focus on steps #3, #4, and #5

The Block Class

We won’t cover basic block creation here. If you need instructions on creating a block, consult a standard block tutorial.

Once your block class exists:

  1. Implement EntityBlock (or a subclass) to mark it as a blockentity.
  2. Override newBlockEntity and return a new instance of your BlockEntity.

Disabling the Vanilla Renderer

Minecraft normally uses a blockstate file to render blocks. PulseLib renders blocks differently, so we need to disable vanilla block rendering.

Override the getRenderShape method in your block class:

    @Override
    public RenderShape getRenderShape(BlockState state) 
    {
        return RenderShape.ENTITYBLOCK_ANIMATED;
    }

This tells Minecraft to skip the default block rendering system.

The BlockEntity Class

Quick Summary
  1. Implement PAnimatable
  2. Override getAnimationManager and registerAnimationControllers
  3. Instantiate PAnimationManager via PLibHelper.createManager(this) at the top of blockentity class
  4. Add animation controllers in registerAnimationControllers

Steps to set up a PulseLib blockentity:

  1. Implement PAnimatable – base interface for all animatable objects.
  2. Create a PAnimationManager instance to store the animatable state:
private final PAnimationManager<ExampleBlockEntity> animationManager = PLibHelper.createManager(this);
  1. Override getAnimationManager() – return the cached manager.
  2. Override registerAnimationControllers() – define your animation controllers here.

Once this is done, your blockentity is ready, and you can define animations in registerAnimationControllers()

Example BlockEntity Class

public class ExampleBlockEntity extends BlockEntity implements ArcAnimatable<ExampleBlockEntity> {
	private final PAnimationManager<ExampleBlockEntity> animationManager = PLibHelper.createManager(this);
	private final PRawAnimation ANIMATION = PRawAnimation.begin().
			thenPlay("animation").
			thenWait(20).
			thenLoop("animation").
			build();

        public ExampleBlockEntity(BlockPos pos, BlockState state) {
            super(MyBlockEntities.EXAMPLE_BLOCK_ENTITY.get(), pos, state);
        }
    
        @Override
        public void registerAnimationControllers(PAnimationManager.PAnimationRegistrar<ExampleBlockEntity> registrar)
        {
		registrar.add(() -> animatableState ->
		{
			ExampleBlockEntity blockEntity = animatableState.animatable();
			PAnimationController<ExampleBlockEntity> controller = animatableState.controller();
			if (blockEntity.isPlayAnimation())
			{
				controller.play(ANIMATION);
				return ControllerState.PLAY;
			}
			else
			{
				controller.stop();
				return ControllerState.STOP;
			}
		});
        }
    
	@Override
	public PAnimationManager<ExampleBlockEntity> getAnimationManager(AnimManagerKey key)
	{
		return this.animationManager;
	}
}

The Renderer

To render your blockentity in the world:

  1. Extend PBlockRenderer instead of vanilla renderers.
  2. Pass your PModelData and render type.
  3. No need to register model layers or mesh definitions.

Example BlockEntity Renderer

    public class ExampleBlockEntityRenderer extends PBlockRenderer<ExampleBlockEntity> 
    {
        public ExampleBlockEntityRenderer(final EntityRendererProvider.Context context) 
        {
		super(new DefaultBlockModelData.DefaultBlockModelDataBuilder(ResourceLocation.fromNamespaceAndPath("example_mod", "test_block")).
				addTexture(ResourceLocation.fromNamespaceAndPath("example_mod", "tube_texture")).
				addTexture(ResourceLocation.fromNamespaceAndPath("example_mod", "torus_texture")).
				addTexture(ResourceLocation.fromNamespaceAndPath("example_mod", "pyramid_texture")).
				addTexture(ResourceLocation.fromNamespaceAndPath("example_mod", "cube_texture")).
				build(),
				PRenderTypes.RenderTypeProvider :: trianglesTranslucent);
        }
    }

Caution

PulseLib uses triangles for rendering instead of quads (vanilla). This means you cannot use vanilla RenderType. Use the provided render types or create your own. Be aware, when you creating own RenderType you have to use this vertex format

Note

PulseLib uses instanced rendering for blockentity rendering. Coz Minecraft doesn't support this type of rendering by default, PulseLib rendering all blockentities in own pipeline. It's a bit later, then vanilla pipeline. So, if you want add something custom to your model, you should use preSubmit() or postSubmit() methods. You can directly add something to MultiBufferSource to use vanilla rendering or use PRenderQueue.submit() to add something to PulseLib render pipeline.

Registering the Renderer (NeoForge)

    @SubscribeEvent
    public static void registerRenderers(final EntityRenderersEvent.RegisterRenderers event) 
    {
        event.registerBlockEntityRenderer(MyBlockEntities.EXAMPLE_BLOCK_ENTITY.get(), ExampleBlockEntityRenderer::new);
    }

This completes the setup for a fully animated PulseLib block.

Clone this wiki locally