优化火炮弹道计算

This commit is contained in:
Atsuishio 2025-06-28 13:57:44 +08:00 committed by Light_Quanta
parent 146c014fc7
commit acbbc1bff4
No known key found for this signature in database
GPG key ID: 11A39A1B8C890959
4 changed files with 139 additions and 82 deletions

View file

@ -16,9 +16,14 @@ import com.atsuishio.superbwarfare.init.ModItems;
import com.atsuishio.superbwarfare.init.ModSounds;
import com.atsuishio.superbwarfare.item.common.ammo.CannonShellItem;
import com.atsuishio.superbwarfare.network.message.receive.ShakeClientMessage;
import com.atsuishio.superbwarfare.tools.*;
import com.atsuishio.superbwarfare.tools.CustomExplosion;
import com.atsuishio.superbwarfare.tools.InventoryTool;
import com.atsuishio.superbwarfare.tools.ParticleTool;
import com.atsuishio.superbwarfare.tools.SoundTool;
import net.minecraft.ChatFormatting;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
@ -51,6 +56,8 @@ import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.*;
import software.bernie.geckolib.util.GeckoLibUtil;
import static com.atsuishio.superbwarfare.tools.RangeTool.calculateLaunchVector;
public class Mk42Entity extends VehicleEntity implements GeoEntity, CannonEntity {
public static final EntityDataAccessor<Integer> COOL_DOWN = SynchedEntityData.defineId(Mk42Entity.class, EntityDataSerializers.INT);
@ -123,15 +130,6 @@ public class Mk42Entity extends VehicleEntity implements GeoEntity, CannonEntity
public @NotNull InteractionResult interact(Player player, @NotNull InteractionHand hand) {
ItemStack stack = player.getMainHandItem();
if (player.getMainHandItem().getItem() == ModItems.FIRING_PARAMETERS.get() && player.isCrouching()) {
setTarget(player.getMainHandItem());
return InteractionResult.SUCCESS;
}
if (player.getOffhandItem().getItem() == ModItems.FIRING_PARAMETERS.get() && player.isCrouching()) {
setTarget(player.getOffhandItem());
return InteractionResult.SUCCESS;
}
if (stack.getItem() instanceof CannonShellItem) {
if (this.entityData.get(COOL_DOWN) == 0) {
var weaponType = stack.is(ModItems.AP_5_INCHES.get()) ? 0 : 1;
@ -140,12 +138,31 @@ public class Mk42Entity extends VehicleEntity implements GeoEntity, CannonEntity
}
return InteractionResult.SUCCESS;
}
if (player.getMainHandItem().getItem() == ModItems.FIRING_PARAMETERS.get()) {
if (setTarget(player.getMainHandItem())) {
player.swing(InteractionHand.MAIN_HAND);
return InteractionResult.SUCCESS;
} else {
player.displayClientMessage(Component.translatable("tips.superbwarfare.mortar.warn").withStyle(ChatFormatting.RED), true);
return InteractionResult.FAIL;
}
}
if (player.getOffhandItem().getItem() == ModItems.FIRING_PARAMETERS.get()) {
if (setTarget(player.getOffhandItem())) {
player.swing(InteractionHand.OFF_HAND);
return InteractionResult.SUCCESS;
} else {
player.displayClientMessage(Component.translatable("tips.superbwarfare.mortar.warn").withStyle(ChatFormatting.RED), true);
return InteractionResult.FAIL;
}
}
return super.interact(player, hand);
}
public void setTarget(ItemStack stack) {
public boolean setTarget(ItemStack stack) {
var parameters = stack.get(ModDataComponents.FIRING_PARAMETERS);
if (parameters == null) return;
if (parameters == null) return false;
var pos = parameters.pos();
int targetX = pos.getX();
@ -157,11 +174,19 @@ public class Mk42Entity extends VehicleEntity implements GeoEntity, CannonEntity
Vector4f worldPosition = transformPosition(transform, 0f, 2.16f, 0.5175f);
Vec3 shootPos = new Vec3(worldPosition.x, worldPosition.y, worldPosition.z);
if (!RangeTool.canReach(15, shellGravity, shootPos, new Vec3(targetX, targetY, targetZ), -14.9, 85, isDepressed))
return;
try {
Vec3 launchVector = calculateLaunchVector(shootPos, new Vec3(targetX, targetY, targetZ), 15, -shellGravity, isDepressed);
this.look(new Vec3(targetX, targetY, targetZ));
float angle = (float) -getXRotFromVector(launchVector);
if (angle < -85 || angle > 14.9) {
return false;
}
entityData.set(PITCH, angle);
} catch (Exception e) {
return false;
}
this.look(new Vec3(targetX, targetY, targetZ));
entityData.set(PITCH, (float) -RangeTool.calculateAngle(15, shellGravity, shootPos, new Vec3(targetX, targetY, targetZ), isDepressed));
return true;
}
private void look(Vec3 pTarget) {

View file

@ -16,9 +16,14 @@ import com.atsuishio.superbwarfare.init.ModItems;
import com.atsuishio.superbwarfare.init.ModSounds;
import com.atsuishio.superbwarfare.item.common.ammo.CannonShellItem;
import com.atsuishio.superbwarfare.network.message.receive.ShakeClientMessage;
import com.atsuishio.superbwarfare.tools.*;
import com.atsuishio.superbwarfare.tools.CustomExplosion;
import com.atsuishio.superbwarfare.tools.InventoryTool;
import com.atsuishio.superbwarfare.tools.ParticleTool;
import com.atsuishio.superbwarfare.tools.SoundTool;
import net.minecraft.ChatFormatting;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
@ -51,6 +56,8 @@ import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.*;
import software.bernie.geckolib.util.GeckoLibUtil;
import static com.atsuishio.superbwarfare.tools.RangeTool.calculateLaunchVector;
public class Mle1934Entity extends VehicleEntity implements GeoEntity, CannonEntity {
public static final EntityDataAccessor<Integer> COOL_DOWN = SynchedEntityData.defineId(Mle1934Entity.class, EntityDataSerializers.INT);
@ -126,15 +133,6 @@ public class Mle1934Entity extends VehicleEntity implements GeoEntity, CannonEnt
public @NotNull InteractionResult interact(Player player, @NotNull InteractionHand hand) {
ItemStack stack = player.getMainHandItem();
if (player.getMainHandItem().getItem() == ModItems.FIRING_PARAMETERS.get() && player.isCrouching()) {
setTarget(player.getMainHandItem());
return InteractionResult.SUCCESS;
}
if (player.getOffhandItem().getItem() == ModItems.FIRING_PARAMETERS.get() && player.isCrouching()) {
setTarget(player.getOffhandItem());
return InteractionResult.SUCCESS;
}
if (stack.getItem() instanceof CannonShellItem) {
if (this.entityData.get(COOL_DOWN) == 0) {
var weaponType = stack.is(ModItems.AP_5_INCHES.get()) ? 0 : 1;
@ -143,12 +141,31 @@ public class Mle1934Entity extends VehicleEntity implements GeoEntity, CannonEnt
}
return InteractionResult.SUCCESS;
}
if (player.getMainHandItem().getItem() == ModItems.FIRING_PARAMETERS.get()) {
if (setTarget(player.getMainHandItem())) {
player.swing(InteractionHand.MAIN_HAND);
return InteractionResult.SUCCESS;
} else {
player.displayClientMessage(Component.translatable("tips.superbwarfare.mortar.warn").withStyle(ChatFormatting.RED), true);
return InteractionResult.FAIL;
}
}
if (player.getOffhandItem().getItem() == ModItems.FIRING_PARAMETERS.get()) {
if (setTarget(player.getOffhandItem())) {
player.swing(InteractionHand.OFF_HAND);
return InteractionResult.SUCCESS;
} else {
player.displayClientMessage(Component.translatable("tips.superbwarfare.mortar.warn").withStyle(ChatFormatting.RED), true);
return InteractionResult.FAIL;
}
}
return super.interact(player, hand);
}
public void setTarget(ItemStack stack) {
public boolean setTarget(ItemStack stack) {
var parameters = stack.get(ModDataComponents.FIRING_PARAMETERS);
if (parameters == null) return;
if (parameters == null) return false;
var pos = parameters.pos();
int targetX = pos.getX();
@ -161,11 +178,19 @@ public class Mle1934Entity extends VehicleEntity implements GeoEntity, CannonEnt
Vector4f worldPosition = transformPosition(transform, 0, 1.4992625f, 1.52065f);
Vec3 shootPos = new Vec3(worldPosition.x, worldPosition.y, worldPosition.z);
if (!RangeTool.canReach(15, shellGravity, shootPos, new Vec3(targetX, targetY, targetZ), -2.7, 30, isDepressed))
return;
try {
Vec3 launchVector = calculateLaunchVector(shootPos, new Vec3(targetX, targetY, targetZ), 15, -shellGravity, isDepressed);
this.look(new Vec3(targetX, targetY, targetZ));
float angle = (float) -getXRotFromVector(launchVector);
if (angle < -30 || angle > 2.7) {
return false;
}
entityData.set(PITCH, angle);
} catch (Exception e) {
return false;
}
this.look(new Vec3(targetX, targetY, targetZ));
entityData.set(PITCH, (float) -RangeTool.calculateAngle(15, shellGravity, shootPos, new Vec3(targetX, targetY, targetZ), isDepressed));
return true;
}
private void look(Vec3 pTarget) {

View file

@ -8,7 +8,6 @@ import com.atsuishio.superbwarfare.init.ModEntities;
import com.atsuishio.superbwarfare.init.ModItems;
import com.atsuishio.superbwarfare.init.ModSounds;
import com.atsuishio.superbwarfare.item.common.ammo.MortarShell;
import com.atsuishio.superbwarfare.tools.RangeTool;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.arguments.EntityAnchorArgument;
import net.minecraft.core.particles.ParticleTypes;
@ -37,6 +36,8 @@ import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.*;
import software.bernie.geckolib.util.GeckoLibUtil;
import static com.atsuishio.superbwarfare.tools.RangeTool.calculateLaunchVector;
public class MortarEntity extends VehicleEntity implements GeoEntity {
public static final EntityDataAccessor<Integer> FIRE_TIME = SynchedEntityData.defineId(MortarEntity.class, EntityDataSerializers.INT);
@ -110,8 +111,8 @@ public class MortarEntity extends VehicleEntity implements GeoEntity {
Level level = this.level();
if (level instanceof ServerLevel server) {
MortarShellEntity entityToSpawn = shell.createShell(player, level, stack);
entityToSpawn.setPos(this.getX(), this.getY() + this.getEyeY(), this.getZ());
entityToSpawn.shoot(this.getLookAngle().x, this.getLookAngle().y, this.getLookAngle().z, 11.4f, 0.1f);
entityToSpawn.setPos(this.getX(), this.getEyeY(), this.getZ());
entityToSpawn.shoot(this.getLookAngle().x, this.getLookAngle().y, this.getLookAngle().z, 11.4f, (float) 0.5);
level.addFreshEntity(entityToSpawn);
server.sendParticles(ParticleTypes.CAMPFIRE_COSY_SMOKE, (this.getX() + 3 * this.getLookAngle().x), (this.getY() + 0.1 + 3 * this.getLookAngle().y), (this.getZ() + 3 * this.getLookAngle().z), 8, 0.4, 0.4, 0.4,
0.007);
@ -161,19 +162,18 @@ public class MortarEntity extends VehicleEntity implements GeoEntity {
double targetZ = pos.getZ();
var isDepressed = parameters.isDepressed();
if (!RangeTool.canReach(11.4, 0.146, this.getEyePosition(), new Vec3(targetX, targetY, targetZ), 20, 89, isDepressed)) {
try {
Vec3 launchVector = calculateLaunchVector(getEyePosition(), new Vec3(targetX, targetY, targetZ), 11.4, -0.146, isDepressed);
this.look(new Vec3(targetX, targetY, targetZ));
float angle = (float) -getXRotFromVector(launchVector);
if (angle < -89 || angle > -20) {
return false;
}
entityData.set(PITCH, angle);
} catch (Exception e) {
return false;
}
this.look(new Vec3(targetX, targetY, targetZ));
entityData.set(PITCH, (float) -RangeTool.calculateAngle(
11.4, 0.146,
this.getEyePosition(),
new Vec3(targetX, targetY, targetZ),
parameters.isDepressed()
));
return true;
}

View file

@ -3,6 +3,10 @@ package com.atsuishio.superbwarfare.tools;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class RangeTool {
/**
@ -19,48 +23,51 @@ public class RangeTool {
// 谢谢DeepSeek
/**
* 判断按指定参数发射是否可以击中目标
*
* @param v 初始速度
* @param g 重力加速度
* @param startPos 起始位置
* @param endPos 目标位置
* @param minAngle 最小仰角
* @param maxAngle 最大仰角
* @param isDepressedTrajectory 是否使用低伸弹道
*/
public static boolean canReach(double v, double g, Vec3 startPos, Vec3 endPos, double minAngle, double maxAngle, boolean isDepressedTrajectory) {
if (getD(v, g, startPos, endPos) < 0) return false;
public static Vec3 calculateLaunchVector(Vec3 pos, Vec3 pos2, double velocity, double g, boolean isDepressed) {
double dx = pos2.x - pos.x;
double dy = pos2.y - pos.y;
double dz = pos2.z - pos.z;
double horizontalDistSq = dx * dx + dz * dz;
var targetAngle = calculateAngle(v, g, startPos, endPos, isDepressedTrajectory);
return targetAngle >= minAngle && targetAngle <= maxAngle;
double a = 0.25 * g * g;
double b = -velocity * velocity - g * dy;
double c = horizontalDistSq + dy * dy;
List<Double> validT = getDoubles(b, a, c);
double t;
if (isDepressed) {
t = Collections.min(validT);
} else {
t = Collections.max(validT);
}
double vx = dx / t;
double vz = dz / t;
double vy = (dy - 0.5 * g * t * t) / t;
return new Vec3(vx, vy, vz);
}
/**
* 计算按指定参数发射所需的仰角
*
* @param v 初始速度
* @param g 重力加速度
* @param startPos 起始位置
* @param endPos 目标位置
* @param isDepressedTrajectory 是否使用低伸弹道
*/
public static double calculateAngle(double v, double g, Vec3 startPos, Vec3 endPos, boolean isDepressedTrajectory) {
var xDiff = startPos.x - endPos.x;
var zDiff = startPos.z - endPos.z;
var x = Math.sqrt(Math.pow(xDiff, 2) + Math.pow(zDiff, 2));
double d = getD(v, g, startPos, endPos);
return Math.atan((v * v + (isDepressedTrajectory ? -d : d)) / (g * x)) * Mth.RAD_TO_DEG;
}
private static List<Double> getDoubles(double b, double a, double c) {
double discriminant = b * b - 4 * a * c;
if (discriminant < 0) {
throw new IllegalStateException("No valid trajectory: Increase velocity or adjust target");
}
private static double getD(double v, double g, Vec3 startPos, Vec3 endPos) {
var xDiff = startPos.x - endPos.x;
var zDiff = startPos.z - endPos.z;
var x = Math.sqrt(Math.pow(xDiff, 2) + Math.pow(zDiff, 2));
var y = startPos.y - endPos.y;
double sqrtDisc = Math.sqrt(discriminant);
double u1 = (-b + sqrtDisc) / (2 * a);
double u2 = (-b - sqrtDisc) / (2 * a);
return Math.sqrt(Math.pow(v, 4) - g * g * x * x - 2 * g * y * v * v);
List<Double> validT = new ArrayList<>();
if (u1 > 0) validT.add(Math.sqrt(u1));
if (u2 > 0) validT.add(Math.sqrt(u2));
if (validT.isEmpty()) {
throw new IllegalStateException("No positive real solution for flight time");
}
return validT;
}
}