修改爆头判定,延迟补偿

This commit is contained in:
Atsuihsio 2024-06-03 03:49:10 +08:00
parent 89f3852a42
commit 7707a24a3d
3 changed files with 75 additions and 36 deletions

View file

@ -0,0 +1,19 @@
package net.mcreator.target.api.entity;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.EntityHitResult;
/**
* 用于进行一些并非 {@link LivingEntity} 但是可被子弹击中的特殊实体的处理
*/
public interface ITargetEntity {
/**
* @param projectile 弹射物实体
* @param result 击中实体的位置
* @param source 伤害源类型
* @param damage 伤害值
*/
void onProjectileHit(Entity projectile, EntityHitResult result, DamageSource source, float damage);
}

View file

@ -1,6 +1,7 @@
package net.mcreator.target.entity; package net.mcreator.target.entity;
import net.mcreator.target.TargetMod; import net.mcreator.target.TargetMod;
import net.mcreator.target.api.entity.ITargetEntity;
import net.mcreator.target.headshot.BoundingBoxManager; import net.mcreator.target.headshot.BoundingBoxManager;
import net.mcreator.target.headshot.IHeadshotBox; import net.mcreator.target.headshot.IHeadshotBox;
import net.mcreator.target.init.TargetModDamageTypes; import net.mcreator.target.init.TargetModDamageTypes;
@ -19,6 +20,7 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundSoundPacket; import net.minecraft.network.protocol.game.ClientboundSoundPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
@ -39,6 +41,7 @@ import net.minecraftforge.entity.IEntityAdditionalSpawnData;
import net.minecraftforge.entity.PartEntity; import net.minecraftforge.entity.PartEntity;
import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.PlayMessages; import net.minecraftforge.network.PlayMessages;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
@ -147,52 +150,44 @@ public class ProjectileEntity extends Entity implements IEntityAdditionalSpawnDa
private EntityResult getHitResult(Entity entity, Vec3 startVec, Vec3 endVec) { private EntityResult getHitResult(Entity entity, Vec3 startVec, Vec3 endVec) {
double expandHeight = entity instanceof Player && !entity.isCrouching() ? 0.0625 : 0.0; double expandHeight = entity instanceof Player && !entity.isCrouching() ? 0.0625 : 0.0;
AABB boundingBox = entity.getBoundingBox(); AABB boundingBox = entity.getBoundingBox();
Vec3 velocity = new Vec3(entity.getX() - entity.xOld, entity.getY() - entity.yOld, entity.getZ() - entity.zOld);
// 延迟补偿 // hitbox 延迟补偿只有射击者是玩家且被击中者也是玩家才进行此类延迟补偿计算
if (entity instanceof ServerPlayer && this.shooter != null) { if (entity instanceof ServerPlayer player && this.shooter instanceof ServerPlayer serverPlayerOwner) {
int ping = (int) Math.floor((((ServerPlayer) this.shooter).latency / 1000.0) * 20.0 + 4.0); int ping = Mth.floor((serverPlayerOwner.latency / 1000.0) * 20.0 + 0.5);
boundingBox = BoundingBoxManager.getBoundingBox((Player) entity, ping); boundingBox = BoundingBoxManager.getBoundingBox(player, ping);
velocity = BoundingBoxManager.getVelocity(player, ping);
} }
// 应用蹲伏导致的 hitbox 变形
boundingBox = boundingBox.expandTowards(0, expandHeight, 0); boundingBox = boundingBox.expandTowards(0, expandHeight, 0);
// 根据速度一定程度地扩展 hitbox
if (this.beast) { boundingBox = boundingBox.expandTowards(velocity.x, velocity.y, velocity.z);
boundingBox = boundingBox.inflate(3); // 玩家 hitbox 修正可以通过 Config 调整
} double playerHitboxOffset = 3;
if (entity instanceof ServerPlayer) {
Vec3 hitPos = boundingBox.clip(startVec, endVec).orElse(null); if (entity.getVehicle() != null) {
Vec3 grownHitPos = boundingBox.inflate(0.35, 0.2, 0.35).clip(startVec, endVec).orElse(null); boundingBox = boundingBox.move(velocity.multiply(playerHitboxOffset / 2, playerHitboxOffset / 2, playerHitboxOffset / 2));
if (hitPos == null && grownHitPos != null) {
HitResult result = rayTraceBlocks(this.level(), new ClipContext(startVec, grownHitPos, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this), IGNORE_LEAVES);
if (result.getType() == HitResult.Type.BLOCK) {
return null;
} }
hitPos = grownHitPos; boundingBox = boundingBox.move(velocity.multiply(playerHitboxOffset, playerHitboxOffset, playerHitboxOffset));
} }
// 给所有实体统一应用的 Hitbox 偏移其数值为实验得出的定值
if (entity.getVehicle() != null || entity instanceof ITargetEntity) {
boundingBox = boundingBox.move(velocity.multiply(-2.5, -2.5, -2.5));
}
boundingBox = boundingBox.move(velocity.multiply(-5, -5, -5));
// 计算射线与实体 boundingBox 的交点
Vec3 hitPos = boundingBox.clip(startVec, endVec).orElse(null);
/* Check for headshot */ /* Check for headshot */
boolean headshot = false;
if (entity instanceof LivingEntity) {
IHeadshotBox<LivingEntity> headshotBox = (IHeadshotBox<LivingEntity>) BoundingBoxManager.getHeadshotBoxes(entity.getType());
if (headshotBox != null) {
AABB box = headshotBox.getHeadshotBox((LivingEntity) entity);
if (box != null) {
box = box.move(boundingBox.getCenter().x, boundingBox.minY, boundingBox.getCenter().z);
Optional<Vec3> headshotHitPos = box.clip(startVec, endVec);
if (headshotHitPos.isEmpty()) {
box = box.inflate(0.2, 0.2, 0.2);
headshotHitPos = box.clip(startVec, endVec);
}
if (headshotHitPos.isPresent() && (hitPos == null || headshotHitPos.get().distanceTo(hitPos) < 0.55)) {
hitPos = headshotHitPos.get();
headshot = true;
}
}
}
}
if (hitPos == null) { if (hitPos == null) {
return null; return null;
} }
Vec3 hitBoxPos = hitPos.subtract(entity.position());
boolean headshot = false;
float eyeHeight = entity.getEyeHeight();
if ((eyeHeight - 0.35) < hitBoxPos.y && hitBoxPos.y < (eyeHeight + 0.35)) {
headshot = true;
}
return new EntityResult(entity, hitPos, headshot); return new EntityResult(entity, hitPos, headshot);
} }

View file

@ -26,6 +26,12 @@ public class BoundingBoxManager {
private static final Map<EntityType<?>, IHeadshotBox<?>> headshotBoxes = new HashMap<>(); private static final Map<EntityType<?>, IHeadshotBox<?>> headshotBoxes = new HashMap<>();
private static final WeakHashMap<Player, LinkedList<AABB>> playerBoxes = new WeakHashMap<>(); private static final WeakHashMap<Player, LinkedList<AABB>> playerBoxes = new WeakHashMap<>();
private static final WeakHashMap<Player, LinkedList<Vec3>> PLAYER_POSITION = new WeakHashMap<>();
// 玩家命中箱缓存表
private static final WeakHashMap<Player, LinkedList<AABB>> PLAYER_HITBOXES = new WeakHashMap<>();
// 玩家速度缓存表
private static final WeakHashMap<Player, LinkedList<Vec3>> PLAYER_VELOCITY = new WeakHashMap<>();
static { static {
/* Player */ /* Player */
registerHeadshotBox(EntityType.PLAYER, (entity) -> { registerHeadshotBox(EntityType.PLAYER, (entity) -> {
@ -118,6 +124,16 @@ public class BoundingBoxManager {
playerBoxes.remove(event.getEntity()); playerBoxes.remove(event.getEntity());
} }
public static Vec3 getPlayerVelocity(Player entity) {
LinkedList<Vec3> positions = PLAYER_POSITION.computeIfAbsent(entity, player -> new LinkedList<>());
if (positions.size() > 1) {
Vec3 currPos = positions.getFirst();
Vec3 prevPos = positions.getLast();
return new Vec3(currPos.x - prevPos.x, currPos.y - prevPos.y, currPos.z - prevPos.z);
}
return new Vec3(0, 0, 0);
}
public static AABB getBoundingBox(Player entity, int ping) { public static AABB getBoundingBox(Player entity, int ping) {
if (playerBoxes.containsKey(entity)) { if (playerBoxes.containsKey(entity)) {
LinkedList<AABB> boxes = playerBoxes.get(entity); LinkedList<AABB> boxes = playerBoxes.get(entity);
@ -126,4 +142,13 @@ public class BoundingBoxManager {
} }
return entity.getBoundingBox(); return entity.getBoundingBox();
} }
public static Vec3 getVelocity(Player entity, int ping) {
if (PLAYER_VELOCITY.containsKey(entity)) {
LinkedList<Vec3> velocities = PLAYER_VELOCITY.get(entity);
int index = Mth.clamp(ping, 0, velocities.size() - 1);
return velocities.get(index);
}
return getPlayerVelocity(entity);
}
} }