"有些話說出來就舒服多了"

(標題不用太在意,又一發黑槍而已...)
因為昨天把腳崴了,所以今天在家休息一天,感覺這段時間造的孽太多,前幾天把一個在圖書館看霓虹動♂作♀片的仁兄曝光了(請叫我正義的朋友face4),在blog上又搞了十幾天的放置play.因此決定趁現在瘸着腿什麼都不想乾的時候寫點啥.
首先是教程還更不更的問題,這個我想說的是,基礎篇估計不會更新了...真要更新的話可能是實體部分會再詳細一點. 至於TileEntity,冶煉和地圖生成這三部分可能不會再有了,等着別人來寫吧 23333
Extra篇可能會有個3D部分,此外肯定還會有個關於這三年間我遇到的各種MC問題的Q&A.

順便再感慨一下,終於有人做到這一步了:

它的作者Arun Gupta自稱是暢銷書作者,你可以在他的blogO'Reilly的官網找到目錄信息,能把這點內容寫出200頁確實有兩下子,反正我是把新舊兩個教程連同Extra篇加在一起也湊不出200頁.我覺得那本書叫Minecraft modding for dummies或許更合適,事實上作者本人也說他的目標讀者是初學編程的未成年人,因此就不要指望書中會有網絡編程或Coremod之類的了...

另外,有些東西不一定非要問別人才能知道...我不是很喜歡知乎的原因除了山寨和半實名制外,還因為它扼殺了一些人探究問題的能力,當擅長嘩眾取寵的人能壟斷一個問題的話語權時,一切理性都是蒼白無力的.比如關於配置時JAVA_HOME不正確之類的...這些問題大部分我都沒法在自己的機器上重現,最後結果還不全是去Google搜索...想知道MC的xx怎麼做/怎麼用,最簡單的辦法是:看官方是怎麼弄的.Eclipse中的References(查找引用)功能非常強大,點上你想查找引用的類/字段/方法,然後右鍵-References-Project(其實更多情況下我是直接Ctrl-Shift-G...)就能找它的所有調用,然後看看官方是怎麼使用它的.想追蹤調用棧的話可以Ctrl+Alt+H,查看類的繼承家譜還可以F4,能自己很快完成的事情為何還要去找別人問呢. (順便一提,如果不知道eclipse怎麼解除智能感知的封印的話...去網上搜一下吧.雖然它的智能感知即使解除封印了也確實不如VS和IDEA,甚至在高版本中有越來越蠢的跡象,Luna版的智能感知一度想讓我砸鍵盤...就看4個月後的Mars版怎麼樣了)

舉個栗子,之前有人問我成就系統怎麼弄,我以前從來沒研究過成就系統,那現在就從零開始,開始我甚至找不到成就類在哪,不過在客戶端中有個成就的Gui類(net.minecraft.client.gui.achievement.GuiAchievement),從它的一個字段中我找到了成就類(net.minecraft.stats.Achievement),然而在我現在的版本(1.7.2)中它的構造函數的參數還沒有被寫上可讀的解釋,因此我只能看看官方是怎麼用的,通過超找它的構造函數的引用,我看到AchievementList類包含了遊戲默認成就的全部實例,發現OpenInventory(遊戲中的打開物品欄成就)的初始化方式是

public static Achievement openInventory = (new Achievement("achievement.openInventory", "openInventory", 0, 0, Items.book, (Achievement)null)).initIndependentStat().registerStat();

第一個參數看上去十分像語言文件中的鍵,第二個參數或許是它的ID(其實這兩個結論在之後都被證實為錯了...但推翻的過程要到待會再說),第三四個參數暫時不得而知,第五個參數應該是它的圖標,第六個參數是個null,因此無從得知它的用途,之後還有兩個採用Builder模式的方法,initIndependentStat的內容待會再調查,registerStat看上去像是註冊當前成就.
然後我回過頭去看Achievement的構造函數,未反混淆的參數名看上去亂糟糟的,但因為已經看過它們的實際使用方式,因此心裡更有些普,我發現對第一個和第二個參數作用的結論反了,第一個才是成就ID,而第二個是語言文件的鍵的後半段,真是個充滿迷惑性的不可理喻的設計...第三個和第四個在這裡發現是他們在成就頁中的位置.第五個參數在這裡被包裝為了ItemStack,然後存入了theItemStack字段中,它的介紹是"Holds the ItemStack that will be used to draw the achievement into the GUI.",看起來之前的猜想是正確的.第六個參數這裡被明確指出是前提成就.由於OpenInventory是遊戲中的第一個成就,因此它的前提成就肯定是null.此外我還注意到成就的描述文本(achievementDescription字段)是通過直接從語言文件中獲取鍵為"achievement.[參數2].desc"的文本來獲得.
然後再調查那兩個方法,initIndependentStat方法的描述很含糊,內容也只是將initIndependentStat設為true,但是通過查找initIndependentStat的引用,我發現在StatBase里的toString中,將isIndependent稱為"awardLocallyOnly",我記得MC1.7.2增加了成就在不同服務器中區分計算的設定,因此這個大概是讓成就只儲存在客戶端中.而registerStat就是註冊成就,沒有什麼好說的...
然而,我們還知道Forge為MC增加了成就分類系統,然而我並不知道它在哪(就裝作我不知道吧...實際情況下翻翻那幾個package也能翻到),但我想肯定它和成就界面有關,至少在成就界面中應該能找到它的引用.果然在net.minecraft.client.gui.achievement.GuiAchievements中看到了個AchievementPage.AchievementPage位於net.minecraftforge.common包下,作為一個庫中的類,它的使用方式很簡潔明了:創建一個實例,然後通過靜態方法registerAchievementPage註冊成就分頁.
最後還有個問題,就是如何觸發成就,這個聽起來有些棘手,雖然只要有官方的用法參照就不是問題,但關鍵在於如何尋找官方的用法.由於AchievementList包含了所有成就的實例,因此我猜想如果想觸發一個成就,則勢必會用到那個成就的引用(雖然也有可能是通過成就Id來觸發的...),果然通過查找隨意一個成就(但不建議是openInventory,跟它相關的東西太多了...)的引用可以看到,觸發成就的方式是調用玩家實體的triggerAchievement方法.

於是這便是從零研究如何做成就的辦法,全程用時應該不會超過一刻鐘,而如果你要問別人的話,需要等天知道有多久的時間才能得到回復,而且對方的解釋你還不一定能立刻明白,為什麼就不花一點點時間來自己研究一下呢?