添加自定义聊天气泡
约 2346 字大约 8 分钟
2025-01-25
女仆模组提供了强大的聊天气泡系统,允许开发者为女仆添加自定义的聊天气泡。
通过实现相应的接口,你可以创建各种样式的气泡内容,如渲染图片、进度条、乃至各种图表等。
一、快速开始:使用预设气泡
对于大多数简单需求(纯文本、图片、进度条),模组已经提供了开箱即用的预设气泡类型,你可以直接使用,无需自定义:
1. 文本气泡
用于显示简单的文本内容:
public class ExampleUsage {
public static void showTextBubble(EntityMaid maid) {
// 创建文本气泡(使用 TYPE_2 背景样式)
TextChatBubbleData bubbleData = TextChatBubbleData.type2(
Component.literal("这是一条简单的文本消息")
);
// 显示气泡
maid.getChatBubbleManager().addChatBubble(bubbleData);
}
public static void showTextBubbleCustom(EntityMaid maid) {
// 使用 TYPE_1 背景样式
TextChatBubbleData bubbleData = TextChatBubbleData.type1(
Component.literal("使用第一种背景样式")
);
maid.getChatBubbleManager().addChatBubble(bubbleData);
}
public static void showTextBubbleAdvanced(EntityMaid maid) {
// 自定义持续时间、背景和优先级
TextChatBubbleData bubbleData = TextChatBubbleData.create(
200, // 持续时间(tick)
Component.literal("自定义气泡"),
IChatBubbleData.TYPE_2, // 背景样式
5 // 优先级
);
maid.getChatBubbleManager().addChatBubble(bubbleData);
}
}2. 图片气泡
用于显示图片内容:
public class ExampleUsage {
public static void showImageBubble(EntityMaid maid) {
// 图片资源位置
ResourceLocation imageTexture = new ResourceLocation("examplemod", "textures/chat_image.png");
// 创建图片气泡(默认纹理大小为 256x256)
ImageChatBubbleData bubbleData = ImageChatBubbleData.create(
imageTexture,
64, // 显示宽度
64 // 显示高度
);
// 显示气泡
maid.getChatBubbleManager().addChatBubble(bubbleData);
}
public static void showImageBubbleWithOffset(EntityMaid maid) {
ResourceLocation imageTexture = new ResourceLocation("examplemod", "textures/atlas.png");
// 从大图中截取指定区域(适用于纹理图集)
ImageChatBubbleData bubbleData = ImageChatBubbleData.create(
imageTexture,
32, // 显示宽度
32, // 显示高度
64, // U 偏移
128 // V 偏移
);
maid.getChatBubbleManager().addChatBubble(bubbleData);
}
public static void showSingleImageBubble(EntityMaid maid) {
ResourceLocation imageTexture = new ResourceLocation("examplemod", "textures/icon.png");
// 单张独立图片(纹理大小与显示大小相同)
ImageChatBubbleData bubbleData = ImageChatBubbleData.singleImage(
imageTexture,
48, // 宽度
48 // 高度
);
maid.getChatBubbleManager().addChatBubble(bubbleData);
}
}3. 进度条气泡
用于显示进度条,适合展示任务进度、加载状态等:
public class ExampleUsage {
public static void showProgressBubble(EntityMaid maid) {
// 创建进度条气泡
ProgressChatBubbleData bubbleData = ProgressChatBubbleData.create(
Component.literal("工作进度"),
0xFF404040, // 进度条背景色(深灰色)
0xFF00FF00, // 进度条前景色(绿色)
0.5, // 进度(0.0 到 1.0,这里是 50%)
true // 是否居中对齐
);
// 显示气泡
maid.getChatBubbleManager().addChatBubble(bubbleData);
}
public static void showProgressBubbleCustom(EntityMaid maid) {
// 自定义持续时间、背景和优先级
ProgressChatBubbleData bubbleData = ProgressChatBubbleData.create(
300, // 持续时间(tick)
IChatBubbleData.TYPE_2, // 背景样式
2, // 优先级
Component.literal("加载中..."),
0xFF808080, // 进度条背景色(灰色)
0xFFFFAA00, // 进度条前景色(橙色)
0.75, // 进度 75%
false // 左对齐
);
maid.getChatBubbleManager().addChatBubble(bubbleData);
}
}提示
如果以上预设类型能满足你的需求,就无需继续阅读后续的自定义部分。只有当你需要更复杂的气泡效果时,才需要创建自定义气泡。
二、聊天气泡系统概述
如果预设的气泡类型无法满足需求,你可以创建自定义气泡。本例中,我们将创建一个简单的自定义聊天气泡,显示一段自定义的文本内容,并为气泡设置自定义的背景和样式。
女仆模组的聊天气泡系统主要由以下两个核心接口组成:
IChatBubbleData:定义气泡的数据结构,包括持续时间、优先级、同步到客户端时的序列化和反序列化等IChatBubbleRenderer:定义气泡的客户端渲染逻辑,包括尺寸、背景、内容绘制等
1. IChatBubbleData 接口
该接口定义了气泡的数据结构,主要方法如下:
public interface IChatBubbleData {
/**
* 气泡的持续时间
* @return 单位:tick(默认 15 秒 = 300 tick)
*/
int existTick();
/**
* 气泡对应的序列化器的注册 ID
* @return 注册 ID
*/
ResourceLocation id();
/**
* 气泡的优先级,数字越大越高
* @return 优先级(默认为 0)
*/
default int priority() {
return DEFAULT_PRIORITY;
}
/**
* 客户端的渲染类
* @param position 排列位置(LEFT/RIGHT/CENTER)
* @return 渲染类实例
*/
@OnlyIn(Dist.CLIENT)
IChatBubbleRenderer getRenderer(IChatBubbleRenderer.Position position);
}此外,IChatBubbleData 还提供了两种默认的气泡背景:
TYPE_1:第一种气泡背景样式TYPE_2:第二种气泡背景样式(边线较细,推荐使用)
2. 自定义气泡背景
气泡背景采用九宫格机制进行渲染,这种机制可以让气泡背景根据内容大小自适应拉伸,而不会出现变形失真的问题。
你可以参考模组自带的 TYPE_1 和 TYPE_2 背景纹理的设计。
3. IChatBubbleRenderer 接口
该接口定义了气泡的客户端渲染逻辑:
@OnlyIn(Dist.CLIENT)
public interface IChatBubbleRenderer {
/**
* 气泡框高度
* @return 高度(像素)
*/
int getHeight();
/**
* 气泡框宽度
* @return 宽度(像素)
*/
int getWidth();
/**
* 渲染气泡框内部
* @param renderer 女仆实体渲染器
* @param graphics 渲染工具
*/
void render(EntityMaidRenderer renderer, EntityGraphics graphics);
/**
* 获取气泡框背景纹理
* @return 背景纹理资源位置
*/
ResourceLocation getBackgroundTexture();
}三、创建气泡数据类
首先,创建一个实现 IChatBubbleData 接口的类。以下示例创建一个简单的文本气泡:
public class CustomChatBubbleData implements IChatBubbleData {
// 气泡的唯一 ID,用于注册序列化器
public static final ResourceLocation ID = new ResourceLocation("examplemod", "custom");
// 使用模组提供的默认背景
public static final ResourceLocation BG = IChatBubbleData.TYPE_2;
private final Component message;
@OnlyIn(Dist.CLIENT)
private IChatBubbleRenderer renderer;
public CustomChatBubbleData(Component message) {
this.message = message;
}
@Override
public int existTick() {
// 气泡持续 10 秒
return 10 * 20;
}
@Override
public ResourceLocation id() {
return ID;
}
@Override
public int priority() {
// 优先级设为 1,比默认气泡高
return 1;
}
public Component getMessage() {
return message;
}
@Override
@OnlyIn(Dist.CLIENT)
public IChatBubbleRenderer getRenderer(IChatBubbleRenderer.Position position) {
// 懒加载渲染器,避免重复创建
if (renderer == null) {
renderer = new CustomChatBubbleRenderer(this, BG, position);
}
return renderer;
}
/**
* 序列化器,用于服务端与客户端之间同步数据
*
* 注意:1.21.1 此处网络同步更换为 Codec,以下示例代码仅供 1.20.1 使用
*/
public static class CustomChatSerializer implements IChatBubbleData.ChatSerializer {
@Override
public IChatBubbleData readFromBuff(FriendlyByteBuf buf) {
// 从网络数据包中读取 Component
return new CustomChatBubbleData(buf.readComponent());
}
@Override
public void writeToBuff(FriendlyByteBuf buf, IChatBubbleData data) {
// 将 Component 写入网络数据包
CustomChatBubbleData bubbleData = (CustomChatBubbleData) data;
buf.writeComponent(bubbleData.message);
}
}
}提示
- 序列化器不需要同步
existTick和priority,这两个参数仅在服务端有效 getRenderer方法中建议使用懒加载模式,避免重复创建渲染器实例
四、创建气泡渲染类
接下来,创建一个实现 IChatBubbleRenderer 接口的渲染类:
@OnlyIn(Dist.CLIENT)
public class CustomChatBubbleRenderer implements IChatBubbleRenderer {
private static final int MAX_WIDTH = 180;
private final List<FormattedCharSequence> lines;
private final Font font;
private final int width;
private final int height;
private final ResourceLocation bg;
private final Position position;
public CustomChatBubbleRenderer(CustomChatBubbleData data,
ResourceLocation bg,
Position position) {
this.font = Minecraft.getInstance().font;
this.bg = bg;
this.position = position;
// 将文本按最大宽度分割成多行
this.lines = font.split(data.getMessage(), MAX_WIDTH);
// 计算气泡宽度(取所有行中最宽的)
this.width = getMaxWidth(lines);
// 计算气泡高度(行数 × 行高 + 边距)
this.height = lines.size() * (font.lineHeight + 2);
}
private int getMaxWidth(List<FormattedCharSequence> lines) {
int maxWidth = 0;
for (FormattedCharSequence line : lines) {
int lineWidth = font.width(line);
if (lineWidth > maxWidth) {
maxWidth = lineWidth;
}
}
return maxWidth;
}
@Override
public int getHeight() {
// 返回气泡高度 + 上下边距
return this.height + 8;
}
@Override
public int getWidth() {
// 返回气泡宽度 + 左右边距
return this.width + 8;
}
@Override
public void render(EntityMaidRenderer renderer, EntityGraphics graphics) {
// 绘制文本内容
int y = 4;
for (FormattedCharSequence line : this.lines) {
// 居中对齐
int x = (this.getWidth() - font.width(line)) / 2;
graphics.drawString(font, line, x, y, 0x000000, false);
y += font.lineHeight + 2;
}
}
@Override
public ResourceLocation getBackgroundTexture() {
return this.bg;
}
}渲染说明
getWidth()和getHeight()返回的是气泡的总尺寸,包括边距render()方法中可以使用graphics.blit()绘制纹理,graphics.drawString()绘制文本
五、注册气泡序列化器
完成数据类和渲染类后,需要在 LittleMaidCompat 类中注册序列化器:
@LittleMaidExtension
public class LittleMaidCompat implements ILittleMaid {
@Override
public void registerChatBubble(ChatBubbleRegister register) {
// 注册自定义气泡的序列化器
// 注意 1.21.1 版本更换为 Codec,以下代码仅供 1.20.1 使用
register.register(CustomChatBubbleData.ID, new CustomChatBubbleData.CustomChatSerializer());
}
}六、使用自定义气泡
注册完成后,你可以在服务端,通过女仆实体的 getChatBubbleManager().addChatBubble() 方法来显示自定义气泡:
public class ExampleUsage {
public static void showCustomBubble(EntityMaid maid) {
// 创建气泡数据
CustomChatBubbleData bubbleData = new CustomChatBubbleData(
Component.literal("Hello, World!")
);
// 显示气泡
maid.getChatBubbleManager().addChatBubble(bubbleData);
}
}七、高级示例
1. 自定义字体气泡
你可以为气泡设置自定义字体,同时还需要在资源文件夹中添加字体资源,具体资源包制作可参考 Minecraft Wiki。
public static final ResourceLocation CUSTOM_FONT = new ResourceLocation("examplemod", "custom_font");
public static CustomChatBubbleData create(String text) {
Component component = Component.literal(text)
.setStyle(Style.EMPTY
.withFont(CUSTOM_FONT)
.withColor(ChatFormatting.DARK_GRAY));
return new CustomChatBubbleData(component);
}2. 带图片的气泡
如果需要在气泡中显示图片,可以在渲染器中使用 graphics.blit() 方法:
@Override
public void render(EntityMaidRenderer renderer, EntityGraphics graphics) {
// 绘制图片
ResourceLocation imageTexture = new ResourceLocation("examplemod", "textures/bubble_image.png");
int imageWidth = 64;
int imageHeight = 64;
graphics.blit(imageTexture,
(getWidth() - imageWidth) / 2, 4, // x, y
0, 0, // u, v
imageWidth, imageHeight, // width, height
imageWidth, imageHeight); // texture width, texture height
// 绘制文本
// ...
}3. 动态背景纹理
你可以根据气泡内容或位置选择不同的背景:
public CustomChatBubbleData(Component message, ResourceLocation bg) {
this.message = message;
this.bg = bg;
}
@Override
@OnlyIn(Dist.CLIENT)
public IChatBubbleRenderer getRenderer(IChatBubbleRenderer.Position position) {
if (renderer == null) {
// 根据位置选择不同背景
ResourceLocation background = position == Position.CENTER
? IChatBubbleData.TYPE_1
: this.bg;
renderer = new CustomChatBubbleRenderer(this, background, position);
}
return renderer;
}八、完整示例参考
你可以参考林间狐咏模组自带的多种聊天气泡实现:
TipsChatBubbleData:简单的文本提示气泡WordsChatBubbleData:多字体、多行文本气泡VersesChatBubbleData:诗词气泡LaTexChatBubbleData:数学公式气泡
这些示例展示了不同复杂度的气泡实现方式,可以作为你开发自定义气泡的参考。
