添加AnimationTimer

This commit is contained in:
Light_Quanta 2025-03-19 11:32:23 +08:00
parent a829085b6d
commit 76a674313c
No known key found for this signature in database
GPG key ID: 11A39A1B8C890959
4 changed files with 186 additions and 44 deletions

View file

@ -15,6 +15,8 @@ import com.atsuishio.superbwarfare.init.ModItems;
import com.atsuishio.superbwarfare.tools.FormatTool; import com.atsuishio.superbwarfare.tools.FormatTool;
import com.atsuishio.superbwarfare.tools.InventoryTool; import com.atsuishio.superbwarfare.tools.InventoryTool;
import com.atsuishio.superbwarfare.tools.SeekTool; 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.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack; 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 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 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; 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) @SubscribeEvent(priority = EventPriority.HIGH)
@ -473,23 +474,34 @@ public class VehicleHudOverlay {
lastTimeRenderingWeapons = temp; lastTimeRenderingWeapons = temp;
var currentTime = System.currentTimeMillis(); var currentTime = System.currentTimeMillis();
// 若上一帧未在渲染武器信息则初始化动画相关变量 // 若上一帧未在渲染武器信息则初始化动画相关变量
if (!lastTimeRenderingWeapons) { if (!lastTimeRenderingWeapons) {
lastTimeWeaponIndex = weaponIndex; weaponSlotsTimer[weaponIndex].begin();
lastTimeRenderWeaponIndex = weaponIndex; weaponSlotsTimer[weaponIndex].forward(currentTime);
weaponSlotUpdateTime[weaponIndex] = currentTime; if (lastTimeWeaponIndex != weaponIndex) {
weaponIndexUpdateTime = currentTime; weaponSlotsTimer[lastTimeWeaponIndex].backward(currentTime);
weaponSlotsTimer[lastTimeWeaponIndex].end();
lastTimeWeaponIndex = weaponIndex;
lastTimeRenderWeaponIndex = weaponIndex;
}
weaponIndexUpdateTimer.begin();
weaponIndexUpdateTimer.forward(currentTime);
} }
// 切换武器时更新上一个武器槽位和当前武器槽位的动画信息 // 切换武器时更新上一个武器槽位和当前武器槽位的动画信息
if (weaponIndex != lastTimeWeaponIndex) { if (weaponIndex != lastTimeWeaponIndex) {
weaponSlotUpdateTime[weaponIndex] = currentTime; weaponSlotsTimer[weaponIndex].forward(currentTime);
weaponSlotUpdateTime[lastTimeWeaponIndex] = currentTime; weaponSlotsTimer[lastTimeWeaponIndex].backward(currentTime);
lastTimeRenderWeaponIndex = lastTimeWeaponIndex; lastTimeRenderWeaponIndex = lastTimeWeaponIndex;
lastTimeWeaponIndex = weaponIndex; lastTimeWeaponIndex = weaponIndex;
weaponIndexUpdateTime = currentTime;
weaponIndexUpdateTimer.begin();
weaponIndexUpdateTimer.forward(currentTime);
} }
var pose = guiGraphics.pose(); var pose = guiGraphics.pose();
@ -512,24 +524,18 @@ public class VehicleHudOverlay {
pose.pushPose(); pose.pushPose();
// 相对于最左边的偏移量 // 相对于最左边的偏移量
float startXDiff; float xOffset;
// 向右偏移的最长长度 // 向右偏移的最长长度
var xDiff = 35; var maxXOffset = 35;
var currentWeaponUpdateTime = weaponSlotUpdateTime[i]; var currentSlotTimer = weaponSlotsTimer[i];
var progress = easeOutCirc(currentWeaponUpdateTime, currentWeaponUpdateTime + ANIMATION_TIME, currentTime); var progress = currentSlotTimer.getProgress(currentTime);
if (weaponIndex != i) { RenderSystem.setShaderColor(1, 1, 1, Mth.lerp(progress, 0.2f, 1));
// 未选中 xOffset = Mth.lerp(progress, maxXOffset, 0);
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));
startXDiff = Mth.lerp(progress, xDiff, 0); // 当前选中武器
if (weaponIndex == i) {
var startY = Mth.lerp(progress, var startY = Mth.lerp(progress,
h - (weapons.size() - 1 - lastTimeRenderWeaponIndex) * 18 - 16, h - (weapons.size() - 1 - lastTimeRenderWeaponIndex) * 18 - 16,
h - (weapons.size() - 1 - weaponIndex) * 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); preciseBlit(guiGraphics, SELECTED, w - 95, startY, 100, 0, 0, 8, 8, 8, 8);
if (InventoryTool.hasCreativeAmmoBox(player) && !(weapon instanceof LaserWeapon) && !(weapon instanceof HeliRocketWeapon)) { 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 { } else {
renderNumber(guiGraphics, weaponVehicle.getAmmoCount(player), weapon instanceof LaserWeapon, 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, frame, w - 85 + xOffset, 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, weapon.icon, w - 85 + xOffset, h - frameIndex * 18 - 20, 100, 0, 0, 75, 16, 75, 16);
pose.popPose(); pose.popPose();
@ -556,23 +562,12 @@ public class VehicleHudOverlay {
pose.popPose(); pose.popPose();
// 切换武器光标动画播放结束后更新上次选择槽位 // 切换武器光标动画播放结束后更新上次选择槽位
if (lastTimeWeaponIndex != lastTimeRenderWeaponIndex && currentTime - weaponIndexUpdateTime > ANIMATION_TIME) { if (lastTimeWeaponIndex != lastTimeRenderWeaponIndex && weaponIndexUpdateTimer.finished(currentTime)) {
lastTimeRenderWeaponIndex = lastTimeWeaponIndex; lastTimeRenderWeaponIndex = lastTimeWeaponIndex;
} }
lastTimeRenderingWeapons = true; 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) { private static void renderNumber(GuiGraphics guiGraphics, int number, boolean percent, float x, float y, float scale) {
float pX = x; float pX = x;
if (percent) { if (percent) {

View file

@ -35,7 +35,7 @@ public class ExplosionMixin {
target = "Ljava/util/List;get(I)Ljava/lang/Object;", target = "Ljava/util/List;get(I)Ljava/lang/Object;",
ordinal = 0), ordinal = 0),
locals = LocalCapture.CAPTURE_FAILHARD) 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) { if (list.size() >= k2) {
var obj = list.get(k2); var obj = list.get(k2);
if (obj instanceof VehicleEntity vehicle) { if (obj instanceof VehicleEntity vehicle) {

View file

@ -0,0 +1,9 @@
package com.atsuishio.superbwarfare.tools.animation;
import java.util.function.Function;
public class AnimationCurves {
public static final Function<Float, Float> LINEAR = progress -> progress;
public static final Function<Float, Float> EASE_OUT_CIRC = progress -> (float) Math.sqrt(1 - Math.pow(progress - 1, 2));
}

View file

@ -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<Float, Float> animationCurve;
/**
* 创建一个反向线性动画计时器
*
* @param duration 动画持续时间单位为毫秒
*/
public AnimationTimer(long duration) {
this(duration, AnimationCurves.LINEAR);
}
/**
* 创建一个反向动画计时器
*
* @param duration 动画持续时间单位为毫秒
* @param animationCurve 动画曲线函数
*/
public AnimationTimer(long duration, Function<Float, Float> 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<Float, Float> 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;
}
}