diff --git a/src/main/java/com/atsuishio/superbwarfare/client/overlay/VehicleHudOverlay.java b/src/main/java/com/atsuishio/superbwarfare/client/overlay/VehicleHudOverlay.java index 310c8bd97..6941af38d 100644 --- a/src/main/java/com/atsuishio/superbwarfare/client/overlay/VehicleHudOverlay.java +++ b/src/main/java/com/atsuishio/superbwarfare/client/overlay/VehicleHudOverlay.java @@ -15,6 +15,8 @@ import com.atsuishio.superbwarfare.init.ModItems; import com.atsuishio.superbwarfare.tools.FormatTool; import com.atsuishio.superbwarfare.tools.InventoryTool; import com.atsuishio.superbwarfare.tools.SeekTool; +import com.atsuishio.superbwarfare.tools.animation.AnimationCurves; +import com.atsuishio.superbwarfare.tools.animation.AnimationTimer; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; @@ -64,13 +66,12 @@ public class VehicleHudOverlay { private static final ResourceLocation SELECTED = ModUtils.loc("textures/screens/vehicle_weapon/selected.png"); private static final ResourceLocation NUMBER = ModUtils.loc("textures/screens/vehicle_weapon/number.png"); - private static final long[] weaponSlotUpdateTime = new long[9]; - private static boolean lastTimeRenderingWeapons = false; - private static int lastTimeWeaponIndex = -1; - private static int lastTimeRenderWeaponIndex = -1; - private static long weaponIndexUpdateTime = 0; - public static final int ANIMATION_TIME = 300; + private static final AnimationTimer[] weaponSlotsTimer = AnimationTimer.createTimers(9, ANIMATION_TIME, AnimationCurves.EASE_OUT_CIRC); + private static boolean lastTimeRenderingWeapons = false; + private static int lastTimeWeaponIndex = 0; + private static int lastTimeRenderWeaponIndex = 0; + private static final AnimationTimer weaponIndexUpdateTimer = new AnimationTimer(ANIMATION_TIME, AnimationCurves.EASE_OUT_CIRC); @SubscribeEvent(priority = EventPriority.HIGH) @@ -473,23 +474,34 @@ public class VehicleHudOverlay { lastTimeRenderingWeapons = temp; var currentTime = System.currentTimeMillis(); + // 若上一帧未在渲染武器信息,则初始化动画相关变量 if (!lastTimeRenderingWeapons) { - lastTimeWeaponIndex = weaponIndex; - lastTimeRenderWeaponIndex = weaponIndex; + weaponSlotsTimer[weaponIndex].begin(); + weaponSlotsTimer[weaponIndex].forward(currentTime); - weaponSlotUpdateTime[weaponIndex] = currentTime; - weaponIndexUpdateTime = currentTime; + if (lastTimeWeaponIndex != weaponIndex) { + weaponSlotsTimer[lastTimeWeaponIndex].backward(currentTime); + weaponSlotsTimer[lastTimeWeaponIndex].end(); + + lastTimeWeaponIndex = weaponIndex; + lastTimeRenderWeaponIndex = weaponIndex; + } + + weaponIndexUpdateTimer.begin(); + weaponIndexUpdateTimer.forward(currentTime); } // 切换武器时,更新上一个武器槽位和当前武器槽位的动画信息 if (weaponIndex != lastTimeWeaponIndex) { - weaponSlotUpdateTime[weaponIndex] = currentTime; - weaponSlotUpdateTime[lastTimeWeaponIndex] = currentTime; + weaponSlotsTimer[weaponIndex].forward(currentTime); + weaponSlotsTimer[lastTimeWeaponIndex].backward(currentTime); lastTimeRenderWeaponIndex = lastTimeWeaponIndex; lastTimeWeaponIndex = weaponIndex; - weaponIndexUpdateTime = currentTime; + + weaponIndexUpdateTimer.begin(); + weaponIndexUpdateTimer.forward(currentTime); } var pose = guiGraphics.pose(); @@ -512,24 +524,18 @@ public class VehicleHudOverlay { pose.pushPose(); // 相对于最左边的偏移量 - float startXDiff; + float xOffset; // 向右偏移的最长长度 - var xDiff = 35; + var maxXOffset = 35; - var currentWeaponUpdateTime = weaponSlotUpdateTime[i]; - var progress = easeOutCirc(currentWeaponUpdateTime, currentWeaponUpdateTime + ANIMATION_TIME, currentTime); + var currentSlotTimer = weaponSlotsTimer[i]; + var progress = currentSlotTimer.getProgress(currentTime); - if (weaponIndex != i) { - // 未选中 - RenderSystem.setShaderColor(1, 1, 1, - Mth.lerp(progress, 1, 0.5f) - ); - startXDiff = Mth.lerp(progress, 0, xDiff); - } else { - // 选中 - RenderSystem.setShaderColor(1, 1, 1, Mth.lerp(progress, 0.2f, 1)); + RenderSystem.setShaderColor(1, 1, 1, Mth.lerp(progress, 0.2f, 1)); + xOffset = Mth.lerp(progress, maxXOffset, 0); - startXDiff = Mth.lerp(progress, xDiff, 0); + // 当前选中武器 + if (weaponIndex == i) { var startY = Mth.lerp(progress, h - (weapons.size() - 1 - lastTimeRenderWeaponIndex) * 18 - 16, h - (weapons.size() - 1 - weaponIndex) * 18 - 16 @@ -537,15 +543,15 @@ public class VehicleHudOverlay { preciseBlit(guiGraphics, SELECTED, w - 95, startY, 100, 0, 0, 8, 8, 8, 8); if (InventoryTool.hasCreativeAmmoBox(player) && !(weapon instanceof LaserWeapon) && !(weapon instanceof HeliRocketWeapon)) { - preciseBlit(guiGraphics, NUMBER, w - 28 + startXDiff, h - frameIndex * 18 - 15, 100, 58, 0, 10, 7.5f, 75, 7.5f); + preciseBlit(guiGraphics, NUMBER, w - 28 + xOffset, h - frameIndex * 18 - 15, 100, 58, 0, 10, 7.5f, 75, 7.5f); } else { renderNumber(guiGraphics, weaponVehicle.getAmmoCount(player), weapon instanceof LaserWeapon, - w - 20 + startXDiff, h - frameIndex * 18 - 15.5f, 0.25f); + w - 20 + xOffset, h - frameIndex * 18 - 15.5f, 0.25f); } } - preciseBlit(guiGraphics, frame, w - 85 + startXDiff, h - frameIndex * 18 - 20, 100, 0, 0, 75, 16, 75, 16); - preciseBlit(guiGraphics, weapon.icon, w - 85 + startXDiff, h - frameIndex * 18 - 20, 100, 0, 0, 75, 16, 75, 16); + preciseBlit(guiGraphics, frame, w - 85 + xOffset, h - frameIndex * 18 - 20, 100, 0, 0, 75, 16, 75, 16); + preciseBlit(guiGraphics, weapon.icon, w - 85 + xOffset, h - frameIndex * 18 - 20, 100, 0, 0, 75, 16, 75, 16); pose.popPose(); @@ -556,23 +562,12 @@ public class VehicleHudOverlay { pose.popPose(); // 切换武器光标动画播放结束后,更新上次选择槽位 - if (lastTimeWeaponIndex != lastTimeRenderWeaponIndex && currentTime - weaponIndexUpdateTime > ANIMATION_TIME) { + if (lastTimeWeaponIndex != lastTimeRenderWeaponIndex && weaponIndexUpdateTimer.finished(currentTime)) { lastTimeRenderWeaponIndex = lastTimeWeaponIndex; } lastTimeRenderingWeapons = true; } - private static float easeOutCirc(long start, long end, long current) { - double t = (double) (current - start) / (double) (end - start); - - if (t < 0) { - return 0; - } else if (t > 1) { - return 1; - } - return (float) Math.sqrt(1 - java.lang.Math.pow(t - 1, 2d)); - } - private static void renderNumber(GuiGraphics guiGraphics, int number, boolean percent, float x, float y, float scale) { float pX = x; if (percent) { diff --git a/src/main/java/com/atsuishio/superbwarfare/mixins/ExplosionMixin.java b/src/main/java/com/atsuishio/superbwarfare/mixins/ExplosionMixin.java index a323ba2e5..508c29ce9 100644 --- a/src/main/java/com/atsuishio/superbwarfare/mixins/ExplosionMixin.java +++ b/src/main/java/com/atsuishio/superbwarfare/mixins/ExplosionMixin.java @@ -35,7 +35,7 @@ public class ExplosionMixin { target = "Ljava/util/List;get(I)Ljava/lang/Object;", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) - public void explode(CallbackInfo ci, Set set, int i, float f2, int k1, int l1, int i2, int i1, int j2, int j1, List list, Vec3 vec3, int k2) { + public void explode(CallbackInfo ci, Set set, int i, float f2, int k1, int l1, int i2, int i1, int j2, int j1, List list, Vec3 vec3, int k2) { if (list.size() >= k2) { var obj = list.get(k2); if (obj instanceof VehicleEntity vehicle) { diff --git a/src/main/java/com/atsuishio/superbwarfare/tools/animation/AnimationCurves.java b/src/main/java/com/atsuishio/superbwarfare/tools/animation/AnimationCurves.java new file mode 100644 index 000000000..352e87361 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/tools/animation/AnimationCurves.java @@ -0,0 +1,9 @@ +package com.atsuishio.superbwarfare.tools.animation; + + +import java.util.function.Function; + +public class AnimationCurves { + public static final Function LINEAR = progress -> progress; + public static final Function EASE_OUT_CIRC = progress -> (float) Math.sqrt(1 - Math.pow(progress - 1, 2)); +} diff --git a/src/main/java/com/atsuishio/superbwarfare/tools/animation/AnimationTimer.java b/src/main/java/com/atsuishio/superbwarfare/tools/animation/AnimationTimer.java new file mode 100644 index 000000000..7c87ba50a --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/tools/animation/AnimationTimer.java @@ -0,0 +1,138 @@ +package com.atsuishio.superbwarfare.tools.animation; + +import net.minecraft.util.Mth; + +import java.util.function.Function; + +public class AnimationTimer { + private final long duration; + private long startTime; + private boolean reversed; + private boolean initialized; + private boolean isStart = true; + + private final Function animationCurve; + + /** + * 创建一个反向线性动画计时器 + * + * @param duration 动画持续时间,单位为毫秒 + */ + public AnimationTimer(long duration) { + this(duration, AnimationCurves.LINEAR); + } + + /** + * 创建一个反向动画计时器 + * + * @param duration 动画持续时间,单位为毫秒 + * @param animationCurve 动画曲线函数 + */ + public AnimationTimer(long duration, Function animationCurve) { + this.duration = duration; + this.animationCurve = animationCurve; + } + + /** + * 创建多个线性动画计时器 + * + * @param size 计时器数量 + * @param duration 动画持续时间,单位为毫秒 + */ + + public static AnimationTimer[] createTimers(int size, long duration) { + return createTimers(size, duration, AnimationCurves.LINEAR); + } + + /** + * 创建多个动画计时器 + * + * @param size 计时器数量 + * @param duration 动画持续时间,单位为毫秒 + * @param animationCurve 动画曲线函数 + */ + public static AnimationTimer[] createTimers(int size, long duration, Function animationCurve) { + var timers = new AnimationTimer[size]; + var currentTime = System.currentTimeMillis(); + for (int i = 0; i < size; i++) { + timers[i] = new AnimationTimer(duration, animationCurve); + timers[i].end(); + timers[i].backward(currentTime); + } + return timers; + } + + + /** + * 获取当前进度 + * + * @return 进度值,范围在0到1之间 + */ + public float getProgress(long currentTime) { + if (reversed) { + return 1 - animationCurve.apply(Mth.clamp(1 - getElapsedTime(currentTime) / (float) duration, 0, 1)); + } else { + return animationCurve.apply(Mth.clamp(getElapsedTime(currentTime) / (float) duration, 0, 1)); + } + } + + private long getElapsedTime(long currentTime) { + if (!initialized) return 0; + + if (reversed) { + return Math.min(duration, Math.max(0, startTime - currentTime)); + } else { + return Math.min(duration, currentTime - startTime); + } + } + + /** + * 当前动画是否已经结束 + */ + public boolean finished(long currentTime) { + return getProgress(currentTime) >= 1; + } + + /** + * 将计时器设置为开始状态 + */ + public void begin() { + initialized = false; + isStart = true; + } + + /** + * 将计时器设置为结束状态 + */ + public void end() { + initialized = false; + isStart = false; + } + + /** + * 正向开始计时 + */ + public void forward(long currentTime) { + if (!initialized) { + initialized = true; + startTime = currentTime + (isStart ? 0 : duration); + } else { + startTime = currentTime - getElapsedTime(currentTime); + } + reversed = false; + } + + /** + * 反向开始计时 + */ + public void backward(long currentTime) { + if (!initialized) { + initialized = true; + startTime = currentTime + (isStart ? duration : 0); + } else { + startTime = currentTime + getElapsedTime(currentTime); + } + reversed = true; + } + +}