iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 27
1

https://ithelp.ithome.com.tw/upload/images/20191012/20115823zoKOq3etbU.png

那們我們今天呢,就用自定義AI的功能來做一隻孤僻的貓吧!

建立檔案

我們先把需要的檔案先建立好,一共有:

  1. com.ithome.mymod.entities.LonerCat : 孤僻貓實體類別。
  2. com.ithome.mymod.ai.EntityAILoner : 自定義的AI工作,用來控制孤僻貓的行為。

自定義實體內容

LonerCat這個檔案內,我們會繼承現有的EntityOcelot類別,並且自訂我們自己的AI行為:

  1. LonerCat繼承EntityOcelot後,會跳出紅色底線要我們建立建構子方法:
    public class LonerCat extends EntityOcelot {
        public LonerCat(World worldIn) {
            super(worldIn);
            // 設置AI
            setupUI();
        }
    }
    
  2. 接著,在該建構子下方定義我們的AI行為方法,主要是將現有的AI都先清除,並且把我們自定義AI工作的優先順序放到第一個:
    private void setupUI() {
        this.tasks.taskEntries.clear();
        this.targetTasks.taskEntries.clear();
        // 自定義的AI工作
        tasks.addTask(0, new EntityAILoner(this));
    }
    
  3. 我們還需要定義三個全域變數在類別內,變數的用途在下方各自說明:
    // 孤僻貓的類別ID
    public static final String name = "LonerCat";
    // 是否為第一次與其它實體碰撞
    public static AtomicBoolean firstHit = new AtomicBoolean(true);
    // 與這個實體碰撞的玩家
    public static EntityPlayer thePlayer;
    
  4. 最後,我們希望當玩家與孤僻貓碰撞時,能夠取得碰撞的玩家物件EntityPlayer與是否為第一次碰撞;在這個EntityOcelot類別內,有一個onCollideWithPlayer方法可以供我們覆寫並且定義自己的動作:
    @Override
    public void onCollideWithPlayer(EntityPlayer player) {
        // 是第一次被碰撞 (第一個參數先判斷為true後,就透過第二個參數將這個firstHit更改為false)
        if(firstHit.compareAndSet(true, false)) {
            // 將AI工作內的判斷變數改為true
            EntityAILoner.isHit = true;
            // 設定玩家物件
            thePlayer = player;
        }
    }
    

自定義AI工作內容

EntityAILoner這個類別中,我們需要繼承EntityAIBase這個類別,並且實作方法:

  • shouldExecute : 判斷這個AI工作是否需要執行。
  • startExecuting : 開始執行AI工作;我們在這裡定義孤僻貓的行為。
  • continueExecuting : 判斷這一輪的AI工作是否已經完成,或是需要繼續。
  1. 將此類別繼承EntityAIBase
    public class EntityAILoner extends EntityAIBase {
    }
    
  2. 然後我們定義三個必須的變數在這個類別內:
    // 定義實體的物件
     private final EntityAnimal animal;
     // 用來判斷是否需要執行這個AI工作
     public static boolean isHit = false;
     // 判斷碰撞的時間,避免太頻繁執行
     public static long timer;
    
  3. 接著,定義建構子方法 (這裡的MutexBit是為了之後使用這個AI工作時,可以與其他AI工作同時發生):
    public EntityAILoner(EntityAnimal entity) {
        this.animal = entity;
        // 7可以與大部分的AI工作同時發生
        setMutexBits(7);
    }
    
  4. 覆寫shouldExecute方法:
    @Override
    public boolean shouldExecute() {
        // 如果碰撞了,開始執行AI工作
        if(isHit) {
            // 將該變數設定為false等待下次碰撞發生
            isHit = false;
            return true;
        } else {
            // 其餘狀況都不執行AI工作
            return false;
        }
    }
    
  5. 覆寫startExecuting方法:
    @Override
    public void startExecuting() {
        // 往碰撞的玩家送出隨機的訊息
        LonerCat.thePlayer.addChatComponentMessage(new ChatComponentText(
                EnumChatFormatting.RED + messages.get(new Random().nextInt(messages.size()))
        ));
        // 設定執行時間
        timer = System.currentTimeMillis();
    }
    
  6. 然後是覆寫continueExecuting方法:
    @Override
    public boolean continueExecuting() {
        // 若當前時間與執行時間相差在2秒內,就繼續這個AI工作來避免下一輪的觸發 (避免太頻繁)
        if(System.currentTimeMillis() - timer < 2 * 1000) {
            return true;
        } else {
            // 此次AI工作完成,讓LonerCat實體可以開始下一次的碰撞判斷事件了
            LonerCat.firstHit.compareAndSet(false, true);
            return false;
        }
    }
    
  7. 加入一些孤僻AI工作的訊息在這個類別吧!
    private static List<String> messages = new ArrayList<String>();
    static {
        messages.add("Don't touch me!");
        messages.add("Mew~~~~~~~");
        messages.add("Are you alone?");
        messages.add("Do you wanna be a cat?");
        messages.add("What does the fox say?");
    }
    

實體註冊

最後一步驟,來到MyMod類別中,在preInit這個方法內需要註冊我們的自定義實體LonerCat

@EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        int availableId = 1;
        while (EntityList.getClassFromID(availableId) != null) {
            availableId++;
        }
        EntityRegistry.registerGlobalEntityID(EntityScalePig.class, EntityScalePig.name, availableId);

        // 註冊LonerCat類別,找一個可以用的ID給它
        while (EntityList.getClassFromID(availableId) != null) {
            availableId++;
        }
        EntityRegistry.registerGlobalEntityID(LonerCat.class, LonerCat.name, availableId);
...(略)

前半段是我們俄羅斯豬的註冊,在後面加上孤僻貓的註冊。

好了,至此你已經完成一個簡單的自定義AI工作,到遊戲內你可以透過:

/summon LonerCat ~2 ~ ~

來產生我們的孤僻貓,然後嘗試去碰牠看看會產生什麼事吧!


上一篇
[Day26] 來玩玩實體AI吧 (上)
下一篇
[Day28] 載入其他模組
系列文
[Minecraft - 當個創世神] 從玩遊戲到設計遊戲30

尚未有邦友留言

立即登入留言