zh_cn:tutorial:custom_model
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
zh_cn:tutorial:custom_model [2021/08/30 09:42] – [创建模型] solidblock | zh_cn:tutorial:custom_model [2024/08/27 04:34] (current) – 更新翻译 solidblock | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== 使用自定义模型动态渲染方块和物品 ====== | ====== 使用自定义模型动态渲染方块和物品 ====== | ||
- | 可以通过方块模型 JSON 文件将模型添加到游戏,但也可以通过 Java 代码来渲染。本教程中,我们将会将一个四面熔炉模型添加到游戏。 | + | 可以通过方块模型 JSON 文件将模型添加到游戏,但也可以通过 Java 代码来渲染。本教程中,我们将会把一个四面熔炉模型添加到游戏。 |
- | 注意模型会在区块被重建时渲染。如果需要更加动态的渲染,可以使用 '' | + | 注意模型会在区块被重建时渲染。如果需要更加动态的渲染,可以使用 '' |
===== 创建模型 ===== | ===== 创建模型 ===== | ||
Line 8: | Line 8: | ||
<code java> | <code java> | ||
+ | @Environment(EnvType.CLIENT) | ||
public class FourSidedFurnaceModel implements UnbakedModel, | public class FourSidedFurnaceModel implements UnbakedModel, | ||
</ | </ | ||
==== Sprites ==== | ==== Sprites ==== | ||
- | 渲染纹理离不开'' | + | 渲染纹理离不开'' |
- | 这里,我们会使用两个熔炉纹理。它们是方块纹理,所以要从方块atlas'' | + | |
<code java> | <code java> | ||
+ | // 对于 1.21 之前的版本,将 `Identifier.ofVanilla` 替换为 `new Identifier`。 | ||
private static final SpriteIdentifier[] SPRITE_IDS = new SpriteIdentifier[]{ | private static final SpriteIdentifier[] SPRITE_IDS = new SpriteIdentifier[]{ | ||
- | new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, | + | new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, |
- | new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, | + | new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, |
}; | }; | ||
- | private Sprite[] SPRITES = new Sprite[2]; | + | private Sprite[] SPRITES = new Sprite[SPRITE_IDS.length]; |
+ | |||
+ | // 一些常量,以避免魔法数据,需要匹配 SPRITE_IDS | ||
+ | private static final int SPRITE_SIDE = 0; | ||
+ | private static final int SPRITE_TOP = 1; | ||
</ | </ | ||
- | ==== Meshes | + | ==== Mesh ==== |
'' | '' | ||
<code java> | <code java> | ||
Line 28: | Line 33: | ||
</ | </ | ||
- | ==== UnbakedModel方法 ==== | + | ==== UnbakedModel 方法 ==== |
<code java> | <code java> | ||
@Override | @Override | ||
Line 36: | Line 41: | ||
@Override | @Override | ||
- | public | + | public |
- | | + | // 与模型继承有关,我们这里还不需要使用到 |
} | } | ||
@Override | @Override | ||
- | public BakedModel bake(ModelLoader loader, Function< | + | public BakedModel bake(Baker baker, Function< |
- | // 获得sprites | + | // 获得 sprites |
for(int i = 0; i < 2; ++i) { | for(int i = 0; i < 2; ++i) { | ||
SPRITES[i] = textureGetter.apply(SPRITE_IDS[i]); | SPRITES[i] = textureGetter.apply(SPRITE_IDS[i]); | ||
} | } | ||
- | // 用Renderer API构建mesh | + | // 用 Renderer API 构建 mesh |
Renderer renderer = RendererAccess.INSTANCE.getRenderer(); | Renderer renderer = RendererAccess.INSTANCE.getRenderer(); | ||
MeshBuilder builder = renderer.meshBuilder(); | MeshBuilder builder = renderer.meshBuilder(); | ||
Line 53: | Line 58: | ||
for(Direction direction : Direction.values()) { | for(Direction direction : Direction.values()) { | ||
- | int spriteIdx = direction == Direction.UP || direction == Direction.DOWN ? 1 : 0; | + | int spriteIdx = direction == Direction.UP || direction == Direction.DOWN ? SPRITE_TOP |
- | // 将新的面(face)添加到mesh | + | // 将新的面(face)添加到 mesh |
emitter.square(direction, | emitter.square(direction, | ||
- | // 设置面的sprite,必须在.square()之后调用 | + | // 设置面的 sprite,必须在 .square() 之后调用 |
- | // 我们还没有指定任何uv坐标,所以我们使用整个纹理,BAKE_LOCK_UV恰好就这么做。 | + | // 我们还没有指定任何 uv 坐标,所以我们使用整个纹理,BAKE_LOCK_UV 恰好就这么做。 |
emitter.spriteBake(0, | emitter.spriteBake(0, | ||
// 启用纹理使用 | // 启用纹理使用 | ||
emitter.spriteColor(0, | emitter.spriteColor(0, | ||
- | // 将quad添加到mesh | + | // 将 quad 添加到 mesh |
emitter.emit(); | emitter.emit(); | ||
} | } | ||
Line 70: | Line 75: | ||
</ | </ | ||
- | ==== BakedModel方法 ==== | + | ==== BakedModel 方法 ==== |
- | 注意这里不是所有的方法都会被Fabric Renderer使用,所以我们并不关心这个实现。 | + | 注意这里不是所有的方法都会被 Fabric Renderer 使用,所以我们并不关心这个实现。 |
<code java> | <code java> | ||
- | @Override | + | |
public List< | public List< | ||
- | return | + | return |
} | } | ||
Line 99: | Line 104: | ||
@Override | @Override | ||
- | public Sprite | + | public Sprite |
- | return SPRITES[1]; // 方块破坏纹理,使用furnace_top | + | return SPRITES[1]; // 方块被破坏时产生的颗粒,使用 furnace_top |
} | } | ||
Line 114: | Line 119: | ||
</ | </ | ||
- | ==== FabricBakedModel方法 ==== | + | ==== FabricBakedModel 方法 ==== |
<code java> | <code java> | ||
@Override | @Override | ||
public boolean isVanillaAdapter() { | public boolean isVanillaAdapter() { | ||
- | return false; // false以触发FabricBakedModel渲染 | + | return false; // false 以触发 FabricBakedModel 渲染 |
} | } | ||
Line 125: | Line 130: | ||
// 渲染函数 | // 渲染函数 | ||
| | ||
- | // 我们仅渲染mesh | + | // 我们仅渲染 mesh |
renderContext.meshConsumer().accept(mesh); | renderContext.meshConsumer().accept(mesh); | ||
} | } | ||
Line 135: | Line 140: | ||
} | } | ||
</ | </ | ||
+ | |||
+ | 注意:确保覆盖了 '' | ||
===== 注册模型 ===== | ===== 注册模型 ===== | ||
- | 我们先写一个'' | ||
- | 我们在名称'' | + | 要让模型在游戏内被渲染需要注册,为注册我们需要创建 |
<code java> | <code java> | ||
- | public class TutorialModelProvider | + | @Environment(EnvType.CLIENT) |
- | public static final Identifier | + | public class TutorialModelLoadingPlugin |
+ | public static final ModelIdentifier | ||
@Override | @Override | ||
- | public | + | public |
- | if(identifier.equals(FOUR_SIDED_FURNACE_MODEL)) { | + | |
- | return new FourSidedFurnaceModel(); | + | pluginContext.modifyModelOnLoad().register((original, |
- | } else { | + | // 这个每次加载模型时都会调用,所以确保我们只针对我们的 |
- | return | + | final ModelIdentifier id = context.topLevelId(); |
- | } | + | |
+ | return new FourSidedFurnaceModel(); | ||
+ | } else { | ||
+ | // 如果不修改模型,就照样返回原来的 | ||
+ | | ||
+ | } | ||
+ | }); | ||
} | } | ||
} | } | ||
</ | </ | ||
- | 现在我们要将这个类注册到客户端初始化器中,the entry point for client-specific code. | + | 现在我们要将这个类注册到客户端初始化器(仅适用于客户端的代码的入口点)中。 |
<code java> | <code java> | ||
+ | @Environment(EnvType.CLIENT) | ||
public class ExampleModClient implements ClientModInitializer { | public class ExampleModClient implements ClientModInitializer { | ||
@Override | @Override | ||
public void onInitializeClient() { | public void onInitializeClient() { | ||
- | | + | |
| | ||
- | /* 其他客户端指定的初始化 */ | + | /* 其他客户端特定的初始化 */ |
} | } | ||
} | } | ||
</ | </ | ||
- | 不要忘记在'' | + | 不要忘记在 '' |
- | < | + | < |
- | /* ... */ | + | { |
+ | [...] | ||
" | " | ||
- | | + | |
" | " | ||
" | " | ||
] | ] | ||
}, | }, | ||
+ | [...] | ||
+ | } | ||
</ | </ | ||
===== 使用模型 ===== | ===== 使用模型 ===== | ||
- | 你现在可以注册你的方块以使用新模型。比如,如果你的方块只有一个状态,把这个放在'' | + | 你可以[[blocks|注册方块]]以使用你的新模型。我们假设方块的 id 是 '' |
- | <code json> | + | |
+ | < | ||
+ | public final class TutorialBlocks { | ||
+ | [...] | ||
+ | public static final Block FOUR_SIDED_FURNACE = register(" | ||
+ | [...] | ||
+ | } | ||
+ | </ | ||
+ | <code javascript src/ | ||
{ | { | ||
" | " | ||
Line 196: | Line 223: | ||
==== 更新模型 ==== | ==== 更新模型 ==== | ||
我们复用相同的模型类,但是有一点点小改变: | 我们复用相同的模型类,但是有一点点小改变: | ||
- | * 我们会使用 '' | + | * 我们会使用 '' |
- | 我们会更新我们的 | + | 现在对 |
<code java> | <code java> | ||
- | // Minecraft默认方块模型 | ||
- | private static final Identifier DEFAULT_BLOCK_MODEL = new Identifier(" | ||
- | |||
- | private ModelTransformation transformation; | ||
- | | ||
- | // 我们会将默认模型添加到其依赖中 | ||
- | public Collection< | ||
- | return Arrays.asList(DEFAULT_BLOCK_MODEL); | ||
- | } | ||
- | | ||
- | // 我们会给bake函数添加一点逻辑 | ||
- | @Override | ||
- | public BakedModel bake(ModelLoader loader, Function< | ||
- | // 加载默认方块模型 | ||
- | JsonUnbakedModel defaultBlockModel = (JsonUnbakedModel) loader.getOrLoadModel(DEFAULT_BLOCK_MODEL); | ||
- | // 获取ModelTransformation | ||
- | transformation = defaultBlockModel.getTransformations(); | ||
- | | ||
- | /* 先前的代码 */ | ||
- | } | ||
| | ||
- | // 我们会实现 getTransformation() 和 getOverrides() | + | // 我们需要实现 getTransformation() 和 getOverrides() |
@Override | @Override | ||
public ModelTransformation getTransformation() { | public ModelTransformation getTransformation() { | ||
- | return | + | return |
} | } | ||
Line 232: | Line 239: | ||
} | } | ||
| | ||
- | // 我们也会使用此方法以使得物品渲染时有正确的光照。尝试将其设为false,你就会看到不同。 | + | // 我们也会使用此方法以使得物品渲染时有正确的光照。尝试将其设为 false,你就会看到不同。 |
@Override | @Override | ||
public boolean isSideLit() { | public boolean isSideLit() { | ||
Line 241: | Line 248: | ||
@Override | @Override | ||
public void emitItemQuads(ItemStack itemStack, Supplier< | public void emitItemQuads(ItemStack itemStack, Supplier< | ||
- | | + | |
} | } | ||
</ | </ | ||
==== 加载模型 ==== | ==== 加载模型 ==== | ||
- | 更新我们先前创建的 '' | + | 更新我们先前创建的 '' |
<code java> | <code java> | ||
- | public class TutorialModelProvider | + | @Environment(EnvType.CLIENT) |
- | public static final FourSidedFurnaceModel | + | public class TutorialModelLoadingPlugin |
- | public static final Identifier FOUR_SIDED_FURNACE_MODEL_BLOCK = new Identifier(" | + | public static final ModelIdentifier |
- | public static final Identifier | + | public static final ModelIdentifier |
@Override | @Override | ||
- | public | + | public |
- | if(identifier.equals(FOUR_SIDED_FURNACE_MODEL_BLOCK) || identifier.equals(FOUR_SIDED_FURNACE_MODEL_ITEM)) { | + | |
- | return | + | pluginContext.modifyModelOnLoad().register((original, |
- | } else { | + | // 这个每次加载模型时都会调用,所以确保我们只针对我们的 |
- | return | + | final ModelIdentifier id = context.topLevelId(); |
- | } | + | |
+ | return | ||
+ | } else { | ||
+ | // 如果不修改模型,就照样返回原来的 | ||
+ | | ||
+ | } | ||
+ | }); | ||
} | } | ||
} | } | ||
Line 270: | Line 283: | ||
===== 更加动态的渲染 ===== | ===== 更加动态的渲染 ===== | ||
- | '' | + | '' |
<code java> | <code java> | ||
@Override | @Override |
zh_cn/tutorial/custom_model.1630316541.txt.gz · Last modified: 2021/08/30 09:42 by solidblock