Всем здаров! Делаю сейчас мод на животных, написал что-то типа кастомного поведения для утки, используя код черепахи + небольшую самописную цель, и столкнулся с проблемой: когда спавнишь утку, всё окей, плавает по воде, ходит по суше, етц. Но как только перезаходишь в мир, всё, кирдык, держится ровно на воде и только корпусом в разные стороны вертит. В чём может быть проблема, и как вы бы решили эту проблему?
P.S подход с черепахой (сохранение в данных энтити координат, куда оно двигалось) не пробовал, не хочу слишком усложнять.
Код утки:
кастомная цель удержания на воде:
P.S подход с черепахой (сохранение в данных энтити координат, куда оно двигалось) не пробовал, не хочу слишком усложнять.
Код утки:
Java:
package net.epichunt.entity.animals;
import com.google.common.base.Suppliers;
import net.epichunt.entity.SurfaceSwimGoal;
import net.epichunt.sound.Sounds;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.*;
import net.minecraft.world.entity.ai.navigation.AmphibiousPathNavigation;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import java.util.function.Supplier;
public class DuckEntity extends Animal {
public DuckEntity(EntityType<? extends Animal> entityType, Level level) {
super(entityType, level);
}
public static final Supplier<EntityType<DuckEntity>> DUCK = Suppliers.memoize(() -> EntityType.Builder.of(DuckEntity::new, MobCategory.CREATURE)
.sized(0.7f, 0.7f).build("duck"));
@Override
protected void updateWalkAnimation(float partialTick) {
float f;
if (this.getPose() == Pose.STANDING) {
f = Math.min(partialTick * 6F, 1f);
} else {
f = 0f;
}
this.walkAnimation.update(f, 0.4f);
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new PanicGoal(this, 1.7));
this.goalSelector.addGoal(2, new FollowParentGoal(this, 1.1D));
this.goalSelector.addGoal(4, new SurfaceSwimGoal(this));
this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1f, 10));
this.goalSelector.addGoal(6, new DuckRandomStrollGoal(this, 6f, 10));
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 3f));
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
}
protected PathNavigation createNavigation(Level arg) {
return new DuckNavigation(this, arg);
}
public static AttributeSupplier.Builder createAttributes() {
return Animal.createLivingAttributes().add(Attributes.MAX_HEALTH, 4D).add(Attributes.MOVEMENT_SPEED, 0.23000000298023224)
.add(Attributes.FOLLOW_RANGE, 25D);
}
private static class DuckNavigation extends AmphibiousPathNavigation {
DuckNavigation(Mob mob, Level level) {
super(mob, level);
}
@Override
public boolean isStableDestination(BlockPos pos) {
if (this.mob.isInWater()) {
return this.level.getBlockState(pos).is(Blocks.WATER); // Вода
} else {
return !this.level.getBlockState(pos.below()).isAir(); // Суша
}
}
}
@Nullable
@Override
public AgeableMob getBreedOffspring(ServerLevel serverLevel, AgeableMob ageableMob) {
return DUCK.get().create(serverLevel);
}
@Override
public SoundEvent getEatingSound(ItemStack itemStack) {
return Sounds.DEER_EAT.get();
}
@Nullable
@Override
protected SoundEvent getAmbientSound() {
return Sounds.DEER_AMBIENT.get();
}
@Nullable
@Override
protected SoundEvent getHurtSound(DamageSource damageSource) {
return Sounds.DEER_HURT.get();
}
@Nullable
@Override
protected SoundEvent getDeathSound() {
return Sounds.DEER_DEATH.get();
}
protected void playStepSound(BlockPos blockPos, BlockState blockState) {
this.playSound(SoundEvents.CHICKEN_STEP, 0.15F, 1.0F);
}
protected float getSoundVolume() {
return 0.3F;
}
public class DuckRandomStrollGoal extends RandomStrollGoal {
private final DuckEntity duck;
DuckRandomStrollGoal(DuckEntity arg, double d, int i) {
super(arg, d, i);
this.duck = arg;
}
public boolean canUse() {
return this.mob.isInWater() ? super.canUse() : false;
}
}
}
кастомная цель удержания на воде:
Java:
package net.epichunt.entity;
import net.minecraft.world.entity.Mob;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.phys.Vec3;
public class SurfaceSwimGoal extends Goal {
private final Mob mob;
public SurfaceSwimGoal(Mob mob) {
this.mob = mob;
}
@Override
public boolean canUse() {
return mob.isInWater() && mob.getFluidHeight(FluidTags.WATER) > mob.getFluidJumpThreshold();
}
@Override
public boolean requiresUpdateEveryTick() {
return true;
}
@Override
public void tick() {
double surfaceY = mob.getFluidHeight(FluidTags.WATER) + mob.getBlockY();
if (mob.getY() < surfaceY) {
Vec3 currentMovement = mob.getDeltaMovement();
double verticalSpeed = Math.min(0.1, surfaceY - mob.getY());
mob.setDeltaMovement(currentMovement.x, verticalSpeed, currentMovement.z);
}
}
}
Последнее редактирование: