调整弹药总量显示位置,添加弹药盒弹药数量显示,添加更多动画
This commit is contained in:
parent
1aeb3d65fc
commit
3255c0754a
6 changed files with 204 additions and 115 deletions
|
@ -14,14 +14,14 @@ import com.atsuishio.superbwarfare.tools.GunsTool;
|
|||
import com.atsuishio.superbwarfare.tools.InventoryTool;
|
||||
import com.atsuishio.superbwarfare.tools.animation.AnimationCurves;
|
||||
import com.atsuishio.superbwarfare.tools.animation.AnimationTimer;
|
||||
import com.atsuishio.superbwarfare.tools.animation.DualValueHolder;
|
||||
import com.atsuishio.superbwarfare.tools.animation.ValueAnimator;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.FastColor;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
@ -215,10 +215,17 @@ public class AmmoBarOverlay {
|
|||
private static final AnimationTimer ammoInfoTimer = new AnimationTimer(1500)
|
||||
.forwardAnimation(AnimationCurves.EASE_OUT_EXPO)
|
||||
.backwardAnimation(AnimationCurves.EASE_IN_EXPO);
|
||||
private static final AnimationTimer ammoBoxTimer = new AnimationTimer(1500)
|
||||
.forwardAnimation(AnimationCurves.EASE_OUT_EXPO)
|
||||
.backwardAnimation(AnimationCurves.EASE_IN_EXPO);
|
||||
|
||||
private static final DualValueHolder<Integer>[] ammoCountHolders = DualValueHolder.create(AmmoType.values().length, 0);
|
||||
private static final ValueAnimator<Integer>[] ammoCountAnimators = ValueAnimator.create(
|
||||
AmmoType.values().length, 800, 0
|
||||
);
|
||||
private static final ValueAnimator<Integer>[] ammoBoxAnimators = ValueAnimator.create(
|
||||
AmmoType.values().length, 800, 0
|
||||
);
|
||||
|
||||
private static final AnimationTimer[] ammoCountTimers = AnimationTimer.createTimers(AmmoType.values().length, 800, AnimationCurves.EASE_OUT_EXPO);
|
||||
|
||||
/**
|
||||
* 在手持弹药或弹药盒时,渲染玩家弹药总量信息
|
||||
|
@ -229,80 +236,100 @@ public class AmmoBarOverlay {
|
|||
Player player = Minecraft.getInstance().player;
|
||||
if (player == null || player.isSpectator()) return;
|
||||
|
||||
boolean isAmmoBox = false;
|
||||
|
||||
// 动画计算
|
||||
var currentTime = System.currentTimeMillis();
|
||||
ItemStack stack = player.getMainHandItem();
|
||||
if ((stack.getItem() instanceof AmmoSupplierItem || stack.getItem() == ModItems.AMMO_BOX.get()) && !(player.getVehicle() instanceof ArmedVehicleEntity vehicle && vehicle.banHand(player))) {
|
||||
if ((stack.getItem() instanceof AmmoSupplierItem || stack.getItem() == ModItems.AMMO_BOX.get())
|
||||
&& !(player.getVehicle() instanceof ArmedVehicleEntity vehicle && vehicle.banHand(player))
|
||||
) {
|
||||
// 刚拿出弹药物品时,视为开始弹药信息渲染
|
||||
startRenderingAmmoInfo = ammoInfoTimer.getProgress(currentTime) == 0;
|
||||
ammoInfoTimer.forward(currentTime);
|
||||
|
||||
if (stack.getItem() == ModItems.AMMO_BOX.get()) {
|
||||
isAmmoBox = true;
|
||||
ammoBoxTimer.forward(currentTime);
|
||||
} else {
|
||||
ammoBoxTimer.backward(currentTime);
|
||||
}
|
||||
} else {
|
||||
ammoInfoTimer.backward(currentTime);
|
||||
ammoBoxTimer.backward(currentTime);
|
||||
}
|
||||
if (!ammoInfoTimer.isForward() && ammoInfoTimer.finished(currentTime)) return;
|
||||
|
||||
var poseStack = event.getGuiGraphics().pose();
|
||||
poseStack.pushPose();
|
||||
|
||||
var xOffset = -Mth.lerp(ammoInfoTimer.getProgress(currentTime), 0, 120);
|
||||
final int fontHeight = 15;
|
||||
var yOffset = -AmmoType.values().length * fontHeight;
|
||||
|
||||
// 渲染总弹药数量
|
||||
var cap = player.getCapability(ModVariables.PLAYER_VARIABLES_CAPABILITY, null).orElse(new ModVariables.PlayerVariables());
|
||||
int w = event.getWindow().getGuiScaledWidth();
|
||||
int h = event.getWindow().getGuiScaledHeight();
|
||||
|
||||
// 总体透明度设置
|
||||
RenderSystem.setShaderColor(1, 1, 1, Mth.lerp(ammoInfoTimer.getProgress(currentTime), 0, 1));
|
||||
var ammoX = ammoInfoTimer.lerp(w + 120, (float) w / 2 + 40, currentTime);
|
||||
final int fontHeight = 15;
|
||||
var yOffset = (-h - AmmoType.values().length * fontHeight) / 2f;
|
||||
|
||||
// 渲染总弹药数量
|
||||
var cap = player.getCapability(ModVariables.PLAYER_VARIABLES_CAPABILITY, null).orElse(new ModVariables.PlayerVariables());
|
||||
var font = Minecraft.getInstance().font;
|
||||
|
||||
for (var type : AmmoType.values()) {
|
||||
var index = type.ordinal();
|
||||
var ammoCount = type.get(cap);
|
||||
var holder = ammoCountHolders[index];
|
||||
var timer = ammoCountTimers[index];
|
||||
var animator = ammoCountAnimators[index];
|
||||
|
||||
var boxAnimator = ammoBoxAnimators[index];
|
||||
var boxAmmoCount = boxAnimator.newValue();
|
||||
boolean boxAmmoSelected = false;
|
||||
|
||||
if (isAmmoBox) {
|
||||
var ammoBoxType = stack.getOrCreateTag().getString("Type");
|
||||
boxAmmoCount = type.get(stack);
|
||||
if (ammoBoxType.equals("All") || ammoBoxType.equals(type.name)) {
|
||||
boxAnimator.forward(currentTime);
|
||||
boxAmmoSelected = true;
|
||||
} else {
|
||||
boxAnimator.reset(boxAmmoCount);
|
||||
}
|
||||
}
|
||||
|
||||
// 首次开始渲染弹药信息时,记录弹药数量,便于后续播放动画
|
||||
if (startRenderingAmmoInfo) {
|
||||
holder.reset(ammoCount);
|
||||
timer.endForward(currentTime);
|
||||
animator.reset(ammoCount);
|
||||
animator.endForward(currentTime);
|
||||
if (isAmmoBox) {
|
||||
boxAnimator.reset(type.get(stack));
|
||||
boxAnimator.endForward(currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
int isAdd = ammoCount == holder.oldValue() ? 0 : (ammoCount > holder.oldValue() ? 1 : -1);
|
||||
// 弹药数量变化时,开始播放弹药数量更改动画
|
||||
if (holder.newValue() != ammoCount) {
|
||||
// 更新初始和当前弹药数量,播放由 初始弹药数量 -> 当前弹药数量 的动画
|
||||
holder.update(ammoCount);
|
||||
// 开始播放弹药数量更改动画
|
||||
timer.beginForward(currentTime);
|
||||
}
|
||||
int ammoAdd = Integer.compare(ammoCount, animator.oldValue());
|
||||
// 弹药数量变化时,更新并开始播放弹药数量更改动画
|
||||
animator.compareAndUpdate(ammoCount, () -> {
|
||||
// 弹药数量变化时,开始播放弹药数量更改动画
|
||||
animator.beginForward(currentTime);
|
||||
});
|
||||
|
||||
var progress = timer.getProgress(currentTime);
|
||||
var progress = animator.getProgress(currentTime);
|
||||
var ammoCountStr = Integer.toString(
|
||||
Math.round(Mth.lerp(progress, holder.oldValue(), ammoCount))
|
||||
Math.round(animator.lerp(animator.oldValue(), ammoCount, currentTime))
|
||||
);
|
||||
|
||||
// 弹药增加时,颜色由绿变白,否则由红变白
|
||||
var fontColor = switch (isAdd) {
|
||||
case 1 -> Mth.color(
|
||||
Mth.lerp(progress, 0, 1),
|
||||
1,
|
||||
Mth.lerp(progress, 0, 1)
|
||||
);
|
||||
case -1 -> Mth.color(
|
||||
1,
|
||||
Mth.lerp(progress, 0, 1),
|
||||
Mth.lerp(progress, 0, 1)
|
||||
);
|
||||
default -> 0xFFFFFF;
|
||||
};
|
||||
var fontColor = FastColor.ARGB32.lerp(progress, switch (ammoAdd) {
|
||||
case 1 -> 0xFF00FF00;
|
||||
case -1 -> 0xFFFF0000;
|
||||
default -> 0xFFFFFFFF;
|
||||
}, 0xFFFFFFFF);
|
||||
|
||||
RenderSystem.setShaderColor(1, 1, 1, ammoInfoTimer.lerp(0, 1, currentTime));
|
||||
|
||||
// 弹药数量
|
||||
event.getGuiGraphics().drawString(
|
||||
Minecraft.getInstance().font,
|
||||
font,
|
||||
ammoCountStr,
|
||||
w + xOffset + (30 - font.width(ammoCountStr)),
|
||||
ammoX + (30 - font.width(ammoCountStr)),
|
||||
h + yOffset,
|
||||
fontColor,
|
||||
true
|
||||
|
@ -310,14 +337,45 @@ public class AmmoBarOverlay {
|
|||
|
||||
// 弹药类型
|
||||
event.getGuiGraphics().drawString(
|
||||
Minecraft.getInstance().font,
|
||||
font,
|
||||
Component.translatable(type.translatableKey).getString(),
|
||||
w + xOffset + 35,
|
||||
ammoX + 35,
|
||||
h + yOffset,
|
||||
fontColor,
|
||||
true
|
||||
);
|
||||
|
||||
// 弹药盒信息渲染
|
||||
RenderSystem.setShaderColor(1, 1, 1, ammoBoxTimer.lerp(0, 1, currentTime));
|
||||
var ammoBoxX = ammoBoxTimer.lerp(-30, (float) w / 2, currentTime);
|
||||
|
||||
int ammoBoxAdd = Integer.compare(boxAmmoCount, boxAnimator.oldValue());
|
||||
boxAnimator.compareAndUpdate(boxAmmoCount, () -> boxAnimator.beginForward(currentTime));
|
||||
|
||||
// 选中时显示为黄色,否则为白色
|
||||
var targetColor = boxAmmoSelected ? 0xFFFFFF00 : 0xFFFFFFFF;
|
||||
|
||||
var boxFontColor = FastColor.ARGB32.lerp(boxAnimator.getProgress(currentTime),
|
||||
switch (ammoBoxAdd) {
|
||||
case 1 -> 0xFF00FF00;
|
||||
case -1 -> 0xFFFF0000;
|
||||
default -> targetColor;
|
||||
},
|
||||
targetColor
|
||||
);
|
||||
|
||||
// 弹药盒内弹药数量
|
||||
event.getGuiGraphics().drawString(
|
||||
Minecraft.getInstance().font,
|
||||
Integer.toString(
|
||||
Math.round(boxAnimator.lerp(boxAnimator.oldValue(), boxAmmoCount, currentTime))
|
||||
),
|
||||
ammoBoxX - 70,
|
||||
h + yOffset,
|
||||
boxFontColor,
|
||||
true
|
||||
);
|
||||
|
||||
yOffset += fontHeight;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,9 +68,9 @@ public class VehicleHudOverlay {
|
|||
|
||||
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 boolean wasRenderingWeapons = false;
|
||||
private static int oldWeaponIndex = 0;
|
||||
private static int oldRenderWeaponIndex = 0;
|
||||
private static final AnimationTimer weaponIndexUpdateTimer = new AnimationTimer(ANIMATION_TIME).animation(AnimationCurves.EASE_OUT_CIRC);
|
||||
|
||||
|
||||
|
@ -81,7 +81,7 @@ public class VehicleHudOverlay {
|
|||
Player player = Minecraft.getInstance().player;
|
||||
|
||||
if (!shouldRenderHud(player)) {
|
||||
lastTimeRenderingWeapons = false;
|
||||
wasRenderingWeapons = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -456,8 +456,8 @@ public class VehicleHudOverlay {
|
|||
private static void renderWeaponInfo(GuiGraphics guiGraphics, VehicleEntity vehicle, int w, int h) {
|
||||
if (!(vehicle instanceof WeaponVehicleEntity weaponVehicle)) return;
|
||||
|
||||
var temp = lastTimeRenderingWeapons;
|
||||
lastTimeRenderingWeapons = false;
|
||||
var temp = wasRenderingWeapons;
|
||||
wasRenderingWeapons = false;
|
||||
|
||||
Player player = Minecraft.getInstance().player;
|
||||
assert player != null;
|
||||
|
@ -471,37 +471,33 @@ public class VehicleHudOverlay {
|
|||
int weaponIndex = weaponVehicle.getWeaponIndex(index);
|
||||
if (weaponIndex == -1) return;
|
||||
|
||||
lastTimeRenderingWeapons = temp;
|
||||
wasRenderingWeapons = temp;
|
||||
|
||||
var currentTime = System.currentTimeMillis();
|
||||
|
||||
// 若上一帧未在渲染武器信息,则初始化动画相关变量
|
||||
if (!lastTimeRenderingWeapons) {
|
||||
weaponSlotsTimer[weaponIndex].begin();
|
||||
weaponSlotsTimer[weaponIndex].forward(currentTime);
|
||||
if (!wasRenderingWeapons) {
|
||||
weaponSlotsTimer[weaponIndex].beginForward(currentTime);
|
||||
|
||||
if (lastTimeWeaponIndex != weaponIndex) {
|
||||
weaponSlotsTimer[lastTimeWeaponIndex].backward(currentTime);
|
||||
weaponSlotsTimer[lastTimeWeaponIndex].end();
|
||||
if (oldWeaponIndex != weaponIndex) {
|
||||
weaponSlotsTimer[oldWeaponIndex].endBackward(currentTime);
|
||||
|
||||
lastTimeWeaponIndex = weaponIndex;
|
||||
lastTimeRenderWeaponIndex = weaponIndex;
|
||||
oldWeaponIndex = weaponIndex;
|
||||
oldRenderWeaponIndex = weaponIndex;
|
||||
}
|
||||
|
||||
weaponIndexUpdateTimer.begin();
|
||||
weaponIndexUpdateTimer.forward(currentTime);
|
||||
weaponIndexUpdateTimer.beginForward(currentTime);
|
||||
}
|
||||
|
||||
// 切换武器时,更新上一个武器槽位和当前武器槽位的动画信息
|
||||
if (weaponIndex != lastTimeWeaponIndex) {
|
||||
if (weaponIndex != oldWeaponIndex) {
|
||||
weaponSlotsTimer[weaponIndex].forward(currentTime);
|
||||
weaponSlotsTimer[lastTimeWeaponIndex].backward(currentTime);
|
||||
weaponSlotsTimer[oldWeaponIndex].backward(currentTime);
|
||||
|
||||
lastTimeRenderWeaponIndex = lastTimeWeaponIndex;
|
||||
lastTimeWeaponIndex = weaponIndex;
|
||||
oldRenderWeaponIndex = oldWeaponIndex;
|
||||
oldWeaponIndex = weaponIndex;
|
||||
|
||||
weaponIndexUpdateTimer.begin();
|
||||
weaponIndexUpdateTimer.forward(currentTime);
|
||||
weaponIndexUpdateTimer.beginForward(currentTime);
|
||||
}
|
||||
|
||||
var pose = guiGraphics.pose();
|
||||
|
@ -537,7 +533,7 @@ public class VehicleHudOverlay {
|
|||
// 当前选中武器
|
||||
if (weaponIndex == i) {
|
||||
var startY = Mth.lerp(progress,
|
||||
h - (weapons.size() - 1 - lastTimeRenderWeaponIndex) * 18 - 16,
|
||||
h - (weapons.size() - 1 - oldRenderWeaponIndex) * 18 - 16,
|
||||
h - (weapons.size() - 1 - weaponIndex) * 18 - 16
|
||||
);
|
||||
|
||||
|
@ -562,10 +558,10 @@ public class VehicleHudOverlay {
|
|||
pose.popPose();
|
||||
|
||||
// 切换武器光标动画播放结束后,更新上次选择槽位
|
||||
if (lastTimeWeaponIndex != lastTimeRenderWeaponIndex && weaponIndexUpdateTimer.finished(currentTime)) {
|
||||
lastTimeRenderWeaponIndex = lastTimeWeaponIndex;
|
||||
if (oldWeaponIndex != oldRenderWeaponIndex && weaponIndexUpdateTimer.finished(currentTime)) {
|
||||
oldRenderWeaponIndex = oldWeaponIndex;
|
||||
}
|
||||
lastTimeRenderingWeapons = true;
|
||||
wasRenderingWeapons = true;
|
||||
}
|
||||
|
||||
private static void renderNumber(GuiGraphics guiGraphics, int number, boolean percent, float x, float y, float scale) {
|
||||
|
|
|
@ -55,6 +55,6 @@ public class AmmoSupplierItem extends Item {
|
|||
player.displayClientMessage(Component.translatable("item.superbwarfare.ammo_supplier.supply", Component.translatable(this.type.translatableKey), ammoToAdd * count), true);
|
||||
level.playSound(null, player.blockPosition(), ModSounds.BULLET_SUPPLY.get(), SoundSource.PLAYERS, 1, 1);
|
||||
}
|
||||
return InteractionResultHolder.consume(stack);
|
||||
return InteractionResultHolder.success(stack);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ public class AnimationTimer {
|
|||
if (!initialized) {
|
||||
initialized = true;
|
||||
startTime = currentTime + (isStart ? 0 : duration);
|
||||
} else {
|
||||
} else if (reversed) {
|
||||
startTime = currentTime - getElapsedTime(currentTime);
|
||||
}
|
||||
reversed = false;
|
||||
|
@ -181,7 +181,7 @@ public class AnimationTimer {
|
|||
if (!initialized) {
|
||||
initialized = true;
|
||||
startTime = currentTime + (isStart ? duration : 0);
|
||||
} else {
|
||||
} else if (!reversed) {
|
||||
startTime = currentTime + getElapsedTime(currentTime);
|
||||
}
|
||||
reversed = true;
|
||||
|
@ -203,4 +203,8 @@ public class AnimationTimer {
|
|||
backward(currentTime);
|
||||
}
|
||||
|
||||
public float lerp(float start, float end, long currentTime) {
|
||||
return Mth.lerp(getProgress(currentTime), start, end);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
package com.atsuishio.superbwarfare.tools.animation;
|
||||
|
||||
public class DualValueHolder<T> {
|
||||
private T oldValue;
|
||||
private T newValue;
|
||||
|
||||
public static <T> DualValueHolder<T>[] create(int size, T defaultValue) {
|
||||
// 傻逼Java
|
||||
@SuppressWarnings("unchecked")
|
||||
DualValueHolder<T>[] holders = new DualValueHolder[size];
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
holders[i] = new DualValueHolder<>();
|
||||
holders[i].reset(defaultValue);
|
||||
}
|
||||
|
||||
return holders;
|
||||
}
|
||||
|
||||
public void update(T value) {
|
||||
this.oldValue = this.newValue;
|
||||
this.newValue = value;
|
||||
}
|
||||
|
||||
public void reset(T value) {
|
||||
this.oldValue = value;
|
||||
this.newValue = value;
|
||||
}
|
||||
|
||||
public T oldValue() {
|
||||
return this.oldValue;
|
||||
}
|
||||
|
||||
public T newValue() {
|
||||
return this.newValue;
|
||||
}
|
||||
|
||||
public boolean changed() {
|
||||
return this.oldValue.equals(this.newValue);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package com.atsuishio.superbwarfare.tools.animation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* 可以存储额外的新旧数值的AnimationTimer
|
||||
*/
|
||||
public class ValueAnimator<T> extends AnimationTimer {
|
||||
private T oldValue;
|
||||
private T newValue;
|
||||
|
||||
public ValueAnimator(long duration, T defaultValue) {
|
||||
super(duration);
|
||||
reset(defaultValue);
|
||||
}
|
||||
|
||||
public static <T> ValueAnimator<T>[] create(int size, long duration, T defaultValue) {
|
||||
// 傻逼Java
|
||||
@SuppressWarnings("unchecked")
|
||||
ValueAnimator<T>[] animators = (ValueAnimator<T>[]) new ValueAnimator[size];
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
animators[i] = new ValueAnimator<>(duration, defaultValue);
|
||||
}
|
||||
|
||||
return animators;
|
||||
}
|
||||
|
||||
public void update(T value) {
|
||||
this.oldValue = this.newValue;
|
||||
this.newValue = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较当前值和新值,如果不同则更新
|
||||
*
|
||||
* @param value 当前值
|
||||
*/
|
||||
public void compareAndUpdate(T value) {
|
||||
compareAndUpdate(value, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 比较当前值和新值,如果不同则更新
|
||||
*
|
||||
* @param value 当前值
|
||||
* @param callback 更新成功后的回调函数
|
||||
*/
|
||||
public void compareAndUpdate(T value, @Nullable Runnable callback) {
|
||||
if (!this.newValue.equals(value)) {
|
||||
update(value);
|
||||
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reset(T value) {
|
||||
this.oldValue = value;
|
||||
this.newValue = value;
|
||||
}
|
||||
|
||||
public T oldValue() {
|
||||
return this.oldValue;
|
||||
}
|
||||
|
||||
public T newValue() {
|
||||
return this.newValue;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue