优化火控计算

This commit is contained in:
Atsuishio 2025-06-28 18:52:44 +08:00 committed by Light_Quanta
parent b6e58c411a
commit 086b6784e8
No known key found for this signature in database
GPG key ID: 11A39A1B8C890959
2 changed files with 88 additions and 6 deletions

View file

@ -298,19 +298,22 @@ public class Hpj11Entity extends ContainerMobileVehicleEntity implements GeoEnti
this.entityData.set(TARGET_UUID, target.getVehicle().getStringUUID());
}
Vec3 targetPos = new Vec3(target.getX(), target.getY() + target.getBbHeight() / 4, target.getZ()).add(target.getDeltaMovement().scale(1.0 + 0.04 * target.distanceTo(this)));
Vec3 targetVec = barrelRootPos.vectorTo(targetPos).normalize();
Vec3 targetPos = target.getBoundingBox().getCenter();
Vec3 targetVel = target.getDeltaMovement();
Vec3 targetVec = RangeTool.calculateFiringSolution(barrelRootPos, targetPos, targetVel, 30, 0.03);
double d0 = targetVec.x;
double d1 = targetVec.y;
double d2 = targetVec.z;
double d3 = Math.sqrt(d0 * d0 + d2 * d2);
this.setXRot(Mth.clamp(Mth.wrapDegrees((float) (-(Mth.atan2(d1, d3) * 57.2957763671875))), -90, 40));
float targetY = Mth.wrapDegrees((float) (Mth.atan2(d2, d0) * 57.2957763671875) - 90.0F);
float targetY = Mth.wrapDegrees((float) (Mth.atan2(d2, d0) * 57.2957763671875) - 90.0F);
float diffY = Math.clamp(-90f, 90f, Mth.wrapDegrees(targetY - this.getYRot()));
turretTurnSound(0, diffY, 1.1f);
this.setXRot(Mth.clamp(Mth.wrapDegrees((float) (-(Mth.atan2(d1, d3) * 57.2957763671875))), -90, 40));
this.setYRot(this.getYRot() + Mth.clamp(0.9f * diffY, -20f, 20f));
if (target.distanceTo(this) <= 144 && VectorTool.calculateAngle(getViewVector(1), targetVec) < 10) {
@ -473,10 +476,10 @@ public class Hpj11Entity extends ContainerMobileVehicleEntity implements GeoEnti
var entityToSpawn = ((SmallCannonShellWeapon) getWeapon(0)).create(player);
Matrix4f transform = getBarrelTransform(1);
Vector4f worldPosition = transformPosition(transform, 0f, 0.4f, 2.6875f);
Vector4f worldPosition = transformPosition(transform, 0f, 0.4f, 0);
entityToSpawn.setPos(worldPosition.x, worldPosition.y, worldPosition.z);
entityToSpawn.shoot(getLookAngle().x, getLookAngle().y + 0.001, getLookAngle().z, 30, 0.75f);
entityToSpawn.shoot(getLookAngle().x, getLookAngle().y, getLookAngle().z, 30, 0.75f);
level().addFreshEntity(entityToSpawn);
this.entityData.set(GUN_ROTATE, entityData.get(GUN_ROTATE) + 0.5f);

View file

@ -69,5 +69,84 @@ public class RangeTool {
}
return validT;
}
private static final double TOLERANCE = 1e-3; // 牛顿迭代法的容差
private static final int MAX_ITERATIONS = 50; // 最大迭代次数
/**
* 计算炮弹发射向量
* @param launchPos 炮弹发射位置 (Vec3)
* @param targetPos 目标当前位置 (Vec3)
* @param targetVel 目标速度向量 (Vec3单位方块/tick)
* @param muzzleVelocity 炮弹出膛速度 (标量单位方块/tick)
* @return 炮弹的发射向量 (Vec3)若无法击中则返回预测值
*/
public static Vec3 calculateFiringSolution(Vec3 launchPos, Vec3 targetPos, Vec3 targetVel, double muzzleVelocity, double gravity) {
Vec3 d0 = targetPos.subtract(launchPos); // 位置差向量
double D = d0.lengthSqr(); // |d0|²
double E = d0.dot(targetVel); // d0 · u
double F = targetVel.lengthSqr(); // |u|²
// 计算四次方程的系数
double a = 0.25 * gravity * gravity;
double b = gravity * targetVel.y;
double c = F + gravity * d0.y - muzzleVelocity * muzzleVelocity;
double d = 2 * E;
double e = D;
// 牛顿迭代法求解时间 t
double t = estimateInitialTime(d0, muzzleVelocity); // 初始估计值
double prevT = t;
for (int i = 0; i < MAX_ITERATIONS; i++) {
double t2 = t * t;
double t3 = t2 * t;
double t4 = t3 * t;
// 计算函数值 f(t) = a*t + b*t³ + c*t² + d*t + e
double f = a * t4 + b * t3 + c * t2 + d * t + e;
if (Math.abs(f) < TOLERANCE) break;
// 计算导数值 f'(t) = 4a*t³ + 3b*t² + 2c*t + d
double df = 4 * a * t3 + 3 * b * t2 + 2 * c * t + d;
if (Math.abs(df) < 1e-10) {
t = prevT + 0.1; // 避免除零调整t
continue;
}
prevT = t;
t -= f / df; // 牛顿迭代
// 确保t为正数
if (t < 0) t = 0.1;
}
// 检查解的有效性
if (t > 0 && !Double.isNaN(t)) {
double invT = 1.0 / t;
// 计算速度分量
double vx = d0.x * invT + targetVel.x;
double vz = d0.z * invT + targetVel.z;
double vy = d0.y * invT + targetVel.y + 0.5 * gravity * t;
return new Vec3(vx, vy, vz);
} else {
// 备选方案线性预测目标位置
double fallbackT = Math.sqrt(D) / muzzleVelocity;
Vec3 predictedPos = targetPos.add(targetVel.scale(fallbackT));
Vec3 toPredicted = predictedPos.subtract(launchPos);
double vy = (toPredicted.y + 0.5 * gravity * fallbackT * fallbackT) / fallbackT;
Vec3 horizontal = new Vec3(toPredicted.x, 0, toPredicted.z).normalize();
double horizontalSpeed = Math.sqrt(muzzleVelocity * muzzleVelocity - vy * vy);
return new Vec3(
horizontal.x * horizontalSpeed,
vy,
horizontal.z * horizontalSpeed
);
}
}
// 初始时间估计无重力无移动的飞行时间
private static double estimateInitialTime(Vec3 d0, double velocity) {
return d0.length() / velocity;
}
}