尝试抄tacz的弹孔,但是不显示(恼

This commit is contained in:
Atsuishio 2025-06-28 02:30:48 +08:00 committed by Light_Quanta
parent acbbc1bff4
commit 68890a3b40
No known key found for this signature in database
GPG key ID: 11A39A1B8C890959
9 changed files with 292 additions and 64 deletions

View file

@ -0,0 +1,75 @@
package com.atsuishio.superbwarfare.client.particle;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.tacz.guns.init.ModParticles;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleType;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.registries.ForgeRegistries;
public class BulletDecalOption implements ParticleOptions {
public static final Codec<BulletDecalOption> CODEC = RecordCodecBuilder.create(builder ->
builder.group(Codec.INT.fieldOf("dir").forGetter(option -> option.direction.ordinal()),
Codec.LONG.fieldOf("pos").forGetter(option -> option.pos.asLong())
).apply(builder, BulletDecalOption::new));
@SuppressWarnings("deprecation")
public static final ParticleOptions.Deserializer<BulletDecalOption> DESERIALIZER = new ParticleOptions.Deserializer<>() {
@Override
public BulletDecalOption fromCommand(ParticleType<BulletDecalOption> particleType, StringReader reader) throws CommandSyntaxException {
reader.expect(' ');
int dir = reader.readInt();
reader.expect(' ');
long pos = reader.readLong();
return new BulletDecalOption(dir, pos);
}
@Override
public BulletDecalOption fromNetwork(ParticleType<BulletDecalOption> particleType, FriendlyByteBuf buffer) {
return new BulletDecalOption(buffer.readVarInt(), buffer.readLong());
}
};
private final Direction direction;
private final BlockPos pos;
public BulletDecalOption(int dir, long pos) {
this.direction = Direction.values()[dir];
this.pos = BlockPos.of(pos);
}
public BulletDecalOption(Direction dir, BlockPos pos) {
this.direction = dir;
this.pos = pos;
}
public Direction getDirection() {
return this.direction;
}
public BlockPos getPos() {
return this.pos;
}
@Override
public ParticleType<?> getType() {
return ModParticles.BULLET_HOLE.get();
}
@Override
public void writeToNetwork(FriendlyByteBuf buffer) {
buffer.writeEnum(this.direction);
buffer.writeBlockPos(this.pos);
}
@Override
public String writeToString() {
return ForgeRegistries.PARTICLE_TYPES.getKey(this.getType()) + " " + this.direction.getName();
}
}

View file

@ -0,0 +1,170 @@
package com.atsuishio.superbwarfare.client.particle;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.particle.ParticleProvider;
import net.minecraft.client.particle.ParticleRenderType;
import net.minecraft.client.particle.TextureSheetParticle;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.Mth;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
import org.joml.Quaternionf;
import org.joml.Vector3f;
/**
* Author: Forked from MrCrayfish, continued by Timeless devs
*/
public class BulletDecalParticle extends TextureSheetParticle {
private final Direction direction;
private final BlockPos pos;
private int uOffset;
private int vOffset;
private float textureDensity;
public BulletDecalParticle(ClientLevel world, double x, double y, double z, Direction direction, BlockPos pos) {
super(world, x, y, z);
this.setSprite(this.getSprite(pos));
this.direction = direction;
this.pos = pos;
this.lifetime = 200;
this.hasPhysics = false;
this.gravity = 0.0F;
this.quadSize = 0.05F;
// 如果方块是空气则立即移除粒子
if (world.getBlockState(pos).isAir()) {
this.remove();
}
this.rCol = 0;
this.gCol = 0;
this.bCol = 0;
this.alpha = 0.9F;
}
@Override
protected void setSprite(@NotNull TextureAtlasSprite sprite) {
super.setSprite(sprite);
this.uOffset = this.random.nextInt(16);
this.vOffset = this.random.nextInt(16);
// 材质应该都是方形
this.textureDensity = (sprite.getU1() - sprite.getU0()) / 16.0F;
}
private TextureAtlasSprite getSprite(BlockPos pos) {
Minecraft minecraft = Minecraft.getInstance();
Level world = minecraft.level;
if (world != null) {
BlockState state = world.getBlockState(pos);
return Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getTexture(state, world, pos);
}
return Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(MissingTextureAtlasSprite.getLocation());
}
@Override
protected float getU0() {
return this.sprite.getU0() + this.uOffset * this.textureDensity;
}
@Override
protected float getV0() {
return this.sprite.getV0() + this.vOffset * this.textureDensity;
}
@Override
protected float getU1() {
return this.getU0() + this.textureDensity;
}
@Override
protected float getV1() {
return this.getV0() + this.textureDensity;
}
@Override
public void tick() {
super.tick();
if (this.level.getBlockState(this.pos).isAir()) {
this.remove();
}
}
@Override
public void render(@NotNull VertexConsumer buffer, Camera renderInfo, float partialTicks) {
Vec3 view = renderInfo.getPosition();
float particleX = (float) (Mth.lerp(partialTicks, this.xo, this.x) - view.x());
float particleY = (float) (Mth.lerp(partialTicks, this.yo, this.y) - view.y());
float particleZ = (float) (Mth.lerp(partialTicks, this.zo, this.z) - view.z());
Quaternionf quaternion = this.direction.getRotation();
Vector3f[] points = new Vector3f[]{
// Y 值稍微大一点点防止 z-fight
new Vector3f(-1.0F, 0.01F, -1.0F),
new Vector3f(-1.0F, 0.01F, 1.0F),
new Vector3f(1.0F, 0.01F, 1.0F),
new Vector3f(1.0F, 0.01F, -1.0F)
};
float scale = this.getQuadSize(partialTicks);
for (int i = 0; i < 4; ++i) {
Vector3f vector3f = points[i];
vector3f.rotate(quaternion);
vector3f.mul(scale);
vector3f.add(particleX, particleY, particleZ);
}
// UV 坐标
float u0 = this.getU0();
float u1 = this.getU1();
float v0 = this.getV0();
float v1 = this.getV1();
// 0 - 30 tick 15 亮度到 0 亮度
int light = Math.max(15 - this.age / 2, 0);
int lightColor = LightTexture.pack(light, light);
// 颜色逐渐渐变到 0 0 0也就是黑色
float colorPercent = light / 15.0f;
float red = this.rCol * colorPercent;
float green = this.gCol * colorPercent;
float blue = this.bCol * colorPercent;
// 透明度逐渐变成 0也就是透明
double threshold = 0.98 * this.lifetime;
float fade = 1.0f - (float) (Math.max(this.age - threshold, 0) / (this.lifetime - threshold));
float alphaFade = this.alpha * fade;
buffer.addVertex(points[0].x(), points[0].y(), points[0].z()).setUv(u1, v1).setColor(red, green, blue, alphaFade).setLight(lightColor);
buffer.addVertex(points[1].x(), points[1].y(), points[1].z()).setUv(u1, v0).setColor(red, green, blue, alphaFade).setLight(lightColor);
buffer.addVertex(points[2].x(), points[2].y(), points[2].z()).setUv(u0, v0).setColor(red, green, blue, alphaFade).setLight(lightColor);
buffer.addVertex(points[3].x(), points[3].y(), points[3].z()).setUv(u0, v1).setColor(red, green, blue, alphaFade).setLight(lightColor);
}
@Override
public @NotNull ParticleRenderType getRenderType() {
return ParticleRenderType.TERRAIN_SHEET;
}
@OnlyIn(Dist.CLIENT)
public static class Provider implements ParticleProvider<BulletDecalOption> {
public Provider() {
}
@Override
public BulletDecalParticle createParticle(@NotNull BulletDecalOption option, @NotNull ClientLevel world, double x, double y, double z, double pXSpeed, double pYSpeed, double pZSpeed) {
return new BulletDecalParticle(world, x, y, z, option.getDirection(), option.getPos());
}
}
}

View file

@ -1,49 +0,0 @@
package com.atsuishio.superbwarfare.client.particle;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.particle.*;
import net.minecraft.core.particles.SimpleParticleType;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
@OnlyIn(Dist.CLIENT)
public class BulletHoleParticle extends TextureSheetParticle {
public static BulletholeParticleProvider provider(SpriteSet spriteSet) {
return new BulletholeParticleProvider(spriteSet);
}
public static class BulletholeParticleProvider implements ParticleProvider<SimpleParticleType> {
private final SpriteSet spriteSet;
public BulletholeParticleProvider(SpriteSet spriteSet) {
this.spriteSet = spriteSet;
}
public Particle createParticle(@NotNull SimpleParticleType typeIn, @NotNull ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {
return new BulletHoleParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, this.spriteSet);
}
}
protected BulletHoleParticle(ClientLevel world, double x, double y, double z, double vx, double vy, double vz, SpriteSet spriteSet) {
super(world, x, y, z);
this.setSize(0f, 0f);
this.lifetime = 100;
this.gravity = 0f;
this.hasPhysics = false;
this.xd = vx * 0;
this.yd = vy * 0;
this.zd = vz * 0;
this.pickSprite(spriteSet);
}
@Override
public @NotNull ParticleRenderType getRenderType() {
return ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT;
}
@Override
public void tick() {
super.tick();
}
}

View file

@ -2,6 +2,7 @@ package com.atsuishio.superbwarfare.entity.projectile;
import com.atsuishio.superbwarfare.block.BarbedWireBlock;
import com.atsuishio.superbwarfare.component.ModDataComponents;
import com.atsuishio.superbwarfare.client.particle.BulletDecalOption;
import com.atsuishio.superbwarfare.config.server.ProjectileConfig;
import com.atsuishio.superbwarfare.entity.DPSGeneratorEntity;
import com.atsuishio.superbwarfare.entity.OBBEntity;
@ -18,6 +19,7 @@ import com.mojang.datafixers.util.Pair;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.particles.BlockParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.RegistryFriendlyByteBuf;
@ -413,7 +415,7 @@ public class ProjectileEntity extends Projectile implements IEntityWithComplexSp
recordHitScore(rings, dis);
}
this.onHitBlock(hitVec);
this.onHitBlock(hitVec, blockHitResult);
if (heLevel > 0) {
explosionBullet(this, this.damage, heLevel, monsterMultiplier + 1, hitVec);
}
@ -492,13 +494,27 @@ public class ProjectileEntity extends Projectile implements IEntityWithComplexSp
}
}
protected void onHitBlock(Vec3 location) {
protected void onHitBlock(Vec3 location, BlockHitResult result) {
if (this.level() instanceof ServerLevel serverLevel) {
BlockPos pos = result.getBlockPos();
Direction face = result.getDirection();
BlockState state = level().getBlockState(pos);
BlockParticleOption particleData = new BlockParticleOption(ParticleTypes.BLOCK, state);
double speed = 0.05;
double vx = face.getStepX() * speed;
double vy = face.getStepY() * speed;
double vz = face.getStepZ() * speed;
if (this.beast) {
ParticleTool.sendParticle(serverLevel, ParticleTypes.END_ROD, location.x, location.y, location.z, 15, 0.1, 0.1, 0.1, 0.05, true);
} else {
ParticleTool.sendParticle(serverLevel, ModParticleTypes.BULLET_HOLE.get(), location.x, location.y, location.z, 1, 0, 0, 0, 0, true);
ParticleTool.sendParticle(serverLevel, ParticleTypes.SMOKE, location.x, location.y, location.z, 3, 0, 0.1, 0, 0.01, true);
BulletDecalOption bulletDecalOption = new BulletDecalOption(result.getDirection(), result.getBlockPos());
serverLevel.sendParticles(bulletDecalOption, location.x, location.y, location.z, 1, 0, 0, 0, 0);
ParticleTool.sendParticle(serverLevel, ParticleTypes.SMOKE, location.x, location.y, location.z, 3, vx, vy, vz, 0.01, true);
ParticleTool.sendParticle(serverLevel, particleData, location.x, location.y, location.z, 5, vx, vy, vz, 0.1, true);
this.discard();
}
serverLevel.playSound(null, new BlockPos((int) location.x, (int) location.y, (int) location.z), ModSounds.LAND.get(), SoundSource.BLOCKS, 1.0F, 1.0F);

View file

@ -1,18 +1,38 @@
package com.atsuishio.superbwarfare.init;
import com.atsuishio.superbwarfare.Mod;
import com.atsuishio.superbwarfare.client.particle.BulletDecalOption;
import com.mojang.serialization.Codec;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleType;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraft.core.registries.BuiltInRegistries;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import org.jetbrains.annotations.NotNull;
public class ModParticleTypes {
public static final DeferredRegister<ParticleType<?>> REGISTRY = DeferredRegister.create(BuiltInRegistries.PARTICLE_TYPE, Mod.MODID);
public static final DeferredHolder<ParticleType<?>, SimpleParticleType> FIRE_STAR = REGISTRY.register("fire_star", () -> new SimpleParticleType(false));
public static final DeferredHolder<ParticleType<?>, SimpleParticleType> BULLET_HOLE = REGISTRY.register("bullet_hole", () -> new SimpleParticleType(false));
public static final DeferredHolder<ParticleType<?>, ModParticleType<? extends ParticleOptions>> BULLET_DECAL = REGISTRY.register("bullet_decal", () -> new ModParticleType<>(false, BulletDecalOption.DESERIALIZER, BulletDecalOption.CODEC));
public static final DeferredHolder<ParticleType<?>, SimpleParticleType> CUSTOM_CLOUD = REGISTRY.register("custom_cloud", () -> new SimpleParticleType(false));
public static final DeferredHolder<ParticleType<?>, SimpleParticleType> CUSTOM_SMOKE = REGISTRY.register("custom_smoke", () -> new SimpleParticleType(false));
@SuppressWarnings("deprecation")
private static class ModParticleType<T extends ParticleOptions> extends ParticleType<T> {
private final Codec<T> codec;
public ModParticleType(boolean overrideLimiter, ParticleOptions.Deserializer<T> deserializer, Codec<T> codec) {
super(overrideLimiter, deserializer);
this.codec = codec;
}
@Override
public @NotNull Codec<T> codec() {
return this.codec;
}
}
}

View file

@ -1,6 +1,6 @@
package com.atsuishio.superbwarfare.init;
import com.atsuishio.superbwarfare.client.particle.BulletHoleParticle;
import com.atsuishio.superbwarfare.client.particle.BulletDecalParticle;
import com.atsuishio.superbwarfare.client.particle.CustomCloudParticle;
import com.atsuishio.superbwarfare.client.particle.CustomSmokeParticle;
import com.atsuishio.superbwarfare.client.particle.FireStarParticle;
@ -14,7 +14,7 @@ public class ModParticles {
@SubscribeEvent
public static void registerParticles(RegisterParticleProvidersEvent event) {
event.registerSpriteSet(ModParticleTypes.FIRE_STAR.get(), FireStarParticle::provider);
event.registerSpriteSet(ModParticleTypes.BULLET_HOLE.get(), BulletHoleParticle::provider);
event.registerSpecial(ModParticleTypes.BULLET_DECAL.get(), new BulletDecalParticle.Provider());
event.registerSpriteSet(ModParticleTypes.CUSTOM_CLOUD.get(), CustomCloudParticle::provider);
event.registerSpriteSet(ModParticleTypes.CUSTOM_SMOKE.get(), CustomSmokeParticle::provider);
}

View file

@ -1,5 +0,0 @@
{
"textures": [
"superbwarfare:bullet_hole"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B