• Forums

Navigation

  • Home
  • Style Guide
  • Getting Started
    • Home
    • Structuring Your Mod
    • Forge Update Checker
    • Debug Profiler
  • Concepts
    • Sides
    • Resources
    • Data
    • Registries
    • Internationalization and localization
  • Blocks
    • Home
    • Blockstates
    • Interaction
  • Animation API
    • Intro to the Animation API
    • Armatures
    • Animation State Machines
    • Using the API
  • Tile Entities
    • Home
    • Renderer
  • Items
    • Home
    • Loot Modification
  • Models
    • Intro to Models
    • Model Files
    • Blockstates
      • Intro to Blockstate JSONs
      • Forge Blockstate JSON
    • Coloring Textures
    • Item Property Overrides
    • Advanced Models
      • IBakedModel
      • Perspective
      • ItemOverrideList
  • Rendering
    • ItemStackTileEntityRenderer
  • Data Generation
    • Introduction
    • Model Providers
  • Events
    • Basic Usage
  • Networking
    • Home
    • Overview
    • SimpleImpl
    • Entities
  • Data Storage
    • Capabilities
    • World Saved Data
  • Utilities
    • Recipes
    • Tags
  • Effects
    • Particles
      • Terminology
      • Different Particles
      • ParticleTypes
      • IParticleFactory
    • Sounds
  • Conventions
    • Versioning
    • Locations
    • Loading Stages
  • Advanced Topics
    • Access Transformers
  • Contributing to Forge
    • Getting Started
    • PR Guidelines
  • Legacy Versions
    • Home
    • Porting from 1.12 to 1.13/1.14

Particles

Terminology

Term Description
Particle What is responsible for the rendering logic.
ParticleType<?> What is registered using a unique name.
IParticleData What contains server data that needs to be transferred to the client.
IParticleFactory  The factory takes in an IParticleData and returns a Particle. It will be registered using a unique ParticleType<?>.

Different Particles

You will need to have your own implementation of the Particle class, which will handle the rendering of the particle. Vanilla examples will help you on how to handle rendering, this guide will not get into that and only discuss the logic part of particles.

Sidedness

The Particle class is client only, it does not exist on the server. This means that creating a fixed parameter particle (no context information needed) is somewhat simpler, more on that later. However, if the particle has a parameter with dynamic values, server information is required. For example, breaking blocks create a particle effect based on the broken block’s texture. This information obviously depends on the Blockstate of the block, which is taken from the server.

To make a particle appear, use either ClientWorld#addParticle or ServerWorld#spawnParticle. ServerWorld#addParticle will simply do nothing, without throwing any errors.

Does your particle need server data?

Taking as an example a mod which adds some spells to the game: If there’s 8 type of spells added with each one specific color, there is no need for server information. Each spell will have a distinct registered ParticleType. However, if these spells can have modifiers that can affect the color in multiple ways (add/subtract red/green/blue data) then having a registry object for each combination will be tedious. In that case, server information will be required when spawning the particle, to calculate the color to use.

ParticleTypes

Vanilla Implementation

While there are a lot of different particles in vanilla, in almost all cases vanilla uses BasicParticleType, a basic implementation of ParticleType and IParticleData. This implementation is used for anything that does not require server data. The only vanilla particles that do not use BasicParticleType are redstone dust and block/item texture dependent particles. When requiring server data, a direct implementation of IParticleData is needed.

The ParticleTypes class is very helpful to check out vanilla implementations. Seeing how vanilla implemented each particle will help better evaluate what you need.

Registering

ParticleType<?> is a ForgeRegistryEntry, so it is registered normally. A ParticleType needs to be registered for each distinct Particle. When using server information, only one ParticleType needs to be registered, since it will cover all cases. When opting for covering all discrete cases, multiple ParticleTypes are registered all using BasicParticleType. The difference will come in registering the IParticleFactory.

IParticleFactory

To tie up everything, you need to implement an IParticleFactory. One factory needs to be registered for every ParticleType you register. When covering discrete cases, you can specify a parameter in the constructor, to be able to differentiate the cases.

public static class Factory implements IParticleFactory<BasicParticleType> {
  private final Color color;
  public Factory(Color color) {
    this.color = color;
  }

  @Nullable
  @Override
  public Particle makeParticle(BasicParticleType typeIn, World worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {
    return new SHParticle(worldIn, this.color, x, y, z, xSpeed, ySpeed, zSpeed);
  }
}
In the example above, a Color can be specified during registration. The downside is the colors are “hardcoded” and can’t be changed using the /particle command. However, adding a new distinct case is easy, there only needs to be a new ParticleType and the associated factory. Finally, IParticleFactory has one method, #makeParticle, which returns a Particle. You can then return a new instance of your particle as shown above. If you are using a BasicParticleType, that parameter will be unused.

Registering

Factories need to be added to the ParticleManager. The ParticleFactoryRegisterEvent is fired when you should do so. At that point use: Minecraft.getInstance()#particles#registerFactory() to register your factory. It needs a ParticleType (from it, it will get the registry name) and an instance of your IParticleFactory. When registering discrete valued particles, the factory will contain the discrete information, like so pertaining to the previous example:

Minecraft.getInstance().particles.registerFactory(DeferredRegistration.HEART_CRYSTAL_PARTICLE.get(), new SHParticle.Factory(Color.FIREBRICK));
Minecraft.getInstance().particles.registerFactory(DeferredRegistration.POWER_CRYSTAL_PARTICLE.get(), new SHParticle.Factory(Color.ROYALBLUE));
The interest in doing this is to avoid having to deal with IParticleData and not create multiple Particle implementations for each color.

Important

IParticleFactory is client only, like the Particle class. A way of dealing with this is using a handler class and the EventBusSubscriber annotation with a dist value.

Built with MkDocs using a custom theme. Hosted by Read the Docs.
Enable Dark Theme