当前位置:   article > 正文

我的世界 1.20.1 forge 模组开发——粒子_forge 1.20.1开发

forge 1.20.1开发

第一个粒子

最近有了些想法,于是乎,重新开始了模组开发的学习。
本文主要用于记录开发过程的问题,留下学习的记录。
参考forgegemwire1.20.1
参考boson1.16.5
与以上不同的是,这里将更大篇幅的记录粒子开发
而不是浅尝辄止,并且会解释的更加详细
在实操了1.16.5和1.20.1的粒子系统后
我发现粒子系统的改变并不大
1.16.5和1.20.1之间几乎只有一些名称的变化
下面的开发版本为1.20.1

生成粒子的方式

  1. 关于两端(客户端/服务端)
    首先你要知道粒子只在客户端显示
    但这并不意味着服务器与粒子的生成毫无关系
    如果你要在多人服务器中实现一些复杂的自定义时,有些数据只能从服务器获得
    比如伤害事件,只在服务端触发,这就需要数据同步或者在服务端使用SendParticles

  2. 生成粒子的方法
    服务端server
    serverLevel.SendParticles
    客户端client
    clientLevel.addParticle
    Minecraft.getInstance().particleEngine.add(particle);
    在后面会再详细的说明这两种方法的区别

  3. 关于逻辑刻和渲染刻
    逻辑刻在mc中是每秒20tick,若电脑性能较差也会有所降低
    但渲染刻则根据电脑性能不同有所区别,一般都会高于逻辑刻的频率
    要注意你影响粒子生成的是哪种刻,下面会有一个例子

TestParticle–粒子类

这里我们继承的是原版中的DustColorTransitionParticle类

  1. 为什么要继承这个类?

    并没有什么特别的,只要继承了TextureSheetParticle(1.20.1)/SpriteTexturedParticle(1.16.5)类即可使用自定义材质
    你也可以直接继承TextureSheetParticle
    主要的区别是DustColorTransitionParticle已经设置了粒子的最大生命、运动轨迹等等
    我们直接继承灰尘的表现形式

  2. 粒子需不需要注册?

    粒子需要注册,但不是像Item或者Block一样去注册Particle
    而是注册ParticleType,或者你可以把ParticleType理解成Item、Block
    一个粒子类型还需要注册一个Provider(1.20.1)/Factory(1.16.5)(这两者并没有什么区别)
    Provider类一般直接写在你的Particle类里做内部类

public class TestParticle extends DustColorTransitionParticle {
	//这两个成员变量来至父类,可以实现颜色的转变
    private final Vector3f fromColor;
    private final Vector3f toColor;

    protected TestParticle(ClientLevel p_172053_, double p_172054_, double p_172055_, double p_172056_, double p_172057_, double p_172058_, double p_172059_, TestOption p_172060_, SpriteSet p_172061_) {
        super(p_172053_, p_172054_, p_172055_, p_172056_, p_172057_, p_172058_, p_172059_, p_172060_, p_172061_);
        float f = this.random.nextFloat() * 0.4F + 0.6F;
        this.fromColor = this.randomizeColor(p_172060_.getFromColor(), f);
        this.toColor = this.randomizeColor(p_172060_.getToColor(), f);
    }
    //根据TestOptition的xyz的速度更改颜色
    private Vector3f randomizeColor(Vector3f p_254318_, float p_254472_) {
        return new Vector3f(this.randomizeColor(p_254318_.x(), p_254472_), this.randomizeColor(p_254318_.y(), p_254472_), this.randomizeColor(p_254318_.z(), p_254472_));
    }
	//根据粒子age、TestOption速度获取颜色
    private void lerpColors(float p_172070_) {
        float f = ((float)this.age + p_172070_) / ((float)this.lifetime + 1.0F);
        Vector3f vector3f = (new Vector3f((Vector3fc)this.fromColor)).lerp(this.toColor, f);
        this.rCol = vector3f.x();
        this.gCol = vector3f.y();
        this.bCol = vector3f.z();
    }
	
    public void render(VertexConsumer p_172063_, Camera p_172064_, float p_172065_) {
        this.lerpColors(p_172065_);
        super.render(p_172063_, p_172064_, p_172065_);
    }
	//Provider
    @OnlyIn(Dist.CLIENT)
    public static class Provider implements ParticleProvider<TestOption> {
    	//SpriteSet存储着你粒子的材质,在注册Provider的时候会一起指定它
    	//new所有的原版粒子类都需要自己指定SpriteSet,除了BreakingParticle(1.20.1)
    	//它会获取你指定的物体的材质,实现破碎效果,所以使用起来很方便
    	//如过你要使用原版的粒子,就要自己指定SpriteSet
    	//可以使用UnitTextureAtlasSprite类下的INSTANCE去new 一个 SpriteSet
    	//但这是一个纹理集,包含所有的粒子纹理,需要使用uv坐标裁剪,较为麻烦
        private final SpriteSet sprites;
        public Provider(SpriteSet p_172073_) {
            this.sprites = p_172073_;
        }
        public Particle createParticle(TestOption p_172075_, ClientLevel p_172076_, double p_172077_, double p_172078_, double p_172079_, double p_172080_, double p_172081_, double p_172082_) {
            TestParticle testParticle =  new TestParticle(p_172076_, p_172077_, p_172078_, p_172079_, p_172080_, p_172081_, p_172082_, p_172075_, this.sprites);
            //这里的pickSprite加载纹理。
            //查看一下pickSpirte方法,它实现了随机选取sprites内的材质
            testParticle.pickSprite(sprites);
            return testParticle;
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

TestOption–实现接口ParticleOptions存放和处理这些创建粒子效果的数据

直接继承DustColorTransitionOptions即可
使用addParticle会使用到TestOption,在这里你可以处理传入的参数,实现各种特效

public class TestOption extends DustColorTransitionOptions {
    private final Vector3f toColor;

    public TestOption(Vector3f color, Vector3f toColor, float scale) {
        super(color, toColor, scale);
        this.toColor = toColor;
    }

    public Vector3f getToColor() {
        return this.toColor;
    }
    //解析/particle命令行的参数,这里必须复写,否则你的粒子无法通过指令生成
    //并且这里只实现了全参数的命令,你必须完整输入全部参数才可以生成粒子
    //像这样/particle test 0 0 0 75 75 75 10
    public static final Deserializer<TestOption> DESERIALIZER = new Deserializer<TestOption>() {
        @Override
        public TestOption fromCommand(ParticleType<TestOption> p_123733_, StringReader reader) throws CommandSyntaxException {
        	//这里的每一个expect(' ')都将吃掉一个命令行的空格
            reader.expect(' ');
            //简单易懂,数据作为float读入
            float speedX = reader.readFloat();
            reader.expect(' ');
            float speedY = reader.readFloat();
            reader.expect(' ');
            float speedZ = reader.readFloat();
            reader.expect(' ');
            float red = reader.readInt( );
            reader.expect(' ');
            float green = reader.readInt();
            reader.expect(' ');
            float blue = reader.readInt();
            reader.expect(' ');
            float diameter = reader.readFloat();
            return new TestOption(new Vector3f(speedX, speedY, speedZ), new Vector3f(red, green, blue), diameter);
        }
        @Override
        public TestOption fromNetwork(ParticleType<TestOption> p_123735_, FriendlyByteBuf buffer) {
            float speedX = buffer.readFloat();
            float speedY = buffer.readFloat();
            float speedZ = buffer.readFloat();
            int red = buffer.readInt();
            int green = buffer.readInt();
            int blue = buffer.readInt();
            float diameter = buffer.readFloat();
            return new TestOption(new Vector3f(speedX, speedY, speedZ), new Vector3f(red, green, blue), diameter);
        }
    };
    
    @Override
    public ParticleType getType() {
    	//这里是你注册的ParticleType
        return modId.TEST.get();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

TestType–粒子类型

public class TestType extends ParticleType implements ParticleOptions{

	//第一个参数是用来控制粒子在看不到的时候会不会渲染。第二个参数就是命令行解析器。
    public TestType(boolean visAble ){
        super(visAble, TestOption.DESERIALIZER);
    }

    @Override
    public Codec codec() {
        return Codec.unit(this::getType);
    }
    
    @Override
    public ParticleType getType() {
        return this;
    }

	//把粒子的一些信息写入buffer
	//这样可以传输粒子的速度、大小、颜色等信息,以此来复现一个粒子,或者别的操作
	//这里我们不使用
    @Override
    public void writeToNetwork(FriendlyByteBuf p_123732_) {
    }
    
    @Override
    public String writeToString() {
        return BuiltInRegistries.PARTICLE_TYPE.getKey(this).toString();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

ParticleRegistry–注册粒子

使用forge提供的DeferredRegister
注意,这里需要在主类构造方法里,将这个注册器注册到mod总线中

public class ParticleRegistry {
    	public static final DeferredRegister<ParticleType<?>> PARTICLES = DeferredRegister.create(ForgeRegistries.PARTICLE_TYPES, MODID);
	public static final RegistryObject<ParticleType<TestOption>> TEST= PARTICLES.register("test", () -> new TestType(false));
}
  • 1
  • 2
  • 3
  • 4
ParticleRegistry.PARTICLE_TYPES.register(FMLJavaModLoadingContext.get().getModEventBus());
  • 1

ParticleFactoryRegistry–注册工厂类

@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
public class RegisterProvider {
    @SubscribeEvent
    public static void onParticleFactoryRegistration(RegisterParticleProvidersEvent event) {
    	  //为ParticleType注册ParticleProvider。
    	  //粒子的json文件定义了一个纹理列表
		  //粒子json的文件名必须与粒子的注册名相同
		  //否则加载粒子json时将出现缺少纹理列表的错误
          event.registerSpriteSet(L2Artifacts.TEST.get(), TestParticle.Provider::new);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

texture–纹理

接下来我们需要创建为我们粒子效果指定材质
顺便加入一个动态效果用mcmeta来实现,图片太长,放在了最下面

├── META-INF
│   └── mods.toml
├── assets
│   └── modid
│       ├── particles
│       │   └── test.json
│       └── textures
│           └── particle
│               └── hot.png
│               └── hot.png.mcmeta
├── data
└── pack.mcmeta
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

像如上的目录创建particles文件夹,和textures/particle文件夹。
然后在particles文件里创建和你注册名相同的json文件,里面内容如下:

{
  "textures": [
    "modid:hot",
    "modid:hot1"
  ]
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这里我们指定里粒子效果的材质。
多个材质可以随机显示
然后在textures/particle下添加我们的材质。
再在hot.png.mcmeta中用记事本打开输入下面的代码即可实现动态效果

{
  "animation": {
  }
}
  • 1
  • 2
  • 3
  • 4

启动游戏,输入如下命令,就可以生成我们的粒子效果了。

particle modid:test 0 0 0 75 75 75 1 3
  • 1

也许你注意到你的粒子在闪烁,这是因为继承的是DustColorTransitionParticle
如果你不想要这种效果,请换别的类型继承
在这里插入图片描述

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/199290
推荐阅读
相关标签
  

闽ICP备14008679号