實體的模型要如何一個格式多平台通用?透過Geckolib這個模組可以有效舒緩這個問題。
簡單來說,只要是用Blockbench製作的模型,透過Geckolib就可以同時用在Java版模組和基岩版的Add-On,還能夠製作實體動畫。
(左為Java版畫面,右邊為基岩版畫面)
這篇文章以Fabric為基礎,教學如何在Minecraft Java版1.17.1加入實體,並用Geckolib來套用模型。
1. 注意事項#
開發模組使用Geckolib的話,別人玩你的模組前就必須安裝 Geckolib這個前置模組。
Geckolib有分forge版跟fabric版,下載時請抓有寫"fabric"的版本。
2. 準備模型檔案#
要製作跨平台模型,應以基岩版格式為基礎,因為能無痛轉換成Geckolib格式。
首先轉換現有的模型。Blockbench安裝"Geckolib Animation Utils"插件。
按Files -> Convert Project,選擇"Geckolib Animated Model"
- 按Files -> Export -> Export Geckolib Model
會得到一個json檔案,裡面只有記載模型資訊。材質的png檔案要另外匯出。
如果有動畫檔案,在Animation面板按下 Animation -> Export Animations,同樣會得到一個json檔案。
在這個例子中,我將模型檔案放在geo
資料夾裡,叫做shimakaze.json
。
材質放在/entity/kanmusu/
,叫做shimakaze.png
動畫放在/animations
,叫做shimakaze.animation.json
3. 在fabric專案加入Geckolib依賴項目#
- 開啟專案的build.gradle,加入Geckolib:
repositories {
maven { url 'https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/' }
}
dependencies {
modImplementation 'software.bernie.geckolib:geckolib-fabric-1.17:3.0.13:dev'
}
- 開啟
fabric.mod.json
,註明要使用Geckolib。(fabric.mod.json
也可以不聲明依賴Geckolib,但若出錯遊戲就會直接崩潰,而不是由Fabric提示玩家缺少Geckolib模組。)
"depends": {
"fabricloader": ">=0.11.6",
"fabric": "*",
"minecraft": "1.17.x",
"java": ">=16",
"geckolib3": "*"
}
4. fabric實體的主程式寫法#
fabric中建立實體需要以下檔案:
- 註冊實體的class
- 註冊渲染器的class (需在fabric.mod.json註明client entry)
- 渲染器的class
- 模型的lass
模組會從Main.java
開始,接著會呼叫ModEntities的registerEntities()
方法,用這個class來註冊實體(->ShimakazeEntity.java
)。
同時,EntityClinet.java
會在初始化時註冊渲染器(->ShimakazeRenderer.java
)。
ShimakazeEntity.java
包含註冊實體的方法,以及定義實體的行為程式,但這裡僅加入一個靜止實體。
ShimakazeModel.java
為最關鍵的一步,使用Geckolib來創造模型、材質、動畫。
ModEntities.java程式碼:
package net.mcbedev.kancolle.client;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder;
import net.mcbedev.kancolle.Main;
import net.mcbedev.kancolle.client.entity.ShimakazeEntity;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public class ModEntities {
// 註冊實體
public static final EntityType<ShimakazeEntity> SHIMAKAZE = Registry.register(Registry.ENTITY_TYPE,
new Identifier(Main.MOD_ID, "shimakaze"), FabricEntityTypeBuilder
.create(SpawnGroup.CREATURE, ShimakazeEntity::new).dimensions(EntityDimensions.fixed(0.5f, 2f)).build());
public static void registerEntities() {
// 註冊實體屬性
FabricDefaultAttributeRegistry.register(SHIMAKAZE, ShimakazeEntity.createMobAttributes());
System.out.println("Registering mod mobs for" + Main.MOD_ID);
}
}
EntityClient.java程式碼:
package net.mcbedev.kancolle.client;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.impl.client.rendering.EntityRendererRegistryImpl;
import net.mcbedev.kancolle.client.renderer.ShimakazeRenderer;
@Environment(EnvType.CLIENT)
public class EntityClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
//註冊渲染器
EntityRendererRegistryImpl.register(ModEntities.SHIMAKAZE, ShimakazeRenderer::new);
}
}
ShimakazeEntity.java程式碼:
package net.mcbedev.kancolle.client.entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.mob.PathAwareEntity;
import net.minecraft.world.World;
import software.bernie.geckolib3.core.IAnimatable;
import software.bernie.geckolib3.core.manager.AnimationData;
import software.bernie.geckolib3.core.manager.AnimationFactory;
public class ShimakazeEntity extends PathAwareEntity implements IAnimatable {
private AnimationFactory factory = new AnimationFactory(this);
public ShimakazeEntity(EntityType<? extends PathAwareEntity> type, World worldIn) {
super(type, worldIn);
this.ignoreCameraFrustum = true;
}
@Override
public AnimationFactory getFactory() {
return this.factory;
}
@Override
public void registerControllers(AnimationData arg0) {
}
}
ShimakazeRenderer.java程式碼:
package net.mcbedev.kancolle.client.renderer;
import net.mcbedev.kancolle.client.entity.ShimakazeEntity;
import net.mcbedev.kancolle.client.model.ShimakazeEntityModel;
import net.minecraft.client.render.entity.EntityRendererFactory;
import software.bernie.geckolib3.renderers.geo.GeoEntityRenderer;
public class ShimakazeRenderer extends GeoEntityRenderer<ShimakazeEntity> {
public ShimakazeRenderer(EntityRendererFactory.Context renderManager) {
super(renderManager, new ShimakazeEntityModel());
}
}
ShimakazeModel.java程式碼,跟第一步驟準備的路徑要一致。
package net.mcbedev.kancolle.client.model;
import net.mcbedev.kancolle.client.entity.ShimakazeEntity;
import net.minecraft.util.Identifier;
import software.bernie.geckolib3.model.AnimatedGeoModel;
public class ShimakazeEntityModel extends AnimatedGeoModel<ShimakazeEntity> {
@Override
public Identifier getModelLocation(ShimakazeEntity object) {
return new Identifier("kancollemod", "geo/shimakaze.json");
}
@Override
public Identifier getTextureLocation(ShimakazeEntity object) {
return new Identifier("kancollemod", "textures/entity/kanmusu/shimakaze.png");
}
@Override
public Identifier getAnimationFileLocation(ShimakazeEntity animatable) {
return new Identifier("kancollemod", "animations/shimakaze.animation.json");
}
}
在處理完import的問題之後,實際在遊戲中測試,用指令/summon kancolle:shimakaze
,會召喚靜止狀態的島風。