添加AnimationTimer
This commit is contained in:
parent
a829085b6d
commit
76a674313c
4 changed files with 186 additions and 44 deletions
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue