iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 22
1

今日目標要把剩下的處理完,讓我們繼續下去吧,加油!

  1. 我們先處理[Day9]的部分。先將[Day6]PigDoll移除,因為這個算是半成品;再來我們將PigDoll_v2這個類別移動到com.ithome.mymod.events下,並且重新命名為PigDoll
    https://ithelp.ithome.com.tw/upload/images/20191006/20115823SDcTMX2hgZ.png

    進入到PigDoll這個檔案,內容可以參考[Day9],我們這邊需要把自行處理Prxoy的部分移除(就是所有的@SideOnly(Side.CLIENT)都拿掉):
    移除後的PigDoll長這樣

    package com.ithome.mymod.events;
    
    import com.ithome.mymod.entities.EntityScalePig;
    import com.ithome.mymod.entities.RenderScalePig;
    import net.minecraft.client.Minecraft;
    import net.minecraft.client.model.ModelPig;
    import net.minecraft.entity.ai.EntityAIBase;
    import net.minecraft.entity.passive.EntityPig;
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraftforge.client.event.RenderLivingEvent;
    import net.minecraftforge.event.entity.EntityJoinWorldEvent;
    import net.minecraftforge.event.entity.living.LivingDropsEvent;
    import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
    import net.minecraftforge.fml.relauncher.Side;
    import net.minecraftforge.fml.relauncher.SideOnly;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicBoolean;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class PigDoll {
    
        private float scale = 1.0f;
        private AtomicInteger count = new AtomicInteger(0);
        private AtomicBoolean flag = new AtomicBoolean(false);
        private List<String> firstRenderPig = new ArrayList<String>();
    
        @SubscribeEvent
        public void preRender(RenderLivingEvent.Pre event) {
            if (event.entity.getCustomNameTag().startsWith("myPig")) {
    
                String tag = event.entity.getCustomNameTag();
                float parserScale = Float.parseFloat(tag.split("-")[1]);
    
                if (flag.compareAndSet(false, true)) {
                     Minecraft.getMinecraft().getRenderManager().entityRenderMap.put(EntityScalePig.class,
                             new RenderScalePig(Minecraft.getMinecraft().getRenderManager(), new ModelPig(), 0.5f * parserScale));
                }
    
                if(event.renderer instanceof RenderScalePig) {
                    RenderScalePig render = (RenderScalePig) event.renderer;
                    render.setScale(parserScale);
                }
    
                if(!firstRenderPig.contains(tag)) {
                    EntityScalePig pig = (EntityScalePig) event.entity;
                    pig.scale(parserScale);
                    firstRenderPig.add(tag);
                }
            }
        }
    
        @SubscribeEvent
        public void pigDoll(EntityJoinWorldEvent event) {
            if (event.entity.worldObj.isRemote) return;
            if (event.world.playerEntities.isEmpty()) return;
    
            EntityPlayer player = (EntityPlayer) event.entity.worldObj.playerEntities.get(0);
            float distance = event.entity.getDistanceToEntity(player);
    
            if (distance < 7.0f) {
                if (EntityPig.class.isAssignableFrom(event.entity.getClass())) {
    
                    if(!event.entity.getCustomNameTag().startsWith("myPig")) {
                        event.entity.setDead();
                    }
    
                    if(count.compareAndSet(5, 0)) {
                        scale = 1.0f;
                        firstRenderPig.clear();
                        return;
                    }
    
                    scale = scale * 0.8f;
                    EntityScalePig pig = new EntityScalePig(event.entity.worldObj);
                    pig.setPosition(event.entity.posX - 1.2f, event.entity.posY, event.entity.posZ);
                    pig.setCustomNameTag(String.format("myPig-%f", scale));
    
                    // 將豬的AI行為去除
                    EntityAIBase ai = new EntityAIBase() {
                        @Override
                        public boolean shouldExecute() {
                            return true;
                        }
                    };
                    ai.setMutexBits(0xFFFFFF);
                    pig.tasks.addTask(0, ai);
    
                    count.getAndIncrement();
                    event.entity.worldObj.spawnEntityInWorld(pig);
                }
            }
        }
    }
    

    然後把註冊動作放到ClientProxyinit()內:

    MinecraftForge.EVENT_BUS.register(new PigDoll());
    

    最後,註冊我們EntityScalePig的動作是Client與Server都需要的(因為他們都會用到這個實體),在Proxy的概念下,如果不需要去關心是屬於Client或Server,就放在主程式內。因此我們把註冊EntityScalePig實體的動作放到MyModpreInit()內:

    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        int availableId = 1;
        while (EntityList.getClassFromID(availableId) != null) {
            availableId++;
        }
        EntityRegistry.registerGlobalEntityID(EntityScalePig.class, EntityScalePig.name, availableId);
    
        proxy.preInit(event);
    }
    
  2. [Da11]是指令部分,所以我們再建立一個叫做com.ithome.mymod.commands的套件目錄,並且將BurningPigs移動到這個套件內。
    https://ithelp.ithome.com.tw/upload/images/20191006/20115823rf33Nt6zwC.png

    由於指令需要註冊到FMLServerStartingEvent這個事件內,我們先回到IProxy介面定義方法,然後將指令註冊放到ServerProxy內(因為這個事件是Server端的)。注意:雖然ClientProxy不會用到這個事件,但還是需要定義!
    IProxy

    package com.ithome.mymod.proxy;
    
    import net.minecraftforge.fml.common.event.FMLInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
    
    public interface IProxy {
        /**
         * 準備Minecraft的所有物體。舉凡讀取設定檔、建立方塊、物品,註冊物體到Minecraft等。
         *
         * @param event 事件
         */
        void preInit(FMLPreInitializationEvent event);
    
        /**
         * 模組設置。例如註冊事件處理、註冊資源、建立配方(合成、燒煉與釀造)。
         *
         * @param event 事件
         */
        void init(FMLInitializationEvent event);
    
        /**
         * 與其他模組互動。如果有任何其他需要處理的的功能,或是要建立與其他模組的相依關係。
         *
         * @param event 事件
         */
        void postInit(FMLPostInitializationEvent event);
    
        /**
         * 註冊伺服器指令的地方,這個事件只會被Server呼叫。
         *
         * @param event 事件
         */
        void serverStarting(FMLServerStartingEvent event);
    }
    

    ClientProxy

    package com.ithome.mymod.proxy;
    
    import com.ithome.mymod.commands.Colosseum;
    import com.ithome.mymod.events.*;
    import net.minecraftforge.common.MinecraftForge;
    import net.minecraftforge.fml.common.FMLCommonHandler;
    import net.minecraftforge.fml.common.event.FMLInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
    
    public class ClientProxy implements IProxy {
        @Override
        public void preInit(FMLPreInitializationEvent event) {
    
        }
    
        @Override
        public void init(FMLInitializationEvent event) {
            MinecraftForge.EVENT_BUS.register(new BlockBreakEvent());
            MinecraftForge.EVENT_BUS.register(new TNTExplosions());
            MinecraftForge.EVENT_BUS.register(new PigDroppingItems());
            MinecraftForge.EVENT_BUS.register(new ZombieKnights());
            MinecraftForge.EVENT_BUS.register(new SuperJump());
            MinecraftForge.EVENT_BUS.register(new PigDoll());
            FMLCommonHandler.instance().bus().register(new SuperJump());
        }
    
        @Override
        public void postInit(FMLPostInitializationEvent event) {
    
        }
    
        @Override
        public void serverStarting(FMLServerStartingEvent event) {
            // 這個事件不應該不呼叫
        }
    }
    

    ServerProxy

    package com.ithome.mymod.proxy;
    
    import com.ithome.mymod.commands.BurningPigs;
    import net.minecraftforge.fml.common.event.FMLInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
    
    public class ServerProxy implements IProxy {
        @Override
        public void preInit(FMLPreInitializationEvent event) {
    
        }
    
        @Override
        public void init(FMLInitializationEvent event) {
    
        }
    
        @Override
        public void postInit(FMLPostInitializationEvent event) {
    
        }
    
        @Override
        public void serverStarting(FMLServerStartingEvent event) {
            event.registerServerCommand(new BurningPigs());
        }
    }
    

    最後,我們需要在MyMod檔案內把這個新的事件FMLServerStartingEvent加上,並標註@EventHandler
    MyMod

    package com.ithome.mymod;
    
    import com.ithome.mymod.configs.Constants;
    import com.ithome.mymod.entities.EntityScalePig;
    import com.ithome.mymod.proxy.IProxy;
    import net.minecraft.entity.EntityList;
    import net.minecraftforge.fml.common.Mod;
    import net.minecraftforge.fml.common.Mod.EventHandler;
    import net.minecraftforge.fml.common.SidedProxy;
    import net.minecraftforge.fml.common.event.FMLInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
    import net.minecraftforge.fml.common.registry.EntityRegistry;
    
    @Mod(modid = Constants.MOD_ID, name = Constants.MOD_NAME, version = Constants.VERSION)
    public class MyMod {
    
        @SidedProxy(serverSide = Constants.SERVER_PROXY, clientSide = Constants.CLIENT_PROXY)
        public static IProxy proxy;
    
        @EventHandler
        public void preInit(FMLPreInitializationEvent event) {
            int availableId = 1;
            while (EntityList.getClassFromID(availableId) != null) {
                availableId++;
            }
            EntityRegistry.registerGlobalEntityID(EntityScalePig.class, EntityScalePig.name, availableId);
    
            proxy.preInit(event);
        }
    
        @EventHandler
        public void init(FMLInitializationEvent event) {
            proxy.init(event);
        }
    
        @EventHandler
        public void postInit(FMLPostInitializationEvent event) {
            proxy.postInit(event);
        }
    
        @EventHandler
        public void serverStarting(FMLServerStartingEvent event)
        {
            proxy.serverStarting(event);
        }
    }
    
  3. [Day12][Day13]一樣是使用指令,同前一個步驟,所以這裡只將有變更的地方放上來:
    https://ithelp.ithome.com.tw/upload/images/20191006/20115823dtmIAEnu12.png

    ClientProxy的init()方法:

    MinecraftForge.EVENT_BUS.register(new Colosseum());
    

    ServerProxy的serverStarting()方法:

    event.registerServerCommand(new ColosseumFillCommand());
    
  4. [Day14][Day17]是新的物品與方塊,這個是屬於註冊物體的工作,會放在preInit()方法內。因為註冊物品與Client或Server端無關,我們一樣直接放到MyMod內就好:
    MyMod

    // 自定義的方塊
    public static Block enderBlock;
    
    // 自定義的物品
    public static Item pillItem;
    
    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        int availableId = 1;
        while (EntityList.getClassFromID(availableId) != null) {
            availableId++;
        }
        EntityRegistry.registerGlobalEntityID(EntityScalePig.class, EntityScalePig.name, availableId);
    
        enderBlock = new EnderBlock();
        GameRegistry.registerBlock(enderBlock, "enderBlock");
        pillItem = new PillItem();
        GameRegistry.registerItem(pillItem, "pillItem");
    
        proxy.preInit(event);
    }
    
    @EventHandler
    public void init(FMLInitializationEvent event) {
        // 從方塊轉換成物品的方法
        Item item = Item.getItemFromBlock(enderBlock);
        // 定義方塊資源目錄
        // 第一個參數是"{模組ID}:方塊註冊名稱"
        // 第二個參數請使用"inventory",表示從資源庫內尋找
        ModelResourceLocation location = new ModelResourceLocation(
                "myfancymods:enderBlock",
                "inventory"
        );
        // 註冊這個方塊物品的資源到Forge內
        Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
                .register(item, 0, location);
    
        // 註冊藥丸物品的資源到Forge內
        Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
                .register(pillItem, 0, new ModelResourceLocation(
                        "myfancymods:pillItem",
                        "inventory"
                ));
    
        proxy.init(event);
    }
    
  5. 最後在[Day18][Day19],是屬於配方註冊。直接就想到將這部分的邏輯放到MyMod底下:
    MyMod

    @EventHandler
    public void init(FMLInitializationEvent event) {
        // 從方塊轉換成物品的方法
        Item item = Item.getItemFromBlock(enderBlock);
        // 定義方塊資源目錄
        // 第一個參數是"{模組ID}:方塊註冊名稱"
        // 第二個參數請使用"inventory",表示從資源庫內尋找
        ModelResourceLocation location = new ModelResourceLocation(
                "myfancymods:enderBlock",
                "inventory"
        );
        // 註冊這個方塊物品的資源到Forge內
        Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
                .register(item, 0, location);
    
        // 註冊藥丸物品的資源到Forge內
        Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
                .register(pillItem, 0, new ModelResourceLocation(
                        "myfancymods:pillItem",
                        "inventory"
                ));
    
        GameRegistry.addShapedRecipe(
                new ItemStack(enderBlock),
                "X X",
                " X ",
                "X X",
                'X',
                Blocks.dirt);
    
        GameRegistry.addShapelessRecipe(
                new ItemStack(pillItem, 9),
                enderBlock
        );
    
        GameRegistry.addSmelting(
                Items.sugar,
                new ItemStack(pillItem, 1),
                1.0f
        );
    
        pillItem.setPotionEffect(PotionHelper.blazePowderEffect);
    
        proxy.init(event);
    }
    
  6. 到這裡我們的Main檔案就可以移除了。接下來更新資源檔的命名,把resources/assets/myfancymods改名為resources/assets/mymod就可以了。另外,在資源檔的目錄resources下有一個mcmod.info的檔案,我們順便將這個檔案做更新,可以讓我們的模組資訊更完整:
    更改名稱後的resource目錄
    https://ithelp.ithome.com.tw/upload/images/20191007/20115823f0W4xcm5ls.png
    mcmod.info

    [
    {
      "modid": "mymod",
      "name": "My Mod",
      "description": "A mod for itHome",
      "version": "${version}",
      "mcversion": "${mcversion}",
      "url": "",
      "authorList": ["Sam"],
      "credits": "",
      "logoFile": "",
      "screenshots": [],
      "dependencies": []
    }
    ]
    

    檔案內容應該很好理解,這裡的"version"與"mcversion"是Forge內部保留字,會在執行時自動替換,我們不需要更改這部分內容。

https://ithelp.ithome.com.tw/upload/images/20191006/20115823hYKpClTZRj.png

https://ithelp.ithome.com.tw/upload/images/20191006/20115823HEbjbEI6h5.png
我們的模組現在資訊更完整,並且可以"Disable"了。


如果到目前為止的程式碼你不是很了解的話,別擔心,到最後面會有完整的程式碼可以參考的。


上一篇
[Day21] 整理現有程式碼(上)
下一篇
[Day23] 自定義設定
系列文
[Minecraft - 當個創世神] 從玩遊戲到設計遊戲30

尚未有邦友留言

立即登入留言