优化火炮弹道计算
This commit is contained in:
parent
146c014fc7
commit
acbbc1bff4
4 changed files with 139 additions and 82 deletions
|
@ -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));
|
||||
entityData.set(PITCH, (float) -RangeTool.calculateAngle(15, shellGravity, shootPos, new Vec3(targetX, targetY, targetZ), isDepressed));
|
||||
float angle = (float) -getXRotFromVector(launchVector);
|
||||
if (angle < -85 || angle > 14.9) {
|
||||
return false;
|
||||
}
|
||||
entityData.set(PITCH, angle);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void look(Vec3 pTarget) {
|
||||
|
|
|
@ -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));
|
||||
entityData.set(PITCH, (float) -RangeTool.calculateAngle(15, shellGravity, shootPos, new Vec3(targetX, targetY, targetZ), isDepressed));
|
||||
float angle = (float) -getXRotFromVector(launchVector);
|
||||
if (angle < -30 || angle > 2.7) {
|
||||
return false;
|
||||
}
|
||||
entityData.set(PITCH, angle);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void look(Vec3 pTarget) {
|
||||
|
|
|
@ -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,18 +162,17 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算按指定参数发射所需的仰角
|
||||
*
|
||||
* @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;
|
||||
double vx = dx / t;
|
||||
double vz = dz / t;
|
||||
double vy = (dy - 0.5 * g * t * t) / t;
|
||||
|
||||
return new Vec3(vx, vy, vz);
|
||||
}
|
||||
|
||||
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;
|
||||
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");
|
||||
}
|
||||
|
||||
return Math.sqrt(Math.pow(v, 4) - g * g * x * x - 2 * g * y * v * v);
|
||||
double sqrtDisc = Math.sqrt(discriminant);
|
||||
double u1 = (-b + sqrtDisc) / (2 * a);
|
||||
double u2 = (-b - sqrtDisc) / (2 * a);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue