Переопределение эффекта

Версия Minecraft
1.12.2
API
Forge
25
3
Времени всем доброго.
Вопросы на 1.12.2:
- Есть ли способ переопределить/заменить частицы эффектов зелий. При создании эффекта зелья на сущность, есть логический параметр, отвечающий за показ эффекта частиц. Там сумма цвета и прочее.
Я хочу отображать свой эффект/частицы для создаваемого кастомного(не ванильного) зелья.
В данный момент я отрубал этот параметр и использовал тиковые события сущности. Но это оказалось проблемой, для тех кто использует зелье напрямую - отображаются все частицы и свои и ванильные.

- Подскажите ресурс для создания кастомных частиц на 1.12.2. Перелопатил тонну сайтов. Чаще всего версия либо старая, либо уже новая. И почти везде используется создание частиц путём переопределением ванильного варианта. Хотелось бы создавать свои частицы со своей механикой и текстурой.
 
25
3
Сейчас получается вот такая ерунда:
1599824565865.png
Как видно - оба вида частиц.
Навсякий скину свои коды:
Java:
    @SubscribeEvent //регистрация текстуры на стороне клиента
    public void adderPreTextureLoad(TextureStitchEvent.Pre event)
    {
        TextureMap map = event.getMap();
        map.registerSprite(new ResourceLocation(Adder.MODID, "effect/particles")); 
    }
Вызов своей частицы в классе кастомного зелья вот такой:
Java:
    @Override
    public void performEffect(EntityLivingBase entity, int amplifier) { // every second
        int duration = entity.getActivePotionEffect(this).getDuration();
        if (duration % 10 == 0) {Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleStun(entity.world, entity.posX, entity.posY+2, entity.posZ, entity, duration));}
    }
Ну и сам класс частицы. Все коменты написал сам, поправьте если что не так:
Java:
package betazavr.adder.client.particle;

import java.util.Random;

import org.lwjgl.opengl.GL11;

import betazavr.adder.Adder;
import betazavr.adder.api.Metods;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;

public class ParticleStun
extends Particle {
    
    private static final ResourceLocation texture = new ResourceLocation(Adder.MODID, "textures/effect/particles.png");
    private static final VertexFormat VERTEX_FORMAT = (new VertexFormat()).addElement(DefaultVertexFormats.POSITION_3F).addElement(DefaultVertexFormats.TEX_2F).addElement(DefaultVertexFormats.COLOR_4UB).addElement(DefaultVertexFormats.TEX_2S).addElement(DefaultVertexFormats.NORMAL_3B).addElement(DefaultVertexFormats.PADDING_1B);
    protected int icon = 0;
    protected double centerX = 0.0;
    protected double centerZ = 0.0;
    protected EntityLivingBase target = null;
    protected double radius = 0.5;
    protected boolean dimension = false;
    
    /*конструктор класса №1*/
    public ParticleStun(World world, double x, double y, double z) {
        super(world, x, y, z);
        prevPosX = x;
        prevPosY = y;
        prevPosZ = z;
        particleRed = 1.0F;
        particleGreen = 1.0F;
        particleBlue = 1.0F;
        particleMaxAge = 50;
        particleAge = 0;
        particleScale = 1.0F;
        particleAngle = (float) Math.round(360.0F * Math.random());
        centerX = x;
        centerZ = z;
        icon = 0;
        radius = 0.5D;
    }
    
    /*конструктор класса №2*/
    public ParticleStun(World world, double x, double y, double z, double xS, double yS, double zS) {
        super(world, x, y, z, xS, yS, zS);
        prevPosX = x;
        prevPosY = y;
        prevPosZ = z;
        particleRed = (float) xS;
        particleGreen = (float) yS;
        particleBlue = (float) zS;
        particleMaxAge = 50;
        particleAge = 0;
        particleScale = 1.0F;
        particleAngle = (float) Math.round(360.0F * Math.random());
        centerX = x;
        centerZ = z;
        icon = 0;
        radius = 0.5D;
    }
    
    /*конструктор класса №3*/
    public ParticleStun(World world, double x, double y, double z, Entity entity, int delay) {
        super(world, x, y, z);
        prevPosX = x;
        prevPosY = y;
        prevPosZ = z;
        particleRed = 1.0F;
        particleGreen = 1.0F;
        particleBlue = 1.0F;
        if (delay < 50) {particleMaxAge = delay;} // время существования частицы ограничено
        else {particleMaxAge = 50;}
        particleAge = 0;
        particleScale = 1.0F;
        particleAngle = (float) Math.round(360.0F * Math.random()); // начальное положение на окружности (для механики движения)
        centerX = x; // положение оси X для центра окружности (для механики движения)
        centerZ = z; // положение оси Z для центра окружности (для механики движения)
        target = (EntityLivingBase) entity; // частица прикреплена к сущности
        icon = 0; // стартовый порядковый номер частицы из текстуры
        radius = 0.5D; // радиус окружности вращения (для механики движения)
    }

    @Override
    /*отображение частицы игроку на клиенте:*/
    public void renderParticle(BufferBuilder buffer, Entity entity, float partialTicks, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ) {
        Minecraft.getMinecraft().getTextureManager().bindTexture(this.texture); // подмена карты текстуры на собственную
        
        float minU = 0.0625F * (((float) this.icon) % 16.0F); // базовое имя f - начальное горизонтальное значение положения вершины из текстуры
        float maxU = minU + 0.0625f; // базовое имя f1 - конечное горизонтальное значение положения вершины из текстуры
        float minV = 0.0625F * ((float) Math.floor(this.icon / 16.0F)); // базовое имя f2 - начальное вертикальное значение положения вершины из текстуры
        float maxV = minV + 0.0625f; // базовое имя f3 - конечное горизонтальное значение положения вершины из текстуры
        float scale = 0.1F * this.particleScale; // базовое имя f4 - размер частицы в игре
        
        float f5 = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX);
        float f6 = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY);
        float f7 = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ);
        /* 
         * this.prevPos - начальное/предыдущее положение частицы
         * this.pos - текущее положение частицы
         * interpPos - интерполяция положения изображения (будущее положение частицы)
         */
         
        buffer.begin(7, VERTEX_FORMAT); // своего рода способ форматирования вершин
        
        // создание квадратной частицы:
        buffer.pos((double)(f5 - rotX * scale - rotXY * scale), (double)(f6 - rotZ * scale), (double)(f7 - rotYZ * scale - rotXZ * scale)).tex((double)maxU, (double)maxV).color(this.particleRed, this.particleGreen, this.particleBlue, 1.0F).lightmap(0, 240).normal(0.0F, 1.0F, 0.0F).endVertex(); // нижняя правая вершина
        buffer.pos((double)(f5 - rotX * scale + rotXY * scale), (double)(f6 + rotZ * scale), (double)(f7 - rotYZ * scale + rotXZ * scale)).tex((double)maxU, (double)minV).color(this.particleRed, this.particleGreen, this.particleBlue, 1.0F).lightmap(0, 240).normal(0.0F, 1.0F, 0.0F).endVertex(); // верхняя правая вершина
        buffer.pos((double)(f5 + rotX * scale + rotXY * scale), (double)(f6 + rotZ * scale), (double)(f7 + rotYZ * scale + rotXZ * scale)).tex((double)minU, (double)minV).color(this.particleRed, this.particleGreen, this.particleBlue, 1.0F).lightmap(0, 240).normal(0.0F, 1.0F, 0.0F).endVertex(); // верхняя левая вершина
        buffer.pos((double)(f5 + rotX * scale - rotXY * scale), (double)(f6 - rotZ * scale), (double)(f7 + rotYZ * scale - rotXZ * scale)).tex((double)minU, (double)maxV).color(this.particleRed, this.particleGreen, this.particleBlue, 1.0F).lightmap(0, 240).normal(0.0F, 1.0F, 0.0F).endVertex(); // нижняя левая вершина
        /*
         * pos - позиция вершины в поле координат игры
         * tex - позиция вершины из текстуры
         * color - маска RGB цвета, накладываемая поверх изображения
         * lightmap - координата из карты освещения (маска затемнения изображения)
         * normal - поверхность, образованная через треугольник, ортогонально к вектору  (что-то вроде угла обзора (0,1,0) - параллельно экрану)
         * endVertex - объявление завершения вершины
         * P.S. Для создания прозрачности необходимо добавлять методы OpenGL типа - GL11.glColor4f(1.0F, 1.0F, 1.0F, alpha);
         * где alpha - (float) степень не прозрачности от 0 до 1
         */
        Tessellator.getInstance().draw(); // отображение частицы на экран
    }

    @Override
    public void onUpdate() { // изменения каждый тик:
        // Запомнить текущее положение на будущее
        this.prevPosX = this.posX;
        this.prevPosY = this.posY;
        this.prevPosZ = this.posZ;
        // Проверка жизни частицы
        if (this.particleAge++ >= this.particleMaxAge)
        {
            this.setExpired();
        }
        // Механика частицы в игре:
        this.icon = (int) Math.floor((this.particleAge % 20) / 4); // смена иконки частицы от 0 до 4 каждые 0,25 секунды
        if (this.dimension) { // изменяет размер частицы (от 30% до 170% за секунду)
            this.particleScale += 0.07F;
            if (this.particleScale >= 1.7F) {this.dimension = false;}
        }
        else {
            this.particleScale -= 0.07F;
            if (this.particleScale <= 0.3F) {this.dimension = true;}
        }
        // движение частицы по окружности над головой сущности:
        this.particleAngle += 25.0F;
        if (this.particleAngle > 360.0F) {this.particleAngle -= 360.0F;}
        
        float imp = 0.98F; // своего рода корректировка движения частицы по окружности = 98% от нормального
        // получение импульса движения из текущего положения, до необходимого на окружности
        double[] motionsXZ = Metods.getMotionOnCircle(centerX, centerZ, this.posX, this.posZ, this.particleAngle, this.radius, imp);
        double motY = 0.0D;
        if (this.target!=null && this.posY!=this.target.posY + 2) {
            motY = this.target.posY + 2 - this.posY; // корректировка оси Y - всегда над головой
        }
        this.move(motionsXZ[0], motY, motionsXZ[1]); // переместить частицу на приращение
    }

    public int getFXLayer() {
        // текущий слой изображения, что-то вроде использования для плавного перехода от одного изображения к другому
        return 3;
    }
}
 
Последнее редактирование:
Сверху