Minecraft 1.18.1、1.18.2模组开发 20.生物AI(人工智能)

我们今天简单介绍一下MC中的一些生物智能,以及实现一个自定义AI到自己的生物身上

猫狗会被驯服,僵尸会被太阳晒,墨鱼会游泳,劫掠兽会进行AOE攻击…

1.首先,我的世界的所有的生物ai都会使用addGoal函数添加:

                            //参数:ai优先级(越小越优先),特定ai函数
        this.goalSelector.addGoal(1, new EntityUrias.AttackGoal(this));
所有的ai基本都在registerGoals()中进行声明与注册:
    //ai注册函数
    @Override
    protected void registerGoals() {
        super.registerGoals();
        //行为选择器goalSelector
        this.goalSelector.addGoal(1, new EntityUrias.AttackGoal(this));
        this.goalSelector.addGoal(1, new RandomLookAroundGoal(this));
        this.goalSelector.addGoal(2, new LookAtPlayerGoal(this, Player.class, 8.0F));
        //this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 0, false, false, LIVING_ENTITY_SELECTOR));
        this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D));
        
        //攻击目标选择器targetSelector
        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));

    }

2.一般来讲生物的行为选择器的可用ai大致包括这些:

优先级名称
0FloatGoal(水面漂浮)
1PanicGoal(受到伤害,被攻击后会逃跑)
2BreedGoal (可被喂养)
3TemptGoal(跟随小麦)
4LandOnOwnersShoulderGoal(落到主人肩膀上)
5EatBlockGoal(吃草)
6WaterAvoidingRandomStrollGoal(在水中不会随便活动)
7LookAtPlayerGoal(看着玩家)
8FollowOwnerGoal(跟随主人)
9RandomLookAroundGoal(四下张望)

3.一般生物的目标选择器的可用ai大致包括这些:

优先级名称
0HurtByTargetGoal(可被攻击)
1NearestAttackableTargetGoal (最近攻击单位)
2AttackGoal (攻击行为ai)

4.为了自定义一个我们的ai,专门用于攻击其他生物,可以在我们的生物类中新建一个AttackGoal类:

    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);
                  //生物时间刻到达15时,就设定攻击状态为2,否则就设为1
                  if (this.attackTimer == 15) {
                      this.parentEntity.setAttackingState(2);
                  } 
                  else{
                	  this.parentEntity.setAttackingState(1);
                  }
                  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);
        }
   }
该类写好后便可以在registerGoal中进行注册:
    protected void addBehaviourGoals() {
		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));
     }

你可以根据自己的需求让你的生物变得多姿多彩~

本文生物类Demo的完整代码

EntityDund.java

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() {
        //注册我们生物的自定义攻击ai
        this.goalSelector.addGoal(2, new DundAttackGoal(this, 1.0D, false));
        this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
        this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
        this.goalSelector.addGoal(1, new EntityDund.AttackGoal(this));
        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, 30.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));
            }
        	this.playAttackAnimation=true;
            return true;
        }
        //System.out.println(attackTick);
        //System.out.println(attackTick);
    }
    
    private <E extends IAnimatable> PlayState predicate(AnimationEvent<E> event) {
        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) {
		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;
		}
		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) {
                      this.parentEntity.setAttackingState(2);
                  } 
                  else{
                	  this.parentEntity.setAttackingState(1);
                  }
                  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);
        }
   }
}
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jay_fearless

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值