我们今天简单介绍一下MC中的一些生物智能,以及实现一个自定义AI到自己的生物身上
猫狗会被驯服,僵尸会被太阳晒,墨鱼会游泳,劫掠兽会进行AOE攻击…
1.首先,我的世界的所有的生物ai都会使用addGoal函数添加:
this.goalSelector.addGoal(1, new EntityUrias.AttackGoal(this));
所有的ai基本都在registerGoals()中进行声明与注册:
@Override
protected void registerGoals() {
super.registerGoals();
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.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));
}
2.一般来讲生物的行为选择器
的可用ai大致包括这些:
优先级 |
名称 |
0 |
FloatGoal(水面漂浮) |
1 |
PanicGoal(受到伤害,被攻击后会逃跑) |
2 |
BreedGoal (可被喂养) |
3 |
TemptGoal(跟随小麦) |
4 |
LandOnOwnersShoulderGoal(落到主人肩膀上) |
5 |
EatBlockGoal(吃草) |
6 |
WaterAvoidingRandomStrollGoal(在水中不会随便活动) |
7 |
LookAtPlayerGoal(看着玩家) |
8 |
FollowOwnerGoal(跟随主人) |
9 |
RandomLookAroundGoal(四下张望) |
3.一般生物的目标选择器
的可用ai大致包括这些:
优先级 |
名称 |
0 |
HurtByTargetGoal(可被攻击) |
1 |
NearestAttackableTargetGoal (最近攻击单位) |
2 |
AttackGoal (攻击行为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);
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));
}
你可以根据自己的需求让你的生物变得多姿多彩~
本文生物类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() {
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));
}
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;
}
}
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() {
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);
}
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);
}
}
}