iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
1

有了昨天的基礎架構後,我們接下來就可以開始整理我們的程式碼了!

  1. 昨天提到的Proxy功能,我們接下來要實際使用它。首先我們先把IProxy介面填充基本的@EventHandler三階段方法:
    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;
    
    public interface IProxy {
        /**
         * 準備Minecraft的所有物體。舉凡讀取設定檔、建立方塊、物品,註冊物體到Minecraft等。
         *
         * @param event 事件
         */
        void preInit(FMLPreInitializationEvent event);
    
        /**
         * 模組設置。例如註冊事件處理、註冊資源、建立配方(合成、燒煉與釀造)。
         *
         * @param event 事件
         */
        void init(FMLInitializationEvent event);
    
        /**
         * 與其他模組互動。如果有任何其他需要處理的的功能,或是要建立與其他模組的相依關係。
         *
         * @param event 事件
         */
        void postInit(FMLPostInitializationEvent event);
    }
    
  2. 這時你的ClientProxyServerProxy類別會出現紅色底線。在[Day3]也有提到,可以透過Alt+Enter來讓IDE自動幫我們除錯並填寫缺少的程式碼;這邊的錯誤是因為類別需要實作介面的方法。
  3. 接下來就是整理程式碼的動作了。回到MyMod檔案,首先需要將@EventHandler標註主要會用到的三階段初始化方法加在proxy變數後面:
    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        proxy.preInit(event);
    }
    
    @EventHandler
    public void init(FMLInitializationEvent event) {
        proxy.init(event);
    }
    
    @EventHandler
    public void postInit(FMLPostInitializationEvent event) {
        proxy.postInit(event);
    }
    
    這裡我們不需要理會proxy執行的是ClientProxy還是ServerProxy裡的方法;Forge會依據當前執行時的需求自己去尋找!
  4. 我們首先搬移[Day3]的程式碼。
    打破方塊事件是需要放在init()方法內,這個昨天有提到;另外事件的處理是屬於Client端的程式碼,所以我們需要將這部分的邏輯搬到ClientProxy檔案內。
    • 先建立新的com.ithome.mymod.events套件目錄,並且將BlockBreakEvent檔案移動到這個目錄下:
      https://ithelp.ithome.com.tw/upload/images/20191006/20115823cE0yShs50t.png
    • [Day5]我們有將BlockBreakEvent從主程式中移除,但我們之後會有另外一種自定義設定的方式來處理同性質的事件要如何變成可以在遊戲內選擇,這邊就先全部都加回來。
    • BlockBreakEvent應該會有兩個自定義事件:
      public class BlockBreakEvent {
          @SubscribeEvent
          public void sendMessage(BreakEvent event) {
              ...(略)
          }
      
          @SubscribeEvent
          public void explodeOre(BreakEvent event) {
              ...(略)
          }
      }
      
    • 主程式內的register搬移。我們直接到Client端ClientProxy檔案的init()動作內加上:
      @Override
      public void init(FMLInitializationEvent event) {
          MinecraftForge.EVENT_BUS.register(new BlockBreakEvent());
      }
      
      註冊我們Client端會使用到的事件。
  5. 接下來的[Day5]的自定義爆破事件參考前一步驟的原則:移動TNTExplosions檔案到events目錄下,並且在ClientProxy中註冊:
    TNTExplosions.java
    package com.ithome.mymod.events;
    
    import net.minecraft.entity.Entity;
    import net.minecraft.entity.item.EntityTNTPrimed;
    import net.minecraft.util.BlockPos;
    import net.minecraft.util.Vec3;
    import net.minecraftforge.event.entity.EntityJoinWorldEvent;
    import net.minecraftforge.event.world.ExplosionEvent;
    import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
    
    import java.util.List;
    
    public class TNTExplosions {
    
        // 威力使用20格
        private float power = 20;
    
        @SubscribeEvent
        public void explodeHead(EntityJoinWorldEvent event) {
            // 只處理加入到世界的實體是TNT : 這裡即是指TNT方塊被啟動後閃爍的TNT"實體"
            if (event.entity instanceof EntityTNTPrimed) {
                Entity entity = event.entity;
                // 建立我們想要的爆炸威力
                event.entity.worldObj.createExplosion(
                        entity,
                        entity.posX,
                        entity.posY,
                        entity.posZ,
                        power,
                        true
                );
            }
        }
    
        @SubscribeEvent
        public void explodeTunnel(ExplosionEvent.Detonate event) {
    
            if (event.world.isRemote) {
                return;
            }
    
            Vec3 eventPos = event.explosion.getPosition();
            event.getAffectedBlocks().clear();
    
            int height = 6;
            int depth = 20;
            toCave(event.getAffectedBlocks(), eventPos, height, depth);
        }
    
        private void toCave(List<BlockPos> affectBlocks, Vec3 originPos, int height, int depth) {
            for (int x = -height; x <= height; x++) {
                for (int y = -height; (y <= x && x <= 0) || (Math.abs(y) >= x && x > 0); y++) {
                    int trueY = height - Math.abs(y);
    
                    for (int z = 0; z <= depth; z++) {
                        BlockPos b = new BlockPos(originPos.xCoord + x, originPos.yCoord + trueY, originPos.zCoord + z);
                        affectBlocks.add(b);
                    }
                }
            }
        }
    }
    
    ClientProxy.java
    package com.ithome.mymod.proxy;
    
    import com.ithome.mymod.events.BlockBreakEvent;
    import com.ithome.mymod.events.TNTExplosions;
    import net.minecraftforge.common.MinecraftForge;
    import net.minecraftforge.fml.common.event.FMLInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
    import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
    
    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());
        }
    
        @Override
        public void postInit(FMLPostInitializationEvent event) {
    
        }
    }
    
  6. [Day6][Day9]我們做了三個功能:
    • [Day6]的鑽石豬:自定義實體掉落事件的方法。這部分的重構我想邦友們都很聰明就不用再多闡述。 (移動PigDroppingItems檔案、在ClientProxy內註冊)
    • [Day7]提到的殭屍騎士:定義實體進入Minecraft時的裝備。這一樣很簡單:移動在[Day8]最一開始的ZombieKnights檔案、註冊事件到ClientProxy內。
  7. 接下來我們在[Day9]的俄羅斯豬豬比較麻煩;因為它牽涉到需要先註冊實體,再註冊事件。這部分明天我們再提。
  8. [Day10]的超級跳躍:定義實體跳躍與降落的事件。這個部分一樣是移動SuperJump與在ClientProxy內註冊事件。

好了,最後你的結構應該會比較乾淨一些了:

檔案結構
https://ithelp.ithome.com.tw/upload/images/20191006/20115823rZgs2V1zCQ.png

ClientProxy的init()方法

@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());
    FMLCommonHandler.instance().bus().register(new SuperJump());
}

上一篇
[Day20] 重新開始
下一篇
[Day22] 整理現有程式碼(下)
系列文
[Minecraft - 當個創世神] 從玩遊戲到設計遊戲30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言