SubtitleOverlay.java
net.minecraft.client.gui.components.SubtitleOverlay
信息
- 全限定名:net.minecraft.client.gui.components.SubtitleOverlay
- 类型:public class
- 包:net.minecraft.client.gui.components
- 源码路径:src/main/java/net/minecraft/client/gui/components/SubtitleOverlay.java
- 起始行号:L25
- 实现:SoundEventListener
- 职责:
TODO
字段/常量
-
DISPLAY_TIME- 类型:
long - 修饰符:
private static final - 源码定位:
L26 - 说明:
TODO
- 类型:
-
minecraft- 类型:
Minecraft - 修饰符:
private final - 源码定位:
L27 - 说明:
TODO
- 类型:
-
subtitles- 类型:
List<SubtitleOverlay.Subtitle> - 修饰符:
private final - 源码定位:
L28 - 说明:
TODO
- 类型:
-
isListening- 类型:
boolean - 修饰符:
private - 源码定位:
L29 - 说明:
TODO
- 类型:
-
audibleSubtitles- 类型:
List<SubtitleOverlay.Subtitle> - 修饰符:
private final - 源码定位:
L30 - 说明:
TODO
- 类型:
内部类/嵌套类型
-
net.minecraft.client.gui.components.SubtitleOverlay.SoundPlayedAt- 类型:
record - 修饰符:
package-private - 源码定位:
L137 - 说明:
TODO
- 类型:
-
net.minecraft.client.gui.components.SubtitleOverlay.Subtitle- 类型:
class - 修饰符:
static - 源码定位:
L141 - 说明:
TODO
- 类型:
构造器
public SubtitleOverlay(Minecraft minecraft) @ L32
- 构造器名:SubtitleOverlay
- 源码定位:L32
- 修饰符:public
参数:
- minecraft: Minecraft
说明:
TODO
方法
下面的方法块按源码顺序生成。
public void extractRenderState(GuiGraphicsExtractor graphics) @ L36
- 方法名:extractRenderState
- 源码定位:L36
- 返回类型:void
- 修饰符:public
参数:
- graphics: GuiGraphicsExtractor
说明:
TODO
public void onPlaySound(SoundInstance sound, WeighedSoundEvents soundEvent, float range) @ L119
- 方法名:onPlaySound
- 源码定位:L119
- 返回类型:void
- 修饰符:public
参数:
- sound: SoundInstance
- soundEvent: WeighedSoundEvents
- range: float
说明:
TODO
代码
@OnlyIn(Dist.CLIENT)
public class SubtitleOverlay implements SoundEventListener {
private static final long DISPLAY_TIME = 3000L;
private final Minecraft minecraft;
private final List<SubtitleOverlay.Subtitle> subtitles = Lists.newArrayList();
private boolean isListening;
private final List<SubtitleOverlay.Subtitle> audibleSubtitles = new ArrayList<>();
public SubtitleOverlay(Minecraft minecraft) {
this.minecraft = minecraft;
}
public void extractRenderState(GuiGraphicsExtractor graphics) {
SoundManager soundManager = this.minecraft.getSoundManager();
if (!this.isListening && this.minecraft.options.showSubtitles().get()) {
soundManager.addListener(this);
this.isListening = true;
} else if (this.isListening && !this.minecraft.options.showSubtitles().get()) {
soundManager.removeListener(this);
this.isListening = false;
}
if (this.isListening) {
ListenerTransform listener = soundManager.getListenerTransform();
Vec3 position = listener.position();
Vec3 forwards = listener.forward();
Vec3 right = listener.right();
this.audibleSubtitles.clear();
for (SubtitleOverlay.Subtitle subtitle : this.subtitles) {
if (subtitle.isAudibleFrom(position)) {
this.audibleSubtitles.add(subtitle);
}
}
if (!this.audibleSubtitles.isEmpty()) {
int row = 0;
int width = 0;
double displayTimeMultiplier = this.minecraft.options.notificationDisplayTime().get();
Iterator<SubtitleOverlay.Subtitle> iterator = this.audibleSubtitles.iterator();
while (iterator.hasNext()) {
SubtitleOverlay.Subtitle subtitlex = iterator.next();
subtitlex.purgeOldInstances(3000.0 * displayTimeMultiplier);
if (!subtitlex.isStillActive()) {
iterator.remove();
} else {
width = Math.max(width, this.minecraft.font.width(subtitlex.getText()));
}
}
width += this.minecraft.font.width("<") + this.minecraft.font.width(" ") + this.minecraft.font.width(">") + this.minecraft.font.width(" ");
if (!this.audibleSubtitles.isEmpty()) {
graphics.nextStratum();
}
for (SubtitleOverlay.Subtitle subtitlex : this.audibleSubtitles) {
int alpha = 255;
Component text = subtitlex.getText();
SubtitleOverlay.SoundPlayedAt closestRecentLocation = subtitlex.getClosest(position);
if (closestRecentLocation != null) {
Vec3 delta = closestRecentLocation.location.subtract(position).normalize();
double rightness = right.dot(delta);
double forwardness = forwards.dot(delta);
boolean inView = forwardness > 0.5;
int halfWidth = width / 2;
int height = 9;
int halfHeight = height / 2;
float scale = 1.0F;
int textWidth = this.minecraft.font.width(text);
int brightness = Mth.floor(
Mth.clampedLerp((float)(Util.getMillis() - closestRecentLocation.time) / (float)(3000.0 * displayTimeMultiplier), 255.0F, 75.0F)
);
graphics.pose().pushMatrix();
graphics.pose().translate(graphics.guiWidth() - halfWidth * 1.0F - 2.0F, graphics.guiHeight() - 35 - row * (height + 1) * 1.0F);
graphics.pose().scale(1.0F, 1.0F);
graphics.fill(-halfWidth - 1, -halfHeight - 1, halfWidth + 1, halfHeight + 1, this.minecraft.options.getBackgroundColor(0.8F));
int textColor = ARGB.color(255, brightness, brightness, brightness);
if (!inView) {
if (rightness > 0.0) {
graphics.text(this.minecraft.font, ">", halfWidth - this.minecraft.font.width(">"), -halfHeight, textColor);
} else if (rightness < 0.0) {
graphics.text(this.minecraft.font, "<", -halfWidth, -halfHeight, textColor);
}
}
graphics.text(this.minecraft.font, text, -textWidth / 2, -halfHeight, textColor);
graphics.pose().popMatrix();
row++;
}
}
}
}
}
@Override
public void onPlaySound(SoundInstance sound, WeighedSoundEvents soundEvent, float range) {
if (soundEvent.getSubtitle() != null) {
Component text = soundEvent.getSubtitle();
if (!this.subtitles.isEmpty()) {
for (SubtitleOverlay.Subtitle subtitle : this.subtitles) {
if (subtitle.getText().equals(text)) {
subtitle.refresh(new Vec3(sound.getX(), sound.getY(), sound.getZ()));
return;
}
}
}
this.subtitles.add(new SubtitleOverlay.Subtitle(text, range, new Vec3(sound.getX(), sound.getY(), sound.getZ())));
}
}
@OnlyIn(Dist.CLIENT)
record SoundPlayedAt(Vec3 location, long time) {
}
@OnlyIn(Dist.CLIENT)
static class Subtitle {
private final Component text;
private final float range;
private final List<SubtitleOverlay.SoundPlayedAt> playedAt = new ArrayList<>();
public Subtitle(Component text, float range, Vec3 location) {
this.text = text;
this.range = range;
this.playedAt.add(new SubtitleOverlay.SoundPlayedAt(location, Util.getMillis()));
}
public Component getText() {
return this.text;
}
public SubtitleOverlay.@Nullable SoundPlayedAt getClosest(Vec3 position) {
if (this.playedAt.isEmpty()) {
return null;
} else {
return this.playedAt.size() == 1
? this.playedAt.getFirst()
: this.playedAt.stream().min(Comparator.comparingDouble(soundPlayedAt -> soundPlayedAt.location().distanceTo(position))).orElse(null);
}
}
public void refresh(Vec3 location) {
this.playedAt.removeIf(soundPlayedAt -> location.equals(soundPlayedAt.location()));
this.playedAt.add(new SubtitleOverlay.SoundPlayedAt(location, Util.getMillis()));
}
public boolean isAudibleFrom(Vec3 camera) {
if (Float.isInfinite(this.range)) {
return true;
} else if (this.playedAt.isEmpty()) {
return false;
} else {
SubtitleOverlay.SoundPlayedAt closest = this.getClosest(camera);
return closest == null ? false : camera.closerThan(closest.location, this.range);
}
}
public void purgeOldInstances(double maxAge) {
long currentTime = Util.getMillis();
this.playedAt.removeIf(soundPlayedAt -> currentTime - soundPlayedAt.time() > maxAge);
}
public boolean isStillActive() {
return !this.playedAt.isEmpty();
}
}
}引用的其他类
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
实现
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
ARGB.color()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Mth.clampedLerp(), Mth.floor()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Util.getMillis()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
Vec3()
- 引用位置: