MCBBS Wiki欢迎您共同参与编辑!在参与编辑之前请先阅读Wiki方针

如果在编辑的过程中遇到了什么问题,可以去讨论板提问。

为了您能够无阻碍地参与编辑 未验证/绑定过邮箱的用户,请尽快绑定/验证

MCBBS Wiki GitHub群组已上线!

您可以在回声洞中发表吐槽!

服务器状态监控。点击进入

本站由MCBBS用户自行搭建,与MCBBS及东银河系漫游指南(北京)科技有限公司没有从属关系。点此了解 MCBBS Wiki 不是什么>>

FancyMenu Wiki/自定义菜单背景

来自MCBBS Wiki
Litwak913留言 | 贡献2022年11月22日 (二) 16:45的版本 (// Edit via Wikiplus)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳到导航 跳到搜索

关于

你可以使用FancyMenu编写你自己的自定义背景拓展模组,而且你可以在布局中使用它们。

重要:Menu Background API只在FancyMenu v2.6.2+可用!

准备开发环境

请参阅 准备工作区

添加菜单背景

每一个菜单背景都系要两个类。

第一个是MenuBackgroundType

这个是你的背景类型(比如已经存在的动画,或是全景图)

第二个就是MenuBackground

这个类是你的菜单背景实例。MenuBackgroundTypes Hold并创建MenuBackgrounds,从而它们可以在布局上渲染出来。

在模组初始化时,每一个MenuBackgroundType都需要被注册到MenuBackgroundTypeRegistry

不同样式的背景

注意捏,菜单背景有两种基础样式。

第一种(普通模式)会在模组启动时初始化一组MenuBackground实例,便于用户可以从这组背景中进行选择。

第二种(输入字符串模式)选不了实例,但可以输入一个字符串创建新实例。

用来构建实例的输入字符串没有限制,它可以是文件路径或者1w字符长度的字符串。

用户可以点击一个"input string button"来输入输入字符串。

按钮也可以高度自定义化,所以你可以你想要的打开任意类型的设置菜单。

创建普通(不需输入字符串)背景

普通的MenuBackgroundTypes在游戏开始时会初始化一组MenuBackground实例供用户选择。

MenuBackground Class

创建一个MenuBackground类的新子类并取一个合适的名字。

我会将我的演示类命名为ExampleMenuBackground

接下来是一下需要特别注意的东西。

构造方法(Constructor)

这里的构造方法需要你为你的背景定义个唯一的标识符。

它也可以用于在构造中设定变量。

public ExampleMenuBackground(@Nonnull String uniqueBackgroundIdentifier, @Nonnull MenuBackgroundType type, Color color) {
    //The identifier needs to be UNIQUE!
    //标识符就像sfz号一样是独一无二的。
    //It's not possible to register multiple backgrounds with the same identifier to the same background type.
    //咱这不可能用相同的标识符注册相同类型的多个背景。
    super(uniqueBackgroundIdentifier, type);
    //Custom color variable. Will get rendered as background later.
    //自定义色彩变量,之后会在背景上渲染。
    this.color = color;
}
onOpenMenu()

每当菜单打开时都会调用这个方法。

这个方法只有在打开菜单时会调用(切换窗口也是),调整窗口大小时不会调用。

使用这个方法可以重置在打开新菜单时应该重新加载的实例内容。

@Override
public void onOpenMenu() {
    //Gets called when opening a NEW menu (not when resizing it).
    //在打开窗口时调用(并非是在调整窗口大小时调用)
    //If you want to reset stuff of your background instance when the menu changes, do it here.
    //如果你想在菜单更改时重置你的背景示例,那么请在此处进行。
}
render()

调用这个方法渲染你的菜单背景实例。

其实不用我废话对吧?

//Here you will render the background instance.
//在这里你可以渲染背景实例。
//You should always render backgrounds over the full size of the screen, otherwise it will look ugly.
//你应该全屏渲染背景,否则看着会很丑。
@Override
public void render(PoseStack matrix, Screen screen, boolean keepAspectRatio) {

    try {

        //Simply renders a colored background to the full size of the screen it is rendered in.
        //只需要把彩色背景渲染到全屏。
        //We will ignore the keepAspectRatio param here, because, well, a simple colored background has no aspect ratio.
        //我们在这里忽略keepAspectRatio参数,因为简单的彩色背景没有长宽比。
        GuiComponent.fill(matrix, 0, 0, screen.width, screen.height, this.color.getRGB());

    } catch (Exception e) {
        e.printStackTrace();
    }

}
完整演示类

MenuBackground的完整样例。

package de.keksuccino.fancymenu.api.background.example.no_input_string;

import com.mojang.blaze3d.vertex.PoseStack;
import de.keksuccino.fancymenu.api.background.MenuBackground;
import de.keksuccino.fancymenu.api.background.MenuBackgroundType;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.screens.Screen;

import javax.annotation.Nonnull;
import java.awt.*;

//This is an example menu background that simply renders a color.
//这是一个非常简单只渲染颜色的菜单背景。
//The color can be set on construction.
//颜色可以在构造中设置。
public class ExampleMenuBackground extends MenuBackground {

    protected Color color;

    public ExampleMenuBackground(@Nonnull String uniqueBackgroundIdentifier, @Nonnull MenuBackgroundType type, Color color) {
        //The identifier needs to be UNIQUE!
        //标识符独一无二!
        //It's not possible to register multiple backgrounds with the same identifier to the same background type.
        //别想着用同一个标识符命名多个相同类型的背景!
        super(uniqueBackgroundIdentifier, type);
        //Will get rendered as background.
        //会被渲染成背景。
        this.color = color;
    }

    @Override
    public void onOpenMenu() {
        //Gets called when opening a NEW menu (not when resizing it).
        //打开新菜单时调用(调整大小时不会)
        //If you want to reset stuff of your background instance when the menu changes, do it here.
        //如果你想在菜单更改时重置你的背景示例,那么请在此处进行。
    }

    //Here you will render the background instance.
    //在这里你可以渲染背景实例。
    //You should always render backgrounds over the full size of the screen, otherwise it will look ugly.
    //你应该全屏渲染背景,否则看着会很丑。
    @Override
    public void render(PoseStack matrix, Screen screen, boolean keepAspectRatio) {

        try {

            //Simply renders a colored background to the full size of the screen it is rendered in.
            //只需要把彩色背景渲染到全屏。
            //We will ignore the keepAspectRatio param here, because, well, a simple colored background has no aspect ratio.
            //我们在这里忽略keepAspectRatio参数,因为简单的彩色背景没有长宽比。
            GuiComponent.fill(matrix, 0, 0, screen.width, screen.height, this.color.getRGB());

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

MenuBackgroundType Class

创建一个MenuBackgroundType的新子类并取一个合适的名字。

接下来,我会将我的示例子类命名为ExampleMenuBackgroundType

这里有些需要特别注意的点。

构造方法(Constructor)

构造方法与独一无二的标识符。

public ExampleMenuBackgroundType() {
    //This identifier needs to be UNIQUE! It is not possible to register multiple types with the same identifier.
    //标识符独一无二!别想着用同一个标识符命名多个类型!
    super("example_type_no_input_string");
}
loadBackgrounds()

调用此方法以加载或重载MenuBackgroundType的一组MenuBackground实例。

//In normal mode (when not using input strings), this is the place you register all of the background instances for the type.
//在正常模式下(不使用输入字符串时),这是你为此类型注册所有背景实例的地方。
//This method gets called on game start and when the reload button gets pressed.
//此方法在游戏开始和按下重载按钮时调用。
//For example, when you want to use a directory where users can put their background properties, like FancyMenu does for animations and slideshows,
//例如,当你想使用一个目录,用户可以把他们的背景属性放在那里,就像FancyMenu为动画和幻灯片做的那样。
//load all backgrounds from this directory here.
//从这个目录加载所有北京。
@Override
public void loadBackgrounds() {

    //Clear the loaded backgrounds first.
    //先清楚加载的背景。
    this.backgrounds.clear();

    //Background identifiers need to be UNIQUE when registering backgrounds!
    //在注册背景时,背景标识符应保持唯一。
    //It is not possible to register multiple backgrounds with the same identifier!
    //暂不可能用同一个标识符注册多个背景。
    //In this case I just directly register a set of background instances,
    //在这种情况下,我只是直接注册一组背景实例。
    //but you can also use this (for example) to load stored background properties, etc.
    //但你也可以使用它(只是举个例子)加载储存的背景属性等。
    this.addBackground(new ExampleMenuBackground("green_background", this, new Color(117, 245, 66)));
    this.addBackground(new ExampleMenuBackground("blue_background", this, new Color(66, 126, 245)));
    this.addBackground(new ExampleMenuBackground("orange_background", this, new Color(245, 164, 51)));

}
getDisplayName()

调用此方法获取背景类型名。

名称显示在布局编辑器的背景选项中。

//You don't really have much space for the display name, so try to choose a short one ond explain the type further in the description.
//显示名字的空间很挤,你的类型名字简短点吧,在描述里详细说明就好。
@Override
public String getDisplayName() {
    return "Example Type No Input";
}
getDescription()

获取背景类型的描述。

在布局编辑器的背景选项中显示。

//Gets displayed when hovering over the type switcher in the background options menu in the layout editor.
//在布局编辑器的背景选项菜单中悬停在类型切换器上时显示。
//This is great for telling users everything important about your background type!
//这对于告诉用户你的背景类型的所有重要信息非常有用!
@Override
public List<String> getDescription() {
    List<String> l = new ArrayList<>();
    l.add("This background type has a set");
    l.add("of backgrounds to choose from.");
    l.add("It doesn't use input strings.");
    return l;
}
needsInputString()

这是最重要的方法,因为你得在这选择背景类型模式。

在这里,我们返回false,因为想为我们的背景类型使用普通模式。

返回true的话可以把背景类型设置为输入字符串模式。

@Override
public boolean needsInputString() {
    //Return false, because we don't use input strings for this type.
    //这里返回false,因为咱不在这个类型使用输入字符串模式。
    return false;
}
createInstanceFromInputString()

这个方法只有在背景类型设置为输入字符串模式时才会用到。

这里不是这种情况,所以直接返回null就行。

@Override
public MenuBackground createInstanceFromInputString(String inputString) {
    //Return null, because we don't use input strings for this type.
    //返回null,因为咱的类型不使用输入字符串模式。
    return null;
}
完整类样例

完整的MenuBackgroundType样例。

package de.keksuccino.fancymenu.api.background.example.no_input_string;

import de.keksuccino.fancymenu.api.background.MenuBackground;
import de.keksuccino.fancymenu.api.background.MenuBackgroundType;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;

//This is a background type that doesn't use an input string.
//这个背景类型不使用输入字符串模式。
//It doesn't create background instances on-the-fly, but has a set of loaded instances to choose from.
//它并不即时创建背景实例,而是有一组加载的实例供其选择。
public class ExampleMenuBackgroundType extends MenuBackgroundType {

    public ExampleMenuBackgroundType() {
        //This identifier needs to be UNIQUE! It is not possible to register multiple types with the same identifier.
        //标识符独一无二!别想着用同一个标识符命名多个类型!
        super("example_type_no_input_string");
    }

    //In normal mode (when not using input strings), this is the place you register all of the background instances for the type.
    //在正常模式下(不使用输入字符串时),这是你为此类型注册所有背景实例的地方。
    //This method gets called on game start and when the reload button gets pressed.
    //此方法在游戏开始和按下重载按钮时调用。
    //For example, when you want to use a directory where users can put their background properties, like FancyMenu does for animations and slideshows,
    //例如,当你想使用一个目录,用户可以把他们的背景属性放在那里,就像FancyMenu为动画和幻灯片做的那样。
    //load all backgrounds from this directory here.
    //从这个目录加载所有背景。
    @Override
    public void loadBackgrounds() {

        //Clear the loaded backgrounds first.
        //先清楚加载的背景。
        this.backgrounds.clear();

        //Background identifiers need to be UNIQUE when registering backgrounds!
        //在注册背景时,背景标识符应保持唯一。
        //It is not possible to register multiple backgrounds with the same identifier!
        //暂不可能用同一个标识符注册多个背景。
        //In this case I just directly register a set of background instances,
        //在这种情况下,我只是直接注册一组背景实例。
        //but you can also use this (for example) to load stored background properties, etc.
        //但你也可以使用它(只是举个例子)加载储存的背景属性等。
        this.addBackground(new ExampleMenuBackground("green_background", this, new Color(117, 245, 66)));
        this.addBackground(new ExampleMenuBackground("blue_background", this, new Color(66, 126, 245)));
        this.addBackground(new ExampleMenuBackground("orange_background", this, new Color(245, 164, 51)));

    }

    //You don't really have much space for the display name, so try to choose a short one ond explain the type further in the description.
    //显示名字的空间很挤,你的类型名字简短点吧,在描述里详细说明就好。
    @Override
    public String getDisplayName() {
        return "Example Type No Input";
    }

    //Gets displayed when hovering over the type switcher in the background options menu in the layout editor.
    //在布局编辑器的背景选项菜单中悬停在类型切换器上时显示。
    //This is great for telling users everything important about your background type!
    //这对于告诉用户你的背景类型的所有重要信息非常有用!
    @Override
    public List<String> getDescription() {
        List<String> l = new ArrayList<>();
        l.add("This background type has a set");
        l.add("of backgrounds to choose from.");
        l.add("It doesn't use input strings.");
        return l;
    }

    @Override
    public boolean needsInputString() {
        //Return false, because we don't use input strings for this type.
        //这里返回false,因为咱不在这个类型使用输入字符串模式。
        return false;
    }

    @Override
    public MenuBackground createInstanceFromInputString(String inputString) {
        //Return null, because we don't use input strings for this type.
        //返回null,因为咱的类型不使用输入字符串模式。
        return null;
    }
}

注册菜单背景

你就快完成了!

现在你只需要在游戏加载时把你的MenuBackgroundType注册到MenuBackgroundTypeRegistry,这样你的新背景就可以用了!

package de.keksuccino.fancymenu;

import net.minecraftforge.fml.common.Mod;
import de.keksuccino.fancymenu.api.background.MenuBackgroundTypeRegistry;

@Mod("modid")
public class ExampleModMainClass {

    public ExampleModMainClass() {
        try {

            //Register your MenuBackgroundType to the MenuBackgroundTypeRegistry at mod init.
            //在模组初始化时注册MenuBackgroundType到MenuBackgroundTypeRegistry。
            MenuBackgroundTypeRegistry.registerBackgroundType(new ExampleMenuBackgroundType());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

创建字符串模式背景

输入字符串模式下的MenuBackgroundTypes从一个输入字符串中快速创建MenuBackground实例。

这些实例并不会想普通模式会被储存下来,而是每次布局被加载时新的实例也会被创建。

MenuBackground Class

创建一个MenuBackground的新子类并取一个合适的名字

接下来我会给我的示例类起名为ExampleMenuBackgroundForInputString

这里有一些需要特别注意的事项。

构造方法(Constructor)

和普通模式不一样,你不需要为你的背景整一个唯一的标识符,因为它们并不会被储存下来。在输入字符串模式下,你为MenuBackground实例使用什么标识符基本上并不重要。

当然,这里仍然可以在构造上设置变量。

public ExampleMenuBackgroundForInputString(@Nonnull MenuBackgroundType type, String imagePath) {
    //Identifiers aren't really used for backgrounds that don't get registered to a type (because the type uses the input string),
    //标识符并不真正用于没有被注册到类型的背景。(因为这个类型使用输入字符串模式)
    //so just set something random here.
    //所以随便在这写点什么就行。
    super("unused_identifier", type);

    //Check if the image exists and has the correct file type, then load it.
    //检查文件文件类型,文件是否存在。确认无误后加载即可。
    File imageFile = new File(imagePath);
    if (imageFile.exists() && (imageFile.getPath().toLowerCase().endsWith(".jpg") || imageFile.getPath().toLowerCase().endsWith(".jpeg") || imageFile.getPath().toLowerCase().endsWith(".png"))) {
        this.imageLocation = TextureHandler.getResource(imageFile.getPath());
        if (this.imageLocation != null) {
            this.imageLocation.loadTexture();
        }
    }

}
onOpenMenu()

这个方法对于输入字符串模式的背景没用,因为在每一次加载菜单时总会创建新的实例,所以这没啥可重置的。

@Override
public void onOpenMenu() {
    //Empty because everytime the menu gets opened, a new instance of the background will be created
    //这里为空是因为在每一次加载菜单时总会创建新的背景实例
    //when using input strings, so you don't need to reset stuff.
    //所以在使用输入字符串模式时你不需要重置。
}
render()

调用此方法来渲染你的菜单背景实例。

应该指我解释一下在这里做什么,对吧?

//Here you will render the background instance.
//在这里你可以渲染背景实例。
//You should always render backgrounds over the full size of the screen, otherwise it will look ugly.
//你应该全屏渲染背景,否则看着会很丑。
@Override
public void render(PoseStack matrix, Screen screen, boolean keepAspectRatio) {

    try {

        //Check if the image location is ready to get rendered
        //检查图像位置是否可以准备渲染。
        if ((this.imageLocation != null) && this.imageLocation.isReady()) {

            RenderSystem.enableBlend();
            RenderUtils.bindTexture(this.imageLocation.getResourceLocation());
            RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);

            //If the keep-aspect-ratio toggle is disabled, just stretch the image background to the full size of the screen it is rendered in
            //如果禁用了保持长宽比切换,那么只将图像背景拉伸到全屏的渲染尺寸即可。
            if (!keepAspectRatio) {

                GuiComponent.blit(matrix, 0, 0, 1.0F, 1.0F, screen.width, screen.height, screen.width, screen.height);

            //If the background image should keep its aspect ratio, try to keep the aspect ratio as long as possible.
            //如果背景图片应保持其长宽比,则尽量保持其长宽比。
            //As soon as it's not possible to keep the aspect ratio anymore, just stretch it.
            //如果不能保持长宽比那就拉伸。
            } else {

                int w = this.imageLocation.getWidth();
                int h = this.imageLocation.getHeight();
                double ratio = (double) w / (double) h;
                int wfinal = (int)(screen.height * ratio);
                int screenCenterX = screen.width / 2;
                if (wfinal < screen.width) {
                    GuiComponent.blit(matrix, 0, 0, 1.0F, 1.0F, screen.width, screen.height, screen.width, screen.height);
                } else {
                    GuiComponent.blit(matrix, screenCenterX - (wfinal / 2), 0, 1.0F, 1.0F, wfinal, screen.height, wfinal, screen.height);
                }

            }

        }

    } catch (Exception e) {
        e.printStackTrace();
    }

}
完整样例类

完整的MenuBackground示例。

package de.keksuccino.fancymenu.api.background.example.with_input_string;

import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import de.keksuccino.fancymenu.api.background.MenuBackground;
import de.keksuccino.fancymenu.api.background.MenuBackgroundType;
import de.keksuccino.konkrete.rendering.RenderUtils;
import de.keksuccino.konkrete.resources.ExternalTextureResourceLocation;
import de.keksuccino.konkrete.resources.TextureHandler;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.screens.Screen;

import javax.annotation.Nonnull;
import java.io.File;

//This is an example menu background that renders an external image.
//这是一个渲染外部图像的菜单背景的例子。
//It gets created by a background type that uses an input string to get the image file.
//它由使用输入字符串获取图片文件的背景创建。
public class ExampleMenuBackgroundForInputString extends MenuBackground {

    private ExternalTextureResourceLocation imageLocation = null;

    public ExampleMenuBackgroundForInputString(@Nonnull MenuBackgroundType type, String imagePath) {
        //Identifiers aren't really used for backgrounds that don't get registered to a type (because the type uses the input string),
        //标识符并不真正用于没有被注册到类型的背景。(因为这个类型使用输入字符串模式)
        //so just set something random here.
        //所以随便在这写点什么就行。
        super("unused_identifier", type);

        //Check if the image exists and has the correct file type, then load it.
        //检查文件文件类型,文件是否存在。确认无误后加载即可。
        File imageFile = new File(imagePath);
        if (imageFile.exists() && (imageFile.getPath().toLowerCase().endsWith(".jpg") || imageFile.getPath().toLowerCase().endsWith(".jpeg") || imageFile.getPath().toLowerCase().endsWith(".png"))) {
            this.imageLocation = TextureHandler.getResource(imageFile.getPath());
            if (this.imageLocation != null) {
                this.imageLocation.loadTexture();
            }
        }

    }

    @Override
    public void onOpenMenu() {
        //Empty because everytime the menu gets opened, a new instance of the background will be created
        //这里为空是因为在每一次加载菜单时总会创建新的背景实例
        //when using input strings, so you don't need to reset stuff.
        //所以在使用输入字符串模式时你不需要重置。
    }

    //Here you will render the background instance.
    //在这里你可以渲染背景实例。
    //You should always render backgrounds over the full size of the screen, otherwise it will look ugly.
    //你应该全屏渲染背景,否则看着会很丑。
    @Override
    public void render(PoseStack matrix, Screen screen, boolean keepAspectRatio) {

        try {

            //Check if the image location is ready to get rendered
            //检查图像位置是否可以准备渲染。
            if ((this.imageLocation != null) && this.imageLocation.isReady()) {

                RenderSystem.enableBlend();
                RenderUtils.bindTexture(this.imageLocation.getResourceLocation());
                RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);

                //If the keep-aspect-ratio toggle is disabled, just stretch the image background to the full size of the screen it is rendered in
                //如果禁用了保持长宽比切换,那么只将图像背景拉伸到全屏的渲染尺寸即可。
                if (!keepAspectRatio) {

                    GuiComponent.blit(matrix, 0, 0, 1.0F, 1.0F, screen.width, screen.height, screen.width, screen.height);

                //If the background image should keep its aspect ratio, try to keep the aspect ratio as long as possible.
                //如果背景图片应保持其长宽比,则尽量保持其长宽比。
                //As soon as it's not possible to keep the aspect ratio anymore, just stretch it.
                //如果不能保持长宽比那就拉伸。
                } else {

                    int w = this.imageLocation.getWidth();
                    int h = this.imageLocation.getHeight();
                    double ratio = (double) w / (double) h;
                    int wfinal = (int)(screen.height * ratio);
                    int screenCenterX = screen.width / 2;
                    if (wfinal < screen.width) {
                        GuiComponent.blit(matrix, 0, 0, 1.0F, 1.0F, screen.width, screen.height, screen.width, screen.height);
                    } else {
                        GuiComponent.blit(matrix, screenCenterX - (wfinal / 2), 0, 1.0F, 1.0F, wfinal, screen.height, wfinal, screen.height);
                    }

                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

MenuBackgroundType Class

创建一个MenuBackgroundType的新子类并给它取合适的名字。

接下来我会将我的示例背景类型类命名为ExampleMenuBackgroundTypeWithInputString

在这个类中有一些特别的点你需要留意。

构造方法(Constructor)

这里的构造方法用于设置你背景类型唯一的标识符。

public ExampleMenuBackgroundTypeWithInputString() {
    //This identifier needs to be UNIQUE! It is not possible to register multiple types with the same identifier.
    //标识符独一无二!别想着用同一个标识符命名多个类型!
    super("example_type_input_string");
}
loadBackgrounds()

在输入字符串模式下,此方法未被使用,因为在此模式下,MenuBackground实例会被即时创建。

@Override
    public void loadBackgrounds() {
    //Empty because background instances get created on-the-fly via createInstanceFromInputString()
    //这里什么都没有是因为背景实例是通过createInstanceFromInputString()即时创建的。
}
getDisplayName()

调用此方法获取背景类型名。

名称显示在布局编辑器的背景选项中。

//You don't really have much space for the display name, so try to choose a short one ond explain the type further in the description.
//显示名字的空间很挤,你的类型名字简短点吧,在描述里详细说明就好。
@Override
public String getDisplayName() {
    return "Example Type w/ Input";
}
getDescription()

获取背景类型的描述。

在布局编辑器的背景选项中显示。

//Gets displayed when hovering over the type switcher in the background options menu in the layout editor.
//在布局编辑器的背景选项菜单中悬停在类型切换器上时显示。
//This is great for telling users everything important about your background type!
//这对于告诉用户你的背景类型的所有重要信息非常有用!
@Override
public List<String> getDescription() {
    List<String> l = new ArrayList<>();
    l.add("This is an example type");
    l.add("that uses input strings.");
    l.add("You can choose an image that");
    l.add("then gets displayed as background.");
    return l;
}
needsInputString()

这是最重要的方法,因为你得在这选择背景类型模式。 在这里,我们返回true,因为我们想为咱的背景类型使用输入字符串模式。 返回false会将背景类型设置为普通模式。

@Override
public boolean needsInputString() {
    //Return true to set this background type to the "input string mode".
    //返回true以将背景类型设置为"input string mode"(输入字符串模式)
    //This means it will call the createInstanceFromInputString() method to create instances of your background,
    //这意味着它会调用createInstanceFromInputString()方法创建你的背景实例。
    //instead of getting it from the loaded background instances.
    //而不是从加载的背景实例中获得它。
    return true;
}
createInstanceFromInputString()

当处于输入字符串模式时,每次需要一个新的MenuBackground实例时都会调用这个方法。

它基本是菜单背景工厂。

这个方法有给定的输入字符串作为参数,所以你可以用它来创建新的实例。

//In input string mode, this will get called whenever a new background instance is needed.
//在输入字符串模式下,每当需要一个新的背景实例时,它都会被调用。
//Is called when opening a menu or when clicking on the input string button in the background options menu of the layout editor.
//在打开菜单或点击布局编辑器的背景选项菜单中的输入字符串按钮时被调用。
@Override
public MenuBackground createInstanceFromInputString(String inputString) {
    //Return a new background instance from the inputString.
    //从输入字符串中返回新的背景实例。
    //In this case, the inputString is a path to an image file.
    //在这里,输入的字符串是图片文件路径。
    return new ExampleMenuBackgroundForInputString(this, inputString);
}
onInputStringButtonPress()

这里,你可以指定当用户在布局编辑器的后台选项中点击输入字符串按钮时会发生什么。

在这基本没啥限制,除了让猴子写代码,想干啥干啥,只要你可以在最后正确地将新背景设置成编辑器实例。

//This gets called when the input string button in the background options is pressed by the user.
//这个方法当用户按下背景选项上的输入字符串按钮后会被调用。
//You can basically do everything here.
//基本在这你啥都能干。
@Override
public void onInputStringButtonPress(LayoutEditorScreen handler, BackgroundOptionsPopup optionsPopup) {

    //This is a file chooser popup to choose the image for the background.
    //这是一个文件选择器的弹出窗口,用于选择背景图片。
    ChooseFilePopup cf = new ChooseFilePopup((filePath) -> {
        if (filePath != null) {
            //Always create a snapshot before changing the custom background fields!
            //在改变自定义背景字段之前,一定要创建一个快照!
            handler.history.saveSnapshot(handler.history.createSnapshot());
            //Always reset all backgrounds before setting a new one!
            //在设置新的背景之前,一定要重置所有的背景!
            optionsPopup.resetBackgrounds();
            //Always set the raw input string (file path in this case) to the input string field!
            //要一直将原始输入字符串(本例中为文件路径)设置为输入字符串字段!
            handler.customMenuBackgroundInputString = filePath;
            //Create a new instance of your background and set it to the custom background field!
            //创建一个你的新背景实例并设置到自定义背景字段中。
            handler.customMenuBackground = this.createInstanceFromInputString(filePath);
        }
        //This will open the parent popup again after choosing an image.
        //在选择图像后再次打开父级弹出窗口。
        PopupHandler.displayPopup(optionsPopup);
    }, "jpg", "jpeg", "png");
    if ((handler.customMenuBackgroundInputString != null)) {
        cf.setText(handler.customMenuBackgroundInputString);
    }
    //Open the file chooser popup.
    //打开文件选择弹出框.
    PopupHandler.displayPopup(cf);

}
inputStringButtonLabel()

返回布局编辑器的背景选项中的输入字符串按钮的标签。

//The button label of the input string button in the background options.
//背景选项中输入字符串按钮的按钮标签。
@Override
public String inputStringButtonLabel() {
    return "Choose File";
}
inputStringButtonTooltip()

返回布局编辑器的背景选项中输入字符串按钮的工具提示。

//A tooltip that gets displayed when hovering over the input string button in the background options menu of the layout editor.
//当悬停在布局编辑器的背景选项菜单的输入字符串按钮上时,会显示工具提示。
@Override
public List<String> inputStringButtonTooltip() {
    List<String> l = new ArrayList<>();
    l.add("This is a button tooltip");
    l.add("for the 'Choose File' button.");
    return l;
}
完整示例

下面是完整的MenuBackgroundType样例.

package de.keksuccino.fancymenu.api.background.example.with_input_string;

import de.keksuccino.fancymenu.api.background.MenuBackground;
import de.keksuccino.fancymenu.api.background.MenuBackgroundType;
import de.keksuccino.fancymenu.menu.fancy.helper.layoutcreator.LayoutEditorScreen;
import de.keksuccino.fancymenu.menu.fancy.helper.layoutcreator.content.BackgroundOptionsPopup;
import de.keksuccino.fancymenu.menu.fancy.helper.layoutcreator.content.ChooseFilePopup;
import de.keksuccino.konkrete.gui.screens.popup.PopupHandler;

import java.util.ArrayList;
import java.util.List;

//This is a background type that creates backgrounds out of user string inputs.
//从用户的字符串输入中创建的背景的背景类型。
//It displays a "Choose File" button in the background options of the layout editor, so the user can choose a file for a background.
//它在布局编辑器的背景选项中显示一个"Choose File"按钮,因此用户可以选择一个文件作为背景。
//This file path (input string) is then used to create an instance of the background.
//文件路径(输入的字符串)用于创建背景实例.
public class ExampleMenuBackgroundTypeWithInputString extends MenuBackgroundType {

    public ExampleMenuBackgroundTypeWithInputString() {
        //This identifier needs to be UNIQUE! It is not possible to register multiple types with the same identifier.
        //标识符独一无二!别想着用同一个标识符命名多个类型!
        super("example_type_input_string");
    }

    @Override
    public void loadBackgrounds() {
        //Empty because background instances get created on-the-fly via createInstanceFromInputString()
        //这里什么都没有是因为背景实例是通过createInstanceFromInputString()即时创建的。
    }

    //You don't really have much space for the display name, so try to choose a short one ond explain the type further in the description.
    //显示名字的空间很挤,你的类型名字简短点吧,在描述里详细说明就好。
    @Override
    public String getDisplayName() {
        return "Example Type w/ Input";
    }

    //Gets displayed when hovering over the type switcher in the background options menu in the layout editor.
    //在布局编辑器的背景选项菜单中悬停在类型切换器上时显示。
    //This is great for telling users everything important about your background type!
    //这对于告诉用户你的背景类型的所有重要信息非常有用!
    @Override
    public List<String> getDescription() {
        List<String> l = new ArrayList<>();
        l.add("This is an example type");
        l.add("that uses input strings.");
        l.add("You can choose an image that");
        l.add("then gets displayed as background.");
        return l;
    }

    @Override
    public boolean needsInputString() {
        //Return true to set this background type to the "input string mode".
        //返回true以将背景类型设置为"input string mode"(输入字符串模式)
        //This means it will call the createInstanceFromInputString() method to create instances of your background,
        //这意味着它会调用createInstanceFromInputString()方法创建你的背景实例。
        //instead of getting it from the loaded background instances.
        //而不是从加载的背景实例中获得它。
        return true;
    }

    //In input string mode, this will get called whenever a new background instance is needed.
    //在输入字符串模式下,每当需要一个新的背景实例时,它都会被调用。
    //Is called when opening a menu or when clicking on the input string button in the background options menu of the layout editor.
    //在打开菜单或点击布局编辑器的背景选项菜单中的输入字符串按钮时被调用。
    @Override
    public MenuBackground createInstanceFromInputString(String inputString) {
        //Return a new background instance from the inputString.
        //从输入字符串中返回新的背景实例。
        return new ExampleMenuBackgroundForInputString(this, inputString);
    }

    //This gets called when the input string button in the background options is pressed by the user.
    //这个方法当用户按下背景选项上的输入字符串按钮后会被调用。
    //You can basically do everything here.
    //除了让猴子写代码,基本在这你啥都能干。
    @Override
    public void onInputStringButtonPress(LayoutEditorScreen handler, BackgroundOptionsPopup optionsPopup) {

        //This is a file chooser popup to choose the image for the background.
        //这是一个文件选择器的弹出窗口,用于选择背景图片。
        ChooseFilePopup cf = new ChooseFilePopup((filePath) -> {
            if (filePath != null) {
                //Always create a snapshot before changing the custom background fields!
                //在改变自定义背景字段之前,一定要创建一个快照!
                handler.history.saveSnapshot(handler.history.createSnapshot());
                //Always reset all backgrounds before setting a new one!
                //在设置新的背景之前,一定要重置所有的背景!
                optionsPopup.resetBackgrounds();
                //Always set the raw input string (file path in this case) to the input string field!
                //要一直将原始输入字符串(本例中为文件路径)设置为输入字符串字段!
                handler.customMenuBackgroundInputString = filePath;
                //Create a new instance of your background and set it to the custom background field!
                //创建一个你的新背景实例并设置到自定义背景字段中。
                handler.customMenuBackground = this.createInstanceFromInputString(filePath);
            }
            //This will open the parent popup again after choosing an image.
            //在选择图像后再次打开父级弹出窗口。
            PopupHandler.displayPopup(optionsPopup);
        }, "jpg", "jpeg", "png");
        if ((handler.customMenuBackgroundInputString != null)) {
            cf.setText(handler.customMenuBackgroundInputString);
        }
        //Open the file chooser popup.
        //打开文件选择弹出框
        PopupHandler.displayPopup(cf);

    }

    //The button label of the input string button in the background options.
    //背景选项中输入字符串按钮的按钮标签。
    @Override
    public String inputStringButtonLabel() {
        return "Choose File";
    }

    //A tooltip that gets displayed when hovering over the input string button in the background options menu of the layout editor.
    //当悬停在布局编辑器的背景选项菜单的输入字符串按钮上时,会显示工具提示。
    @Override
    public List<String> inputStringButtonTooltip() {
        List<String> l = new ArrayList<>();
        l.add("This is a button tooltip");
        l.add("for the 'Choose File' button.");
        return l;
    }

}

注册菜单背景

你就快完成了

现在你只需要在游戏加载时把你的MenuBackgroundType注册到MenuBackgroundTypeRegistry.

package de.keksuccino.fancymenu;

import net.minecraftforge.fml.common.Mod;
import de.keksuccino.fancymenu.api.background.MenuBackgroundTypeRegistry;

@Mod("modid")
public class ExampleModMainClass {

    public ExampleModMainClass() {
        try {

            //Register your MenuBackgroundType to the MenuBackgroundTypeRegistry at mod init.
            //在模组初始化时注册你的MenuBackgroundType到MenuBackgroundTypeRegistry.
            MenuBackgroundTypeRegistry.registerBackgroundType(new ExampleMenuBackgroundTypeWithInputString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}