Forge 1.20.1 Mario creature lesson

超级玛丽在我的世界

把一个轻松、活泼、会跳跃和发射火球的 Mario 风格生物做成 Forge MOD。课程重点放在实体注册、属性设计、AI 技能、模型贴图修复和动画排查。

Forge 1.20.1 Blockbench AI Goals Fireball Skill Texture UV Animation

Course overview

这节课解决三个问题:Mario 找得到、贴图对得上、动作跑起来。

前面的 MOD 调试里出现了两个典型问题:实体没有出现在召唤列表,以及模型贴图位置错乱、没有动画。本页把它们拆成可检查的实现流程,让学生能从“能召唤”推进到“像一个真正的游戏生物”。

01

注册

实体类型、刷怪蛋、属性事件全部挂到 Forge 事件系统。

02

属性

血量、移动速度、跳跃倾向、攻击范围按“轻快角色”设计。

03

技能

踩踏、火球、冲刺、金币粒子反馈组成核心玩法循环。

超级玛丽 MOD 从资源导出到实体注册、贴图修复和动画验证的流程图
实现流程图:先保证资源路径正确,再进入实体逻辑和渲染动画。
超级玛丽 Minecraft MOD 教学思维导图
思维导图:把 Mario 生物拆成注册、资源、属性、AI、技能和调试六个模块。

Mind map

教学结构保持清楚,但网页气质更轻快。

页面沿用 像素世界 的暗色背景、清晰网格和课程说明方式,同时加入 Mario 的红、蓝、黄、绿点缀,让它比恐怖 Boss 页面更像一节轻松的创作课。

  • 资源层:OBJ/MTL/PNG 文件名、贴图路径、UV 展开必须一一对应。
  • 逻辑层:实体注册和属性事件决定游戏能不能识别 Mario。
  • 体验层:跳跃、火球、冲刺和粒子反馈决定它是不是好玩。

Build steps

从看不见的实体,到能动、能打、能调试的 Mario。

1

确认 MOD ID

如果命令补全里没有 Mario,先检查 `DeferredRegister` 是否使用了正确的 `MODID`。

2

绑定属性事件

实体能注册不等于能生成。缺少属性绑定时,召唤经常会失败或直接报错。

3

整理资源路径

贴图放进 `textures/entity/mario/`,模型导出后引用同一个命名体系。

4

加入技能冷却

火球和冲刺不要每 tick 释放,用冷却和距离判断保持战斗节奏。

5

接入动画状态

按 idle、walk、jump、attack 划分状态,让动作和 AI 行为同步。

6

游戏内验证

用 `/summon examplemod:mario` 和日志同时检查实体、渲染、贴图和动画。

Code guide

核心代码:注册、属性、技能和贴图修复。

ModEntities.java
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);
    }
}
MarioEntity.java
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));
}
MarioFireballGoal.java
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;
    }
}
MarioRenderer.java
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 录屏。网页里保留这个演示入口,方便学生对照自己游戏里的效果。

  • 命令补全里能看到 `examplemod:mario`。
  • Mario 生成后贴图没有错位、反色或丢失。
  • 移动、攻击和火球释放时有可见动作反馈。

Debug board

常见问题快速定位。

找不到 Mario

检查注册链路

`ModEntities.register(modEventBus)`、属性事件、客户端 renderer 注册缺一不可。

贴图错位

检查 UV 和路径

重新从 Blockbench 导出 Java 模型,确认贴图尺寸和模型 UV 使用同一张 PNG。

没有动画

检查状态机

如果只导入静态 OBJ,默认不会有骨骼动画。需要 GeckoLib 或 Java 模型动画逻辑。

超级玛丽 MOD 技能循环图
技能循环:发现目标、跳跃接近、火球远程、踩踏近战、粒子和声音反馈。