IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Minecraft 1.18.1、1.18.2模组开发 09.MC无双---动画生物实体 -> 正文阅读

[游戏开发]Minecraft 1.18.1、1.18.2模组开发 09.MC无双---动画生物实体

Minecraft是一款没有上限的游戏,你甚至可以在里面实现魂Like游戏中的酷炫动作:我的世界 类魂式战斗模组

mcws.png

今天我们尝试在模组中添加一个能够做各种动作的生物实体

1.首先,为了实现这些效果,我们需要首先使用到一个模组:geckolib(下载地址)

找到项目的build.gradle文件,在repositoriesdependencies中添加依赖。

repositories {

    //添加这个
    maven { url 'https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/' }
    
}
dependencies {
    minecraft 'net.minecraftforge:forge:1.18.2-40.1.0'
       
    //添加这个
    implementation fg.deobf('software.bernie.geckolib:geckolib-1.18-forge:3.0.18')

}

cr4.png

之后我们重新构建gradle项目

cr5.png

构建好了项目后在项目的Main类中添加一句geckolib的初始化语句:

Main.java

	public Main() {
		IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();

		ItemInit.ITEMS.register(bus);


        //添加GeckoLib初始化函数
        GeckoLib.initialize();


		MinecraftForge.EVENT_BUS.register(this);
	}

2.之后,与之前的教程一样,我们需要在blockbench中制作一个模组中的生物实体:

进入软件后我们要找到一个插件按钮,然后再搜索栏中输入GeckoLib Animation Utils,并下载这个插件

cr6.png

将我们制作好的生物实体进行模型转换工作,找到Convert Project,之后选择Geckolib Animated Model

cr7.png

在这之后,你会发现你的生物实体栏多了一个Animate栏,点击进去:

cr8.png

具体动作制作的视频:Blockbench动画制作

在制作好所有的动画后我们导出模型和动画json文件。

cc.png

3.模型制作完成,接下来需要制作生物实体类,我们的生物的动画事件类似于状态机,所以要对生物实体的状态进行枚举。

EntityDund.java

package com.joy187.re8joymod.entity;

import java.util.Random;
import java.util.function.Predicate;

import javax.annotation.Nullable;

import net.minecraft.core.BlockPos;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.Mth;
import net.minecraft.world.Difficulty;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.BreakDoorGoal;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
import net.minecraft.world.entity.ai.goal.MoveThroughVillageGoal;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.entity.ai.util.GoalUtils;
import net.minecraft.world.entity.animal.IronGolem;
import net.minecraft.world.entity.animal.Turtle;
import net.minecraft.world.entity.monster.Monster;
import net.minecraft.world.entity.monster.ZombifiedPiglin;
import net.minecraft.world.entity.npc.AbstractVillager;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import software.bernie.geckolib3.core.IAnimatable;
import software.bernie.geckolib3.core.PlayState;
import software.bernie.geckolib3.core.builder.AnimationBuilder;
import software.bernie.geckolib3.core.controller.AnimationController;
import software.bernie.geckolib3.core.event.predicate.AnimationEvent;
import software.bernie.geckolib3.core.manager.AnimationData;
import software.bernie.geckolib3.core.manager.AnimationFactory;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
            //我们的生物类要实现一个IAnimatable的接口
public class EntityDund extends Monster implements IAnimatable{
    
    //定义一个动画事件处理器
	private AnimationFactory factory = new AnimationFactory(this);
	
	private boolean canBreakDoors;
    public boolean playAttackAnimation = false;
    public int attackTick=1;
    //枚举攻击状态
	public static final EntityDataAccessor<Integer> STATE = SynchedEntityData.defineId(EntityDund.class,
			EntityDataSerializers.INT);
	
	private final BreakDoorGoal breakDoorGoal = new BreakDoorGoal(this, DOOR_BREAKING_PREDICATE);
	   private static final Predicate<Difficulty> DOOR_BREAKING_PREDICATE = (mode) -> {
		      return mode == Difficulty.HARD || mode == Difficulty.NORMAL;
		   };

    public EntityDund(EntityType<? extends Monster> type, Level worldIn) {
        super(type, worldIn);
        this.xpReward = 10;
    }

    protected void registerGoals() {
        this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
        this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
        this.addBehaviourGoals();
    }

    protected void addBehaviourGoals() {
    
        //将我们的攻击AI进行注册
		this.goalSelector.addGoal(1, new EntityDund.AttackGoal(this));
        this.goalSelector.addGoal(2, new DundAttackGoal(this, 1.0D, false));
        this.goalSelector.addGoal(6, new MoveThroughVillageGoal(this, 1.0D, true, 4, this::canBreakDoors));
        this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D));
        this.targetSelector.addGoal(1, (new HurtByTargetGoal(this)).setAlertOthers(ZombifiedPiglin.class));
        this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
        this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false));
        this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
        //this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR));
     }

    
    public static AttributeSupplier.Builder prepareAttributes() {
        return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 300.0D).
                add(Attributes.MOVEMENT_SPEED, 0.32D).
                add(Attributes.ATTACK_DAMAGE, 5.5D);
    }


    public boolean doHurtTarget(Entity entityIn) {
        if(!super.doHurtTarget(entityIn))
        {
        	this.playAttackAnimation=false;
            return false;
        }
        else{
            if(entityIn instanceof LivingEntity)
            {
                float f = this.level.getCurrentDifficultyAt(this.blockPosition()).getEffectiveDifficulty();
                ((LivingEntity)entityIn).addEffect(new MobEffectInstance(MobEffects.WITHER, 100 * (int)f,0,true,true));
            }
            return true;
        }

    }
    
    //该状态播放器控制生物平时的各种动作
    private <E extends IAnimatable> PlayState predicate(AnimationEvent<E> event) {
        //生物在移动,就播放移动的动画,路径和第2步的动作字符串对应
        if (event.isMoving()) {
            event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.dund.walk", true));
            return PlayState.CONTINUE;
        }
        
        //不动的话就播放闲置时的动画
        event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.dund.idle", true));
        return PlayState.CONTINUE;
    }
    
    //该状态播放器控制生物攻击时的各种动作
	private <E extends IAnimatable> PlayState predicate1(AnimationEvent<E> event) {
	    //如果生物攻击状态为1并且没有死亡,就执行这个攻击动画
		if (this.entityData.get(STATE) == 1 && !(this.dead || this.getHealth() < 0.01 || this.isDeadOrDying())) {
			event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.dund.attack", true));
			return PlayState.CONTINUE;
		}
		//如果生物攻击状态为2并且没有死亡,就执行这个攻击动画
//		if (this.entityData.get(STATE) == 2 && !(this.dead || this.getHealth() < 0.01 || this.isDeadOrDying())) {
//			event.getController().setAnimation(new AnimationBuilder().addAnimation("fire", true));
//			return PlayState.CONTINUE;
//		}
        //如果生物攻击状态为3并且没有死亡,就执行这个攻击动画
        //...
        
        //播完停止
		return PlayState.STOP;
	}
	
	//将我们之前的所有动画控制器进行注册
	@Override
	public void registerControllers(AnimationData data) {
        data.addAnimationController(new AnimationController(this, "controller",
                0, this::predicate));
        data.addAnimationController(new AnimationController(this, "controller1",
                0, this::predicate1));        
	}

	@Override
	public AnimationFactory getFactory() {
		// TODO Auto-generated method stub
		return this.factory;
	}
	
	//生物实体声音
	@Nullable
	protected SoundEvent getAmbientSound() {
	      return SoundEvents.RAVAGER_AMBIENT;
	}

	protected SoundEvent getHurtSound(DamageSource source) {
	      return SoundEvents.RAVAGER_HURT;
	}

	protected SoundEvent getDeathSound() {
	      return SoundEvents.RAVAGER_DEATH;
	}

	protected void playStepSound(BlockPos pos, BlockState blockstate) {
	      this.playSound(SoundEvents.RAVAGER_STEP, 0.15F, 1.0F);
	}

    static class DundAttackGoal extends MeleeAttackGoal {
        private final EntityDund zombie;

        public DundAttackGoal(EntityDund entity, double p_i46803_2_, boolean p_i46803_4_) {
            super(entity, p_i46803_2_, p_i46803_4_);
            this.zombie=entity;
        }
    }
    
    public boolean canBreakDoors() {
        return this.canBreakDoors;
    }
    
    protected boolean supportsBreakDoorGoal() {
        return true;
    }
    //怪物能不能破门而入
    public void setCanBreakDoors(boolean p_34337_) {
        if (this.supportsBreakDoorGoal() && GoalUtils.hasGroundPathNavigation(this)) {
           if (this.canBreakDoors != p_34337_) {
              this.canBreakDoors = p_34337_;
              ((GroundPathNavigation)this.getNavigation()).setCanOpenDoors(p_34337_);
              if (p_34337_) {
                 this.goalSelector.addGoal(1, this.breakDoorGoal);
              } else {
                 this.goalSelector.removeGoal(this.breakDoorGoal);
              }
           }
        } else if (this.canBreakDoors) {
           this.goalSelector.removeGoal(this.breakDoorGoal);
           this.canBreakDoors = false;
        }

     }
    
	public int getAttckingState() {
		return this.entityData.get(STATE);
	}
	
	public void setAttackingState(int time) {
		this.entityData.set(STATE, time);
	}

	@Override
	protected void defineSynchedData() {
		super.defineSynchedData();
		this.entityData.define(STATE, 0);
	}
	
	//我们生物的AI,决定使用哪种攻击方式
    static class AttackGoal extends Goal {
        private final EntityDund parentEntity;
        //攻击计时器
        protected int attackTimer = 0;

        public AttackGoal(EntityDund mob) {
             this.parentEntity = mob;
        }

        public boolean canUse() {
             return this.parentEntity.getTarget() != null;
        }

        public void start() {
             super.start();
             this.parentEntity.setAggressive(true);
        }

        @Override
        public void stop() {
             super.stop();
             this.parentEntity.setAggressive(false);
             this.parentEntity.setAttackingState(0);
             this.attackTimer = -1;
        }

        public void tick() {
             LivingEntity livingentity = this.parentEntity.getTarget();
             if (this.parentEntity.hasLineOfSight(livingentity)) {
                  Level world = this.parentEntity.level;
                  ++this.attackTimer;
                  Random rand = new Random();
                  Vec3 vector3d = this.parentEntity.getViewVector(1.0F);
                  double d0 = Math.min(livingentity.getY(), livingentity.getY());
                  double d1 = Math.max(livingentity.getY(), livingentity.getY()) + 1.0D;
                  double d2 = livingentity.getX() - (this.parentEntity.getX() + vector3d.x * 2.0D);
                  double d3 = livingentity.getY(0.5D) - (0.5D + this.parentEntity.getY(0.5D));
                  double d4 = livingentity.getZ() - (this.parentEntity.getZ() + vector3d.z * 2.0D);
                  float f = (float) Mth.atan2(livingentity.getZ() - parentEntity.getZ(),
                          livingentity.getX() - parentEntity.getX());

                  this.parentEntity.getNavigation().moveTo(livingentity, 1.5D);
                  if (this.attackTimer == 15) {
                      // 将该生物攻击状态设置为2
                      this.parentEntity.setAttackingState(2);
                  } 
                  else{
                      // 将该生物攻击状态设置为1
                	  this.parentEntity.setAttackingState(1);
                  }
                  
                  //计时器到头后,将攻击状态设置为0,同时计时器清0
                  if (this.attackTimer == 30) {
                       this.parentEntity.setAttackingState(0);
                       this.attackTimer = -5;
                  }
             } 
             else if (this.attackTimer > 0) {
                  --this.attackTimer;
             }
             this.parentEntity.lookAt(livingentity, 30.0F, 30.0F);
        }

   }
}

4.新建生物实体模型文件ModelDund类

ModelDund.java

package com.joy187.re8joymod.entity.model;

import com.joy187.re8joymod.Main;
import com.joy187.re8joymod.entity.EntityDund;
import com.joy187.re8joymod.entity.render.RenderDund;

import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.resources.ResourceLocation;
import software.bernie.geckolib3.model.AnimatedGeoModel;

            //我们的模型类要继承AnimatedGeoModel<T>类
public class ModelDund extends AnimatedGeoModel<EntityDund>{
    
	public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(new ResourceLocation(Main.MOD_ID, "dund"), "main");
    
    我们生物模型实体的路径
    @Override
    public ResourceLocation getModelLocation(EntityDund object) {
        return new ResourceLocation(Main.MOD_ID, "geo/dund.geo.json");
    }
    
    //我们生物皮肤材质的路径
    @Override
    public ResourceLocation getTextureLocation(EntityDund object) {
        //return RenderDund.LOCATION_BY_VARIANT.get(object.getVariant());
        return new ResourceLocation(Main.MOD_ID, "textures/entity/dund.png");
    }
    
    //我们生物皮肤动画的路径
    @Override
    public ResourceLocation getAnimationFileLocation(EntityDund animatable) {
        return new ResourceLocation(Main.MOD_ID, "animations/dund.animation.json");
    }

}

5.模型部分结束,开始着手渲染类的编写。新建RenderDund类。

RenderDund.java

package com.joy187.re8joymod.entity.render;

import com.joy187.re8joymod.Main;
import com.joy187.re8joymod.entity.EntityDund;
import com.joy187.re8joymod.entity.model.ModelDund;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;

import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.EntityRendererProvider.Context;
import net.minecraft.resources.ResourceLocation;
import software.bernie.geckolib3.model.AnimatedGeoModel;
import software.bernie.geckolib3.renderers.geo.GeoEntityRenderer;
            //我们的渲染类要继承GeoEntityRenderer<T>
public class RenderDund extends GeoEntityRenderer<EntityDund>{

	
    public RenderDund(EntityRendererProvider.Context renderManager) {
        super(renderManager, new ModelDund());
        //我们生物的阴影大小
        this.shadowRadius = 0.5f;
    }
    
    @Override
    public ResourceLocation getTextureLocation(EntityDund instance) {
        //return LOCATION_BY_VARIANT.get(instance.getVariant());
    	return new ResourceLocation(Main.MOD_ID, "textures/entity/dund.png");
    }

    @Override
    public RenderType getRenderType(EntityDund animatable, float partialTicks, PoseStack stack,
                                    MultiBufferSource renderTypeBuffer, VertexConsumer vertexBuilder, int packedLightIn,
                                    ResourceLocation textureLocation) {
        //如果是幼年动物,就把其按比例缩小
//        if(animatable.isBaby()) {
//            stack.scale(0.4F, 0.4F, 0.4F);
//        } else {
//            stack.scale(0.8F, 0.8F, 0.8F);
//        }
        //默认原来模型大小
    	stack.scale(1F, 1F, 1F);

        return super.getRenderType(animatable, partialTicks, stack, renderTypeBuffer, vertexBuilder, packedLightIn, textureLocation);
    }
}

6.在EntityInit.java中添加我们的生物信息:

    public static final RegistryObject<EntityType<EntityDund>> DUND1 = ENTITY_TYPES.register("dund",
            () -> EntityType.Builder.of(EntityDund::new, MobCategory.MONSTER).sized(1f,1.8f).setTrackingRange(30)
                    .build(new ResourceLocation(Main.MOD_ID, "dund").toString()));

ClientModEventSubscriber.java中添加我们的模型渲染、属性注册语句:

@Mod.EventBusSubscriber(modid = Main.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
public class ClientModEventSubscriber
{


    @SubscribeEvent
    public static void onRegisterRenderer(EntityRenderersEvent.RegisterRenderers event) {
		//添加渲染注册语句
        event.registerEntityRenderer(EntityInit.DUND1.get(), RenderDund::new);
        //event.registerEntityRenderer(EntityInit.DETONATOR_ENTITY.get(), RenderDetonatorEntity::new);

    }
    
    @SubscribeEvent
    public static void onAttributeCreate(EntityAttributeCreationEvent event) {
		//添加属性注册语句
        event.put(EntityInit.DUND1.get(), EntityDund.prepareAttributes().build());

    }
}

7.生物实体部分结束,接下来我们要给生物制作一个刷怪蛋:

在ItemInit类中添加我们的刷怪蛋物品:

  public static final RegistryObject<Item> DUND1_SPAWN_EGG = ITEMS.register("dund_spawn_egg", 
            () -> new ForgeSpawnEggItem(EntityInit.DUND1, 3093009, 65536, new Item.Properties().tab(Main.TUTORIAL_TAB)));

7.代码部分结束,来到资源包制作环节

resources\assets\你的modid中的lang包中的en_us.json添加刷怪蛋和生物实体英文名称:

	"item.re8joymod.dund_spawn_egg": "Mob Spawn Egg",
	"entity.re8joymod.dund": "Mob",

models\item包中添加刷怪蛋模型文件:

dund_spawn_egg.json

{
	"parent": "item/template_spawn_egg"
}

textures\entity中添加生物实体的皮肤贴图

新建一个geo包和animation包,把第二步中的模型和动画文件分别放进去

cr10.png

8.保存所有文件 -> 进行测试:

生物待机动画

11.png

生物攻击动画

12.png

你可以尽情创造!

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-05-18 17:58:07  更:2022-05-18 17:58:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 11:36:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码