注册
实体类型、刷怪蛋、属性事件全部挂到 Forge 事件系统。
Forge 1.20.1 Mario creature lesson
把一个轻松、活泼、会跳跃和发射火球的 Mario 风格生物做成 Forge MOD。课程重点放在实体注册、属性设计、AI 技能、模型贴图修复和动画排查。
Course overview
前面的 MOD 调试里出现了两个典型问题:实体没有出现在召唤列表,以及模型贴图位置错乱、没有动画。本页把它们拆成可检查的实现流程,让学生能从“能召唤”推进到“像一个真正的游戏生物”。
实体类型、刷怪蛋、属性事件全部挂到 Forge 事件系统。
血量、移动速度、跳跃倾向、攻击范围按“轻快角色”设计。
踩踏、火球、冲刺、金币粒子反馈组成核心玩法循环。
Mind map
页面沿用 像素世界 的暗色背景、清晰网格和课程说明方式,同时加入 Mario 的红、蓝、黄、绿点缀,让它比恐怖 Boss 页面更像一节轻松的创作课。
Build steps
如果命令补全里没有 Mario,先检查 `DeferredRegister` 是否使用了正确的 `MODID`。
实体能注册不等于能生成。缺少属性绑定时,召唤经常会失败或直接报错。
贴图放进 `textures/entity/mario/`,模型导出后引用同一个命名体系。
火球和冲刺不要每 tick 释放,用冷却和距离判断保持战斗节奏。
按 idle、walk、jump、attack 划分状态,让动作和 AI 行为同步。
用 `/summon examplemod:mario` 和日志同时检查实体、渲染、贴图和动画。
Code guide
public class ModEntities {
public static final DeferredRegister<EntityType<?>> ENTITIES =
DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, ExampleMod.MODID);
public static final RegistryObject<EntityType<MarioEntity>> MARIO =
ENTITIES.register("mario", () -> EntityType.Builder
.of(MarioEntity::new, MobCategory.CREATURE)
.sized(0.8F, 1.85F)
.clientTrackingRange(10)
.build(new ResourceLocation(ExampleMod.MODID, "mario").toString()));
public static void register(IEventBus bus) {
ENTITIES.register(bus);
}
}
public static AttributeSupplier.Builder createAttributes() {
return PathfinderMob.createMobAttributes()
.add(Attributes.MAX_HEALTH, 36.0D)
.add(Attributes.MOVEMENT_SPEED, 0.34D)
.add(Attributes.ATTACK_DAMAGE, 6.0D)
.add(Attributes.FOLLOW_RANGE, 28.0D)
.add(Attributes.KNOCKBACK_RESISTANCE, 0.15D);
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new FloatGoal(this));
this.goalSelector.addGoal(2, new MarioFireballGoal(this));
this.goalSelector.addGoal(3, new MeleeAttackGoal(this, 1.18D, true));
this.goalSelector.addGoal(4, new WaterAvoidingRandomStrollGoal(this, 1.0D));
this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Monster.class, true));
}
public class MarioFireballGoal extends Goal {
private final MarioEntity mario;
private int cooldown;
public MarioFireballGoal(MarioEntity mario) {
this.mario = mario;
}
@Override
public boolean canUse() {
LivingEntity target = mario.getTarget();
return target != null
&& target.isAlive()
&& mario.distanceTo(target) < 14.0F
&& cooldown-- <= 0;
}
@Override
public void start() {
LivingEntity target = mario.getTarget();
if (target == null) return;
SmallFireball fireball = new SmallFireball(mario.level(), mario, 0, 0, 0);
fireball.setPos(mario.getX(), mario.getEyeY() - 0.15D, mario.getZ());
fireball.shoot(target.getX() - mario.getX(), target.getEyeY() - mario.getEyeY(), target.getZ() - mario.getZ(), 1.3F, 0.8F);
mario.level().addFreshEntity(fireball);
mario.playSound(SoundEvents.BLAZE_SHOOT, 0.7F, 1.25F);
cooldown = 80;
}
}
public class MarioRenderer extends MobRenderer<MarioEntity, MarioModel<MarioEntity>> {
private static final ResourceLocation TEXTURE =
new ResourceLocation(ExampleMod.MODID, "textures/entity/mario/mario.png");
public MarioRenderer(EntityRendererProvider.Context context) {
super(context, new MarioModel<>(context.bakeLayer(MarioModel.LAYER_LOCATION)), 0.42F);
}
@Override
public ResourceLocation getTextureLocation(MarioEntity entity) {
return TEXTURE;
}
}
// 贴图错位时优先检查:
// 1. PNG 是否在 assets/examplemod/textures/entity/mario/mario.png
// 2. 模型导出的 UV 坐标是否匹配贴图尺寸
// 3. MTL/OBJ 文件名中是否有空格或特殊字符
// 4. 客户端事件是否注册了 EntityRenderers.register(ModEntities.MARIO.get(), MarioRenderer::new)
Demo check
课程视频素材来自你的 Minecraft Forge 录屏。网页里保留这个演示入口,方便学生对照自己游戏里的效果。
Debug board
`ModEntities.register(modEventBus)`、属性事件、客户端 renderer 注册缺一不可。
重新从 Blockbench 导出 Java 模型,确认贴图尺寸和模型 UV 使用同一张 PNG。
如果只导入静态 OBJ,默认不会有骨骼动画。需要 GeckoLib 或 Java 模型动画逻辑。