优化投弹

This commit is contained in:
Atsuishio 2025-07-02 15:37:44 +08:00 committed by Light_Quanta
parent 81a91f0a84
commit 58a88578ad
No known key found for this signature in database
GPG key ID: 11A39A1B8C890959
6 changed files with 241 additions and 11 deletions

View file

@ -35,6 +35,7 @@ import java.util.List;
import static com.atsuishio.superbwarfare.client.RenderHelper.preciseBlit;
import static com.atsuishio.superbwarfare.entity.vehicle.base.VehicleEntity.HEAT;
import static com.atsuishio.superbwarfare.event.ClientEventHandler.zoomVehicle;
@OnlyIn(Dist.CLIENT)
public class AircraftOverlay implements LayeredDraw.Layer {
@ -45,6 +46,8 @@ public class AircraftOverlay implements LayeredDraw.Layer {
private static float lerpLock = 1;
private static float lerpG = 1;
private static float scopeScale = 1;
private static final ResourceLocation FRAME = Mod.loc("textures/screens/aircraft/frame.png");
private static final ResourceLocation FRAME_TARGET = Mod.loc("textures/screens/aircraft/frame_target.png");
private static final ResourceLocation FRAME_LOCK = Mod.loc("textures/screens/aircraft/frame_lock.png");
@ -60,7 +63,6 @@ public class AircraftOverlay implements LayeredDraw.Layer {
Camera camera = mc.gameRenderer.getMainCamera();
Vec3 cameraPos = camera.getPosition();
PoseStack poseStack = guiGraphics.pose();
Vec3 lookVec = new Vec3(camera.getLookVector());
var partialTick = deltaTracker.getGameTimeDeltaPartialTick(true);
@ -89,6 +91,42 @@ public class AircraftOverlay implements LayeredDraw.Layer {
Vec3 p = VectorUtil.worldToScreen(pos, cameraPos);
Vec3 pCross = VectorUtil.worldToScreen(posCross, cameraPos);
// 投弹准星
if (mobileVehicle instanceof A10Entity a10Entity && weaponVehicle.getWeaponIndex(0) == 2 && (zoomVehicle || mc.options.getCameraType() != CameraType.FIRST_PERSON)) {
Vec3 p0 = a10Entity.bombLandingPosO;
Vec3 p1 = a10Entity.bombLandingPos;
if (p0 != null && p1 != null) {
Vec3 bombCross = p0.lerp(p1, partialTick);
pCross = VectorUtil.worldToScreen(bombCross, cameraPos);
if (pCross != null && zoomVehicle) {
float f = (float) Math.min(screenWidth, screenHeight);
float f1 = Math.min((float) screenWidth / f, (float) screenHeight / f);
int i = Mth.floor(f * f1);
int j = Mth.floor(f * f1);
float x = (float) pCross.x;
float y = (float) pCross.y;
poseStack.pushPose();
poseStack.rotateAround(Axis.ZP.rotationDegrees(aircraftEntity.getRotZ(partialTick)), x, y, 0);
poseStack.pushPose();
poseStack.pushPose();
poseStack.translate(x, y, 0);
guiGraphics.drawString(Minecraft.getInstance().font, Component.literal("MK82 BOMB " + aircraftEntity.getAmmoCount(player)), 25, -11, 1, false);
poseStack.popPose();
preciseBlit(guiGraphics, Mod.loc("textures/screens/aircraft/bomb_scope.png"), x - 1.5f * i, y - 1.5f * j, 0, 0, 3 * i, 3 * j, 3 * i, 3 * j);
preciseBlit(guiGraphics, Mod.loc("textures/screens/aircraft/bomb_scope_pitch.png"), x - 1.5f * i, y - 1.5f * j - 4 * a10Entity.getPitch(partialTick), 0, 0, 3 * i, 3 * j, 3 * i, 3 * j);
renderKillIndicator(guiGraphics, x - 7.5f + (float) (2 * (Math.random() - 0.5f)), y - 7.5f + (float) (2 * (Math.random() - 0.5f)));
poseStack.popPose();
poseStack.popPose();
return;
}
}
}
if (p != null) {
poseStack.pushPose();
float x = (float) p.x;
@ -226,14 +264,13 @@ public class AircraftOverlay implements LayeredDraw.Layer {
} else if (mc.options.getCameraType() == CameraType.THIRD_PERSON_BACK) {
poseStack.pushPose();
poseStack.rotateAround(Axis.ZP.rotationDegrees(aircraftEntity.getRotZ(partialTick)), x, y, 0);
preciseBlit(guiGraphics, Mod.loc("textures/screens/drone.png"), x - 8, y - 8, 0, 0, 16, 16, 16, 16);
renderKillIndicator(guiGraphics, x - 7.5f + (float) (2 * (Math.random() - 0.5f)), y - 7.5f + (float) (2 * (Math.random() - 0.5f)));
poseStack.pushPose();
poseStack.translate(x, y, 0);
poseStack.scale(0.75f, 0.75f, 1);
ResourceLocation cross = Mod.loc("textures/screens/drone.png");
float size = 16;
if (mobileVehicle instanceof A10Entity a10Entity) {
if (weaponVehicle.getWeaponIndex(0) == 0) {
double heat = a10Entity.getEntityData().get(HEAT) / 100.0F;
@ -241,6 +278,8 @@ public class AircraftOverlay implements LayeredDraw.Layer {
} else if (weaponVehicle.getWeaponIndex(0) == 1) {
guiGraphics.drawString(Minecraft.getInstance().font, Component.literal("70MM ROCKET " + aircraftEntity.getAmmoCount(player)), 25, -9, -1, false);
} else if (weaponVehicle.getWeaponIndex(0) == 2) {
cross = Mod.loc("textures/screens/shotgun_hud.png");
size = 24;
guiGraphics.drawString(Minecraft.getInstance().font, Component.literal("MK82 BOMB " + aircraftEntity.getAmmoCount(player)), 25, -9, -1, false);
} else if (weaponVehicle.getWeaponIndex(0) == 3) {
guiGraphics.drawString(Minecraft.getInstance().font, Component.literal("AGM-65 " + aircraftEntity.getAmmoCount(player)), 25, -9, -1, false);
@ -249,6 +288,8 @@ public class AircraftOverlay implements LayeredDraw.Layer {
guiGraphics.drawString(Minecraft.getInstance().font, Component.literal("IR FLARES " + aircraftEntity.getDecoy()), 25, 1, -1, false);
poseStack.popPose();
preciseBlit(guiGraphics, cross, x - 0.5f * size, y - 0.5f * size, 0, 0, size, size, size, size);
renderKillIndicator(guiGraphics, x - 7.5f + (float) (2 * (Math.random() - 0.5f)), y - 7.5f + (float) (2 * (Math.random() - 0.5f)));
poseStack.popPose();
}
poseStack.popPose();

View file

@ -2,14 +2,17 @@ package com.atsuishio.superbwarfare.client.renderer.entity;
import com.atsuishio.superbwarfare.client.model.entity.A10Model;
import com.atsuishio.superbwarfare.entity.vehicle.A10Entity;
import com.atsuishio.superbwarfare.event.ClientEventHandler;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import software.bernie.geckolib.cache.object.BakedGeoModel;
@ -54,6 +57,10 @@ public class A10Renderer extends GeoEntityRenderer<A10Entity> {
@Override
public void renderRecursively(PoseStack poseStack, A10Entity animatable, GeoBone bone, RenderType renderType, MultiBufferSource bufferSource, VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int color) {
String name = bone.getName();
if (name.equals("root")) {
Player player = Minecraft.getInstance().player;
bone.setHidden(ClientEventHandler.zoomVehicle && animatable.getFirstPassenger() == player && animatable.getWeaponIndex(0) == 2);
}
if (name.equals("wingLR")) {
bone.setRotX(1.5f * Mth.lerp(partialTick, animatable.flap1LRotO, animatable.getFlap1LRot()) * Mth.DEG_TO_RAD);
}

View file

@ -7,7 +7,6 @@ import com.atsuishio.superbwarfare.entity.OBBEntity;
import com.atsuishio.superbwarfare.entity.vehicle.base.*;
import com.atsuishio.superbwarfare.entity.vehicle.damage.DamageModifier;
import com.atsuishio.superbwarfare.entity.vehicle.weapon.*;
import com.atsuishio.superbwarfare.event.ClientEventHandler;
import com.atsuishio.superbwarfare.event.ClientMouseHandler;
import com.atsuishio.superbwarfare.init.ModDamageTypes;
import com.atsuishio.superbwarfare.init.ModItems;
@ -17,6 +16,8 @@ import com.atsuishio.superbwarfare.network.message.receive.ShakeClientMessage;
import com.atsuishio.superbwarfare.tools.*;
import com.mojang.math.Axis;
import it.unimi.dsi.fastutil.Pair;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleTypes;
@ -58,6 +59,7 @@ import software.bernie.geckolib.util.GeckoLibUtil;
import java.util.List;
import java.util.function.Consumer;
import static com.atsuishio.superbwarfare.event.ClientEventHandler.zoomVehicle;
import static com.atsuishio.superbwarfare.event.ClientMouseHandler.freeCameraPitch;
import static com.atsuishio.superbwarfare.event.ClientMouseHandler.freeCameraYaw;
import static com.atsuishio.superbwarfare.tools.ParticleTool.sendParticle;
@ -84,6 +86,10 @@ public class A10Entity extends ContainerMobileVehicleEntity implements GeoEntity
private boolean wasFiring = false;
public float delta_x;
public float delta_y;
public Vec3 bombLandingPosO;
public Vec3 bombLandingPos;
public Vec3 deltaMovementO;
public OBB obb;
public OBB obb2;
public OBB obb3;
@ -222,6 +228,8 @@ public class A10Entity extends ContainerMobileVehicleEntity implements GeoEntity
this.wasFiring = this.isFiring();
this.lockingTargetO = getTargetUuid();
bombLandingPosO = bombLandingPos;
deltaMovementO = getDeltaMovement();
super.baseTick();
this.updateOBB();
@ -277,6 +285,12 @@ public class A10Entity extends ContainerMobileVehicleEntity implements GeoEntity
lowHealthWarning();
releaseDecoy();
// 计算航弹落点
if (level().isClientSide) {
bombLandingPos = ProjectileCalculator.calculateImpactPosition(level(), shootPos(1), shootVec(1), -0.06).getCenter();
}
this.refreshDimensions();
}
@ -734,8 +748,10 @@ public class A10Entity extends ContainerMobileVehicleEntity implements GeoEntity
worldPosition = transformPosition(transform, 0.1321625f, -0.56446875f, 7.85210625f);
} else if (getWeaponIndex(0) == 1) {
worldPosition = transformPosition(transform, 0f, -1.443f, 0.13f);
} else {
} else if (getWeaponIndex(0) == 2) {
worldPosition = transformPosition(transform, 0f, -1.203125f, 0.0625f);
} else {
worldPosition = transformPosition(transform, 0, -1.55f, 1.83f);
}
return new Vec3(worldPosition.x, worldPosition.y, worldPosition.z);
}
@ -745,7 +761,9 @@ public class A10Entity extends ContainerMobileVehicleEntity implements GeoEntity
Matrix4f transform = getVehicleTransform(tickDelta);
Vector4f worldPosition;
Vector4f worldPosition2;
if (getWeaponIndex(0) == 3) {
if (getWeaponIndex(0) == 2) {
return deltaMovementO.lerp(getDeltaMovement(), tickDelta).scale(0.75);
} else if (getWeaponIndex(0) == 3) {
worldPosition = transformPosition(transform, 0, 0, 0);
worldPosition2 = transformPosition(transform, 0, 0f, 1);
} else {
@ -849,7 +867,7 @@ public class A10Entity extends ContainerMobileVehicleEntity implements GeoEntity
}
Mk82Entity.setPos(worldPosition.x, worldPosition.y, worldPosition.z);
Mk82Entity.shoot(getDeltaMovement().x, getDeltaMovement().y, getDeltaMovement().z, (float) getDeltaMovement().length(), 10);
Mk82Entity.shoot(getDeltaMovement().x, getDeltaMovement().y, getDeltaMovement().z, (float) getDeltaMovement().scale(0.75).length(), 0.5f);
player.level().addFreshEntity(Mk82Entity);
BlockPos pos = BlockPos.containing(new Vec3(worldPosition.x, worldPosition.y, worldPosition.z));
@ -982,7 +1000,7 @@ public class A10Entity extends ContainerMobileVehicleEntity implements GeoEntity
@Override
public double getMouseSensitivity() {
return ClientEventHandler.zoomVehicle ? 0.03 : 0.07;
return zoomVehicle ? 0.03 : 0.07;
}
@Override
@ -1018,7 +1036,21 @@ public class A10Entity extends ContainerMobileVehicleEntity implements GeoEntity
@OnlyIn(Dist.CLIENT)
@Override
public @Nullable Vec2 getCameraRotation(float partialTicks, Player player, boolean zoom, boolean isFirstPerson) {
Minecraft mc = Minecraft.getInstance();
Camera camera = mc.gameRenderer.getMainCamera();
Vec3 cameraPos = camera.getPosition();
Vec3 p0 = bombLandingPosO;
Vec3 p1 = bombLandingPos;
Vec3 p2 = getViewVector(partialTicks);
if (p0 != null && p1 != null) {
p2 = cameraPos.vectorTo(p0.lerp(p1, partialTicks));
}
if (this.getSeatIndex(player) == 0) {
if (getWeaponIndex(0) == 2 && zoomVehicle) {
return new Vec2((float) (-getYRotFromVector(p2) - freeCameraYaw), (float) (-getXRotFromVector(p2) + freeCameraPitch));
}
return new Vec2((float) (getRotY(partialTicks) - freeCameraYaw), (float) (getRotX(partialTicks) + freeCameraPitch));
}
@ -1029,8 +1061,12 @@ public class A10Entity extends ContainerMobileVehicleEntity implements GeoEntity
@Override
public Vec3 getCameraPosition(float partialTicks, Player player, boolean zoom, boolean isFirstPerson) {
if (this.getSeatIndex(player) == 0) {
Matrix4f transform = getClientVehicleTransform(partialTicks);
if (getWeaponIndex(0) == 2 && zoomVehicle) {
return shootPos(partialTicks);
}
Matrix4f transform = getClientVehicleTransform(partialTicks);
Vector4f maxCameraPosition = transformPosition(transform, 0, 4, -14 - (float) ClientMouseHandler.custom3pDistanceLerp);
Vec3 finalPos = CameraTool.getMaxZoom(transform, maxCameraPosition);

View file

@ -0,0 +1,146 @@
package com.atsuishio.superbwarfare.tools;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
public class ProjectileCalculator {
private static final double TIME_STEP = 0.1; // 时间步长
private static final int MAX_ITERATIONS = 1000; // 最大迭代次数
/**
* 计算炮弹落地位置
*
* @param world 世界对象
* @param startPos 发射点位置
* @param launchVector 发射向量
* @return 预测的落点方块位置
*/
public static BlockPos calculateImpactPosition(Level world, Vec3 startPos, Vec3 launchVector, double gravity) {
// 当前炮弹位置和速度
Vec3 currentPos = startPos;
Vec3 currentVelocity = launchVector;
// 记录上一刻位置
Vec3 prevPos = startPos;
for (int i = 0; i < MAX_ITERATIONS; i++) {
// 更新位置
Vec3 nextPos = currentPos.add(
currentVelocity.x * TIME_STEP,
currentVelocity.y * TIME_STEP,
currentVelocity.z * TIME_STEP
);
// 更新速度重力影响
currentVelocity = currentVelocity.add(0, gravity * TIME_STEP, 0);
// 检查是否碰撞方块
BlockPos collisionPos = checkCollision(world, prevPos, nextPos);
if (collisionPos != null) {
return collisionPos;
}
// 更新位置进行下一步
prevPos = currentPos;
currentPos = nextPos;
// 安全检查防止飞出世界边界
if (currentPos.y < world.getMinBuildHeight() || currentPos.y > world.getMaxBuildHeight()) {
return new BlockPos(
(int)Math.floor(currentPos.x),
(int)Math.floor(currentPos.y),
(int)Math.floor(currentPos.z)
);
}
}
// 超过最大迭代次数返回当前位置
return new BlockPos(
(int)Math.floor(currentPos.x),
(int)Math.floor(currentPos.y),
(int)Math.floor(currentPos.z)
);
}
/**
* 检查两点之间是否有碰撞方块
*/
private static BlockPos checkCollision(Level world, Vec3 start, Vec3 end) {
// 使用距离和方向向量
double dx = end.x - start.x;
double dy = end.y - start.y;
double dz = end.z - start.z;
double distance = Math.sqrt(dx*dx + dy*dy + dz*dz);
if (distance == 0) return null;
// 方向单位向量
double dirX = dx / distance;
double dirY = dy / distance;
double dirZ = dz / distance;
// 步进检查
double stepSize = 0.1; // 检查步长
for (double t = 0; t < distance; t += stepSize) {
double x = start.x + dirX * t;
double y = start.y + dirY * t;
double z = start.z + dirZ * t;
BlockPos pos = new BlockPos((int)Math.floor(x), (int)Math.floor(y), (int)Math.floor(z));
BlockState state = world.getBlockState(pos);
// 检查是否碰到固体方块
if (!state.isAir()) {
return pos;
}
// 检查是否碰到下方方块炮弹落地
BlockPos belowPos = pos.below();
BlockState belowState = world.getBlockState(belowPos);
if (y - Math.floor(y) < 0.1 && !belowState.isAir()) {
return belowPos;
}
}
return null;
}
/**
* 快速预测落点不考虑地形仅数学计算
* 用于平坦地形或初始估算
*/
public static Vec3 estimateLandingPosition(Vec3 startPos, Vec3 launchVector, double gravity) {
double vx = launchVector.x;
double vy = launchVector.y;
double vz = launchVector.z;
// 计算飞行时间 (解二次方程: y = y0 + vy*t + 0.5*g*t² = 0)
double a = 0.5 * gravity;
double b = vy;
double c = startPos.y; // 假设地面高度为0
// 计算判别式
double discriminant = b*b - 4*a*c;
if (discriminant < 0) {
// 无实数解炮弹不会落地
return null;
}
// 取正数解
double t = (-b + Math.sqrt(discriminant)) / (2*a);
if (t < 0) {
t = (-b - Math.sqrt(discriminant)) / (2*a);
}
// 计算落点
double x = startPos.x + vx * t;
double z = startPos.z + vz * t;
return new Vec3(x, 0, z);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB