Простенький ИИ

Простенький ИИ

Нет прав для скачивания
Версия(и) Minecraft
1.12.2
Требования: достойное знание Java, знание как создавать своего Entity

Часть 1
Что за ИИ-задачи?
Для начала выясним как работает ИИ в Minecraft.

В классе EntityLiving есть список возможных заданий (или действий) и у каждого задания есть свой приоритет. Сильнее приоритет, тот который меньше другого. Допустим чтобы моб не тонул, когда пытается ударить кого-то, то приоритет на плавание нужно поставить 0, а приоритет на преследование 1. Некоторые виды задач срабатывают без задержки (к примеру плавание), а некоторые с задержкой, пока задача не выполниться (пока моб не убьёт кого-то).

В обычном Minecraft есть следующие ИИ-задачи:
  • EntityAISwimming - плавать
  • EntityAIAttackMelee - атака врукопашную
  • EntityAIWanderAvoidWater - блуждать избегая воды
  • EntityAIWatchClosest - смотреть на кого-то/что-то
  • EntityAILookIdle - смотреть в другую сторону
  • EntitySquid.AIMoveRandom - двигаться хаотично (как спрут)
  • EntityAIAvoidEntity - избегать сущность
  • EntityAIFleeSun - убегать от солнца (как зомби)
  • EntityAITempt - преследовать игрока, у которого какой-либо предмет в руках
  • EntityAIMate - спаривание
  • EntityAIPanic - паника от урона
  • EntityAIFollowParent - преследовать родителей (только для EntityAnimal)
  • EntityAIFollowOwner - преследовать владельца (только для EntityTameable)
  • EntityAIFollowOwnerFlying - лететь за владельцем
  • EntityAIWanderAvoidWaterFlying - блуждать летя
  • EntityAIAttackRanged - атака издали
У некоторых ИИ-задач при инициализации нужно указывать радиусы, классы сущностей и прочее. Обязательно надо в аргументах указать саму сущность нужного типа.
При атаке издали выполняется метод public void attackEntityWithRangedAttack(EntityLivingBase target, float distanceFactor) и с помощью его можно сделать выстреливание стрелы, снежка и прочего.

Также можно установить разные приоритетные цели для атаки. К примеру, для того чтобы чтобы моб отбивался от того кто его ударяет, а не случайных окружающих.
Есть список целей для атак. Их есть несколько:
  • EntityAIHurtByTarget - тот кто ударил
  • EntityAINearestAttackableTarget - ближайшая сущность
  • EntityAITargetNonTamed - сущности без бирок
  • EntityAIOwnerHurtTarget - тот кто ударил владельца
  • EntityAIOwnerHurtByTarget - тот кого ударил владелец
Часть 2
Добавление ИИ-задач в сущность
Чтобы добавлять ИИ-задачи в классе Entity нужно добавить следующий метод:
Java:
protected void initEntityAI() {

}
Для добавления задачи нужно написать tasks.addTask(priorety, task);
Пример:
Java:
protected void initEntityAI() {
    tasks.addTask(0, new EntityAISwimming(this));
    tasks.addTask(1, new EntityAIAttackMelee(this, 0.6D, false)); // 0.6D - скорость
    tasks.addTask(2, new EntityAIWanderAvoidWater(this, 0.6D)); // 0.6D - скорость
    tasks.addTask(3, new EntityAIWatchClosest(this, EntityPlayer.class, 6F)); // 6F - дистанция
    tasks.addTask(4, new EntityAILookIdle(this));
}
Для добавление целей нужно написать targetTasks.addTask(priorety, task);
Пример:
Java:
protected void initEntityAI() {
    tasks.addTask(0, new EntityAISwimming(this));
    tasks.addTask(1, new EntityAIAttackMelee(this, 0.6D, false));
    tasks.addTask(2, new EntityAIWanderAvoidWater(this, 0.6D));
    tasks.addTask(3, new EntityAIWatchClosest(this, EntityPlayer.class, 6F));
    tasks.addTask(4, new EntityAILookIdle(this));
    targetTasks.addTask(0, new EntityAINearestAttackableTarget(this, EntityAnimal.class, true));
}

Теперь этот моб плавает, атакует животных и ведёт себя как обычная сущность!
Часть 3
Создание своих ИИ-задач
Чтобы сделать свою задачу нужно создать класс наследованный EntityAIBase. В данном классе есть методы, которые и выполняют саму логику работы задачи.
public boolean shouldExecute() - будет ли выполняться данная задача? Если нет, то пытаемся выполнить другую задачу.
public boolean shouldContinueExecuting() - будет ли ещё выполняться эта задача? Если нет, то задача прервалась.
public void startExecuting() - метод срабатывающий, когда задача начинает выполняться.
public void resetTask() - метод срабатывающий, когда задача прерывается.
public void updateTask() - метод выполнения задачи.

Для примера создадим ИИ-задачу, которая будет пытаться взять все предметы вокруг.
Java:
public class EntityAIItemPickup extends EntityAIBase {

    public EntityLiving living;
    public Path path;
    public EntityAIItemPickup.Sorter sorter;

    public EntityAIItemPickup(EntityLiving living) {
        this.living = living;
  
        sorter = new EntityAIItemPickup.Sorter(living);
  
        setMutexBits(1);
    }

    @Override
    public boolean shouldExecute() {
  
        List<EntityItem> list = living.world.getEntitiesWithinAABB(EntityItem.class, getTargetableArea(10D)); // все предметы в радиусе действия
  
        if (list.isEmpty()) { // нету предметов?
            return false;
        } else { // есть предметы?
            Collections.sort(list, sorter);
            targetItem = list.get(0); // самый близкий предмет
        }
  
        if (targetItem == null) {
            return false;
        } else {
            path = living.getNavigator().getPathToEntityLiving(targetItem); // поиск пути до предмета
      
            return path != null;
        }
    }

    /*
     * Создание зоны, в которой сущность будет брать предметы
    */
    protected AxisAlignedBB getTargetableArea(double targetDistance) {
        return living.getEntityBoundingBox().grow(targetDistance, 4.0D, targetDistance);
    }

    public void resetTask() {
        living.getNavigator().clearPath(); // обнуляем путь
    }

    public void updateTask() {
        living.getLookHelper().setLookPositionWithEntity(targetItem, 30F, 30F); // чтобы сущность смотрела на предмет
  
        double d = living.getDistanceSq(targetItem.posX, targetItem.posY, targetItem.posZ); // дистанция от сущности до предмета
  
        living.getNavigator().tryMoveToEntityLiving(targetItem, 0.5D); // пробуем добраться до предмета
  
        if (d <= 1D) { // если предмет очень близко
            living.onItemPickup(targetItem, targetItem.getItem().getCount()); // делаем эффект взятия в инвентарь
            // берём в инвентарь
            targetItem.setDead(); // убираем предмет
        }
    }

    public void startExecuting() {
        living.getNavigator().setPath(path, 0.5D); // задаём путь к предмету
    }

    /*
     * Сортировка предметов по дальности от сущности, чтобы взять самый близкий предмет
    */
    public static class Sorter implements Comparator<EntityItem> {
        private final EntityLiving entity;
  
        public Sorter(EntityLiving entity) {
            this.entity = entity;
        }
  
        public int compare(EntityItem e1, EntityItem e2) {
            double d0 = entity.getDistanceSq(e1);
            double d1 = entity.getDistanceSq(e2);
      
            if (d0 < d1) {
                return -1;
            } else {
                return d0 > d1 ? 1 : 0;
            }
        }
    }
}
Автор
MaximPixel
Скачивания
1
Просмотры
3,663
Первый выпуск
Обновление
Оценка
4.75 звёзд 4 оценок

Другие ресурсы пользователя MaximPixel

Последние рецензии

Круто, коротко и ясно, особенно порадовал пример создания своей таски.

Я бы разве что переименовал, "Простенький AI", знаю что по факту то же самое, но именно так длинно и с Больших Букв выглядит слишком претенциозно, я как-то чуть ли не потных нейросеток ожидать начал
MaximPixel
MaximPixel
Спасибо за критику! Мне тоже казалось что с названием что-то не так.
Спасибо за полезную статью! И как же она для меня вовремя вышла! Уже запарился изобретать велосипеды и подпирать костылями свой код, дабы получить нужный эффект у моба! Хорошо, что есть люди, готовые оказать помощь!
MaximPixel
MaximPixel
Может ещё есть для тебя неизведанные темы?
Молодец, что распахиваешь новые темы
Хороший ресурс, ждём следующих обновлений. Интересно как например сделать чтобы моб при определённых действиях игрока отзеркаливал его движения как клон.
Сверху