iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 23
1

當我們今天有了很多功能後,有時候會希望某些功能可以進入遊戲後再做調整,例如:

  • 自定義實體的生命值
  • 爆炸的範圍
  • 可以跳多高

這些功能如果可以透過一個設定檔(Configuration)來控制,只要調整檔案內對應的欄位值,儲存後就可以即時在遊戲內看到調整後的結果,而不用像我們現在的做法:

停止遊戲 -> 改程式碼 -> 儲存後,重新進入遊戲 -> 看結果

看起來似乎簡單許多,不是嗎?

建立設定檔類別

  1. com.ithome.mymod.configs套件目錄下(若沒有請自行建立),新增一個CustomConfig類別:
    https://ithelp.ithome.com.tw/upload/images/20191008/20115823acux9LcJ72.png
  2. 在Forge內的設定檔功能都會從net.minecraftforge.common.config.Configuration這個類別開始。我們在CustoConfig這個類別內建立全域變數configuration,並且定義三個基本方法:init(), loadConfiguration()getConfiguration
    package com.ithome.mymod.configs;
    
    import net.minecraftforge.common.config.Configuration;
    import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
    
    public class CustomConfig {
    
        public static Configuration configuration;
    
        // 初始化設定檔
        public static void init(FMLPreInitializationEvent event) {
    
        }
    
        // 讀取設定檔
        private static void loadConfiguration() {
    
        }
    
        // 取得Configuration物件
        public static Configuration getConfiguration() {
    
        }
    }
    
  3. 這裡會出現紅字先不管它。init()方法用的參數是FMLPreInitializationEvent事件,表示這個方法之後會在FMLPreInitializationEvent事件中呼叫。然後我們填入初始化configuration的內容,把我們的設定檔路徑設定為config/mymod.cfg
    // 初始化設定檔
    public static void init(FMLPreInitializationEvent event) {
    
        String configDir = event.getModConfigurationDirectory().toString();
        if(configuration == null) {
            File path = new File(configDir + "/" + Constants.MOD_ID + ".cfg");
            configuration = new Configuration(path);
            loadConfiguration();
        }
    }
    
  4. Configuration有兩個重要的動作:
    • config.load() : 讀取檔案的內容到程式內。
    • config.save() : 儲存目前的設定到檔案內。
      因此,下個動作就是將這兩個方法放到loadConfiguration內,作為一個完整的檔案讀取寫入:
    // 讀取設定檔
    private static void loadConfiguration() {
        configuration.load();
    
        // 當設定檔有變更,才做儲存
        if(configuration.hasChanged()) {
            configuration.save();
        }
    }
    
  5. 最後,在getConfiguration回傳我們的configuration物件:
    // 取得Configuration物件
    public static Configuration getConfiguration() {
        return configuration;
    }
    
  6. 我們的類別到這邊就基本建立完成了,簡單吧?

註冊到Forge

開啟MyMod主程式,在preInit()內初始化我們在前一步驟建立的設定檔類別:

@EventHandler
public void preInit(FMLPreInitializationEvent event) {
    ...(略)
    CustomConfig.init(event);
    ...(略)
} 

連結GUI畫面

到目前為止我們只把設定檔的功能打開,但並不能從畫面中更改設定。要使用這個功能,我們需要再新增兩個檔案到com.ithome.mymod.configs套件目錄下:

CustomGuiFactory.java

package com.ithome.mymod.configs;

import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraftforge.fml.client.IModGuiFactory;

import java.util.Set;

public class CustomGuiFactory implements IModGuiFactory {
    @Override
    public void initialize(Minecraft minecraftInstance) {
    }

    @Override
    public Class<? extends GuiScreen> mainConfigGuiClass() {
        return CustomGuiConfig.class;
    }

    @Override
    public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() {
        return null;
    }

    @Override
    public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
        return null;
    }
}

這個檔案是用來定義我們模組自己使用的GUI設定畫面;由於需要把現有的GUI畫面整個覆蓋,這裡我們直接實作IModGuiFactory。唯一我們需要更改的方法,是mainConfigGuiClass()這一個,並且將它指定到我們下一步驟的檔案類別上。

CustomGuiConfig.java

package com.ithome.mymod.configs;

import net.minecraft.client.gui.GuiScreen;
import net.minecraftforge.common.config.ConfigElement;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.client.config.GuiConfig;

public class CustomGuiConfig extends GuiConfig {
    public CustomGuiConfig(GuiScreen parentScreen) {
        super(parentScreen,
                new ConfigElement(CustomConfig.getConfiguration().getCategory(Configuration.CATEGORY_GENERAL)).getChildElements(),
                Constants.MOD_ID,
                false,
                false,
                "This is My Mod Configuration");
        titleLine2 = CustomConfig.getConfiguration().getConfigFile().getAbsolutePath();
    }
}

這個類別比較特殊,需要繼承GuiConfig這個基礎類別,並且實作建構子(Constructor)方法:

  • 第一個參數我們要用原生參數的pareantScreen作為我們GUI的母畫面。
  • 第二個參數是使用我們CustomConfig包含的所有ConfigElement物件。
  • 第三個參數是我們的模組ID
  • 第四與第五個參數忽略,直接給false
  • 第六個參數是GUI的標題

這裡我們另外指定副標題titleLine2是我們的檔案路徑,這樣比較清楚。

測試設定

  1. 回到主程式MyMod內,在標註@Mod加上guiFactory的連結,並且在Constants內加入我們CustomGuiFactory的完整類別路徑:

    MyMod.java

    @Mod(modid = Constants.MOD_ID, name = Constants.MOD_NAME, version = Constants.VERSION, guiFactory = Constants.GUI_FACTORY_CLASS)
    public class MyMod {
    ...(略)
    

    Constants.java

    // gui config
    public static final String GUI_FACTORY_CLASS = "com.ithome.mymod.configs.CustomGuiFactory";
    
  2. 好了,讓我們來建立一個參數測試看看。回到CustomConfig類別,在最上面建立兩個測試參數:

  • updateCheck(boolean) : 用來檢查是否有更新
  • greeting(String) : 呃,就是問候
  1. 然後在loadConfiguration()內透過get()取得參數,並且設定初始值。最後的程式碼長這樣:
    package com.ithome.mymod.configs;
    
    import net.minecraftforge.common.config.Configuration;
    import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
    import java.io.File;
    public class CustomConfig {
        public static Configuration configuration;
        private static boolean updateCheck = true;
        private static String greeting = null;
        // 初始化設定檔
        public static void init(FMLPreInitializationEvent event) {
            String configDir = event.getModConfigurationDirectory().toString();
            if(configuration == null) {
                File path = new File(configDir + "/" + Constants.MOD_ID + ".cfg");
                configuration = new Configuration(path);
                loadConfiguration();
            }
        }
        // 讀取設定檔
        private static void loadConfiguration() {
            configuration.load();
            configuration.getBoolean("Check for Update", Configuration.CATEGORY_GENERAL, true, "Allow to check for updates");
            configuration.getString("Greeting", Configuration.CATEGORY_GENERAL, "Hello sam", "Greeting to someone");
            // 當設定檔有變更,才做儲存
            if(configuration.hasChanged()) {
                configuration.save();
            }
        }
        // 取得Configuration物件
        public static Configuration getConfiguration() {
            return configuration;
        }
    }
    
  2. 存檔進入遊戲,從主畫面Mods進入,選擇我們的模組My Mod
    https://ithelp.ithome.com.tw/upload/images/20191008/20115823EszfM72hsN.png
  3. 現在左下角的Config選項已經可以使用了,點選後你應該會看到:
    https://ithelp.ithome.com.tw/upload/images/20191008/20115823XocGDVunkH.png
    標題是副標題與我們程式碼內設定的一樣,並且有兩個測試的參數可以用(滑鼠移動到上面,會跳出我們定義的描述內容)
  4. 到我們副標題的路徑,會看到Forge幫我們建立一個新的檔案:
# Configuration file

general {
    # Allow to check for updates [default: true]
    B:"Check for Update"=true

    # Greeting to someone [default: Hello sam]
    S:Greeting=Hello sam
}

內容我想很清楚不用多闡述,這樣我們就完成很陽春的設定檔了!

明天我們會將變更卻無法儲存的功能加進來。


上一篇
[Day22] 整理現有程式碼(下)
下一篇
[Day24] 事件註冊也可以變成設定檔
系列文
[Minecraft - 當個創世神] 從玩遊戲到設計遊戲30

尚未有邦友留言

立即登入留言