由于最近太忙了,所以无力一口气把所有教程更新完.
所以打算现在这里放出预览版然后慢慢更新,全部写完后再正式发到论坛.
注意由于是预览版,所以随时都会有更新和修正.
基础篇
第一章:MCP,Forge和Eclipse的配置
http://www.hakugyokurou.net/wordpress/?p=134
第二章:建立一个基于Forge的Mod
http://www.hakugyokurou.net/wordpress/?p=144
第三章:创建新的砖块,物品和冶炼
http://www.hakugyokurou.net/wordpress/?p=163
第四篇:实体
http://www.hakugyokurou.net/wordpress/?p=340
Extra篇
第一篇:Forge的事件系统
http://www.hakugyokurou.net/wordpress/?p=225
第二篇:在Eclipse下编译和调试(从1.7开始就不用考虑这个问题了)
http://www.hakugyokurou.net/wordpress/?p=257
第三篇:Coremod的制作
http://www.hakugyokurou.net/wordpress/?p=333
配套:Java字节码(Bytecode)与ASM简单说明
http://www.hakugyokurou.net/wordpress/?p=409
第四篇:Gui
http://www.hakugyokurou.net/wordpress/?p=333
常见问题
http://blog.hakugyokurou.net/?p=1298
Plus篇(同样未更新并且严重过时...)
什么是Plus篇?Plus篇倾向于讲那些原理和底层中的东西,或许对大部分人来说,是没有什么作用的.
http://www.hakugyokurou.net/wordpress/?p=284
ASMShooterMappingData的下载(供用来做Coremod的人使用,介绍看Extra编第三篇.)
http://sdrv.ms/1cv32le
另外,基础篇可能以后我不会更新了...换句话说旧教程的TileEntity和地形生成不会再被移植到新教程上,对于这几篇教程的空白,你可以参考别人的教程:
Manageryzy编写的综合索引站,包括所有中文教程的索引:https://mcdev-wiki.org
Manageryzy的教程:http://www.261day.com/minecraft-forge教程/
Darkyoooooo的教程:http://darkyoooooo.minestudio.org/minecraft-forge-开发实例/
非官方Forge文档:http://mcforge-cn.readthedocs.org/zh/latest/ (有点慢,可能需要翻墙)
如果你是位教程作者的话,可以叫我在这里加上你的教程的链接.
更新:
12.12.9 更新一点点...
12.12.12 更新了一点物品的部分
12.12.23 更新到Forge6.5.0.471
13.1.1 过年啦过年啦...旧坑未填又来新坑哟,这次是Forge的事件系统.同时,代码高亮插件修复,看起来挺不错.
13.1.2 紫妈大暴走(?),第二篇Extra教程出炉了!顺便对第一篇基础教程稍微调整了一下.
13.1.28 放出了Plus篇.
13.2.2 更新了基础教程(3)的一部分.
13.2.10 稍微修正了一点小细节(真的?)
13.2.26 Plus篇更新了一部分.为基础篇和Extra篇的更新做准备.
13.3.17 Extra第三篇和其配套教程发布.
13.6.24 Plus篇更新了"AABB盒与Vec3"
13.8.4 基础篇第四篇发布
13.9.1 修正了Extra第三篇的问题
15.1.6 更新了基础篇的1~3篇
15.2.5 更新了Extra第一篇
15.2.6 更新了Extra第三篇和ASM教程
15.2.22 加上了其他作者的教程的链接
16.2.22 时隔一年,在1.9发布前夕,教程开始向1.8更新
sz, 現在有空了嗎?
之前問你如何在畫面中保持顯示字串這部分我OK了, 但是現在又延伸出一個問題, 我同樣在顯示字串的判斷區裡加入了顯示圖片, 圖片是能夠顯示出來, 但是在畫面上卻像是拼貼的樣子一直延伸出去, 我使用的代碼如下:
GL11.glPushMatrix();
GL11.glScalef(1.0f, 1.0f, 1.0f);
GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
mc.renderEngine.bindTexture("/gui/test001.png");
drawTexturedModalRect(0, 0, 0, 0, 339, 456);
GL11.glPopMatrix();
我的圖片畫得很大, 所以我使用 1024 x 1024 的尺寸, 但在圖中目前只用到 339x456那塊區域而已.
我查了很多國外討論板的討論文, 他們最後都沒下文了, 也不知道他們是怎麼解決這問題的.
麻煩你幫我看一下代碼裡是缺了哪條指令.
這是因爲Minecraft假設所有紋理(Texture)都是256x256尺寸而造成的,OpenGL在很多地方采用比例參數,就以GL11.glColor4f舉例,如果它的4個參數都是實際數值的話,那麽妳畫出來的應該是壹個幾乎看不見的黑色方框,但實際上不是,這是因爲它采用的是比例,它將全紅,全綠,全藍,完全不透明定爲1.0,然後以此爲參照按比例來設置,0.5就是半紅,半綠,半藍或半透明.
這麽做是因爲很多東西並非只有壹個標准,采用RGB24的話,壹種顔色有256個深度,但若采用RGB555的話,壹種顔色就只有32個深度了.
同理,不同紋理有不同的大小,不同屏幕有不同的尺寸,因此OpenGL在分割紋理時采用比例參數,Minecraft對此進行了壹次封裝,通過壹個系數(具體值爲0.00390625,即1/256)對輸入值進行換算(實際值=輸入值*系數)
Minecraft假設所有紋理都是256x256,所以這個系數就固定爲1/256了,于是在換算時就有了各種各樣的悲劇...
下面這個紋理繪制函數可以適應各種尺寸的紋理,它相比原來的函數多了2個參數:紋理的寬(texWidth)和紋理的高(texHeight).
public void drawTexturedModalRectWisely(int texWidth,int texHeight,int x, int y, int u, int v, int areaWidth, int areaHeight)
{
float f = 1 / (float)texWidth;
float f1 = 1 / (float)texHeight;
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV((double)(x + 0), (double)(y + areaHeight), (double)this.zLevel, (double)((float)(u + 0) * f), (double)((float)(v + areaHeight) * f1));
tessellator.addVertexWithUV((double)(x + areaWidth), (double)(y + areaHeight), (double)this.zLevel, (double)((float)(u + areaWidth) * f), (double)((float)(v + areaHeight) * f1));
tessellator.addVertexWithUV((double)(x + areaWidth), (double)(y + 0), (double)this.zLevel, (double)((float)(u + areaWidth) * f), (double)((float)(v + 0) * f1));
tessellator.addVertexWithUV((double)(x + 0), (double)(y + 0), (double)this.zLevel, (double)((float)(u + 0) * f), (double)((float)(v + 0) * f1));
tessellator.draw();
}
感恩, 一語驚醒夢中人~
原來0.00390625 是這麼來的, 我一直看不懂在Gui.java檔裡的那數值到底是怎麼來的, 這下子我明白了.
說實在的, 繪圖使用的代碼運作內容我沒一個瞭解的, 我只知道貼文字或貼圖片該用哪個代碼而已, 講起來實在汗顏啊XD
你貼的這個自製函式drawTexturedModalRectWisel我有實驗了, 依然行不通...
我把原來的 drawTexturedModalRect(0, 0, 0, 0, 339, 456); 代碼 換成drawTexturedModalRectWisel(1024, 1024, 0, 0, 0, 0, 339, 456); 後, 圖片顯示被放大了, 而且是很大很大, 在Debug模式運行時, 當我把 1024 的值逐漸縮小時可以看到圖片逐漸跟著縮小, 但是縮到 339的時候, 依然會看到拼貼的樣子, 跟原本我提問時的那樣子一樣. 我一直在思考繪圖座標這問題, 有沒有跟學習VisualBasic那時一樣呢?
後來, 我隨便亂搞一通, 使用原來的代碼, 但改了兩個數值變成 drawTexturedModalRect(0, 0, 0, 0, 256, 256); 居然畫出來的圖就正常了, 1比1的尺寸, 而且沒有拼貼的情況了.
但是, 這下子我更搞糊塗了, 代碼我下成這樣子後, 那我原本還去得知圖片內部尺寸的 (339, 456) 這數值要做什麼呢, 不就變成脫褲子放屁多此一舉了 =.=
讓我來假設一下你看我說的對不對, 因為Minecraft內定以256x256的繪圖模式去畫圖, 如果我使用的圖片總尺寸也是用 256x256 的尺寸, 只要我在這尺寸範圍裡畫我想要的圖, 那麼, 我去使用 drawTexturedModalRect 這代碼時才有需要去明確指定圖片裡的小圖正確的寬和高, 這樣子想法對不對呢?
我又測試了另一數據, 我的圖片一樣是1024x1024尺寸使用了 drawTexturedModalRect(0, 0, 0, 0, 256, 256); 和 drawTexturedModalRect(0, 0, 0, 0, 128, 128); 這兩個顯示出來的圖片都正常, 也都沒有拼貼的情況.
另外測試一個方式, 把我的圖片從1024x1024尺寸轉換為 512x512尺寸, 然後用drawTexturedModalRect(0, 0, 0, 0, 256, 256);的代碼, 結果就顯示不正確了, 圖片變成放大很多.
因為我不想用256x256的規格來畫我想要的圖片, 不夠細緻.
我被它這個貼圖代碼搞得頭昏腦脹, 根本不知道要從何依據了.
上次居然玩砸了,What a shame...
首先先要說明兩件事
1.如果繪制一個紋理並且繪制區域大小大于紋理大小的話(比如用一個64x64的紋理繪制一個256x256的矩形),OpenGL有多種處理方式,其中一個(應該是默認的)就是重複繪制,換句話說就是你所說的"像是拼貼的樣子一直延伸出去"
2.[刪去了大段的話,發現最近自己變笨了無法像以前那樣講原理了,很顯然最近是撸多了],Minecraft對紋理截取的方式是通過比例數值確定4個點然後取樣,左上那個點的比例坐標是(u * x系數,v * y系數),右下那個點的坐標是((u + width) * x系數,(v + height) * y系數)
3.遊戲的GUI Scale默認爲Auto,實際上(至少是在我的機器上)除了Small以外,任何設置下遊戲都會對繪制到屏幕上的GUI進行縮放,256x256的紋理相當于一個背包欄,正常顯示的339x456應該會撐滿整個屏幕吧
4.我賭⑨毛你將你的紋理不用的部分塗爲了透明
5.好像不止兩件事了
如果你看懂了頭3件事那你應該就能明白後面我要說的了,首先,你的紋理太大了...在我們能找到規避無情的GUI Scale之前,先以256x256爲參照物來設計紋理.另外,你說drawTexturedModalRect(0, 0, 0, 0, 256, 256)能正常顯示,是因爲按照正常系數,這剛好是繪制一整張紋理,不存在重複繪制的問題...你說你在縮小drawTexturedModalRectWisel(1024, 1024, 0, 0, 0, 0, 339, 456)中的1024時遇到了圖片縮小和拼貼的情況,首先,這是個數字遊戲,系數的算法是1/寬度或1/長度,取樣點的算法之一是((u + width) * x系數,(v + height) * y系數),隨著寬度與長度的縮小,系數會越來越大,從紋理上截取到的區域也越大,但要繪制的矩形卻不變,因此遊戲便縮小了紋理,你那圖片也越來越小了.至于紋理重複,是因爲你所要繪制的矩形的長寬不相同的問題造成的...不用說也是能明白的.
至于那個縮小尺寸反而圖片變大...你確定你用的是縮放而不是截取嗎...那個我想了半天也沒想出來是怎麽回事.按理說,只要你有內容的區域在紋理中占的面積比例不變,那繪制出來的就應該是相同的.
請原諒我資質駑鈍, 某些部分我理解能力還不足.
第1點, 我大約瞭解了, 如果遊戲內定是用拼貼的方式來展現紋理, 那麼我就得去仔細算一下我要使用的小圖片尺寸了.
第2點, 紋理截取的座標方式理解了.
第3點, 如果GUI的Scale 是以Auto的方式來進行, 這也表示我必須在Scale代碼進行設定才會展現出符合我想要的畫面, 對吧.
第4點, 是的, 通常我都會把不用的區域塗成透明色.
經由你的提醒後, 我把重點放在 Scale 這代碼上, 我稍微的測試了一下, 圖片都我自己繪製, 在一張256x256尺寸的圖片裡, 我畫了 140x13 尺寸的小圖, 使用GL11.glScalef(0.25f, 0.25f, 0.25f) 它才顯示出1:1的尺寸. 當我在另外一張 512x512尺寸的圖片裡畫一個 339x456尺寸的小圖, 使用GL11.glScalef(0.5f, 0.5f, 0.5f) 它才顯示出1:1的尺寸, 雖然這解決了我的顯示圖片問題, 但我仍然不瞭解 drawTexturedModalRect 這代碼的運作到底準不準確.
Forge的更新兩三天就來一次, 我也不知道它在繪圖這方面有沒有修正什麼Bug, 我目前只能用得上就用, 用不上就另外找方法來實現自己想要的功能了.
謝謝你的指導, 我想我該讓你知道,我有做出小小的模組發佈在巴哈姆特, 也順便幫你推廣一下.
文章位址: http://forum.gamer.com.tw/C.php?bsn=18673&snA=77253&tnum=1
一起加油吧~
再來跟你請教一下NBT的問題, 我在使用TileEntity的NBT上沒有問題, 它能隨時存讀.
但是, 我用Entity的NBT就有很大的問題了, 我試過在write和read的NBT那裡放 system.out.println 來觀察我設定的變數存讀情形, 結果系統都很久才存讀一次, 而且在存讀方面也有問題, 一開始是存讀正確的值, 在接著下去的值都是 0了.
例如以下我使用的代碼:
@Override
public void readEntityFromNBT(NBTTagCompound nbt) {
super.readEntityFromNBT(nbt);
this.safeMode = nbt.getBoolean("NBT_Attacking");
system.out.println("讀目前值="+this.safe.Mode);
}
@Override
public void writeEntityToNBT(NBTTagCompound nbt) {
super.writeEntityToNBT(nbt);
nbt.setBoolean("NBT_Attacking", this.safeMode);
system.out.println("存目前值="+this.safe.Mode);
}
我的主要疑問是為何Entity的NBT不是隨時在存讀, 而 TileEntity的NBT就時常在存讀?
可否請你在指導一下 "Entity" 的NBT正確用法.
NBT的作用之一是將數據寫入硬盤上的存檔,以及將存檔中的數據讀入遊戲,並非所有NBT都適合存儲實時需要的數據,Entity的readEntityFromNBT和writeEntityToNBT並不能保證實時存讀數據(另外TileEntity真的是實時存讀嗎...我感覺它在沒有增刪時每900Tick才存儲一次...).
另外Entity在讀取NBT時,有些地方采用這種寫法:
if (par1NBTTagCompound.hasKey("xxx"))
this.xxxx = par1NBTTagCompound.getXX("xxx");
換句話說是先判斷有沒有已存儲的數據,然後再進行讀取,不過我也不明白爲什麽要這樣...也許有時存在沒存的時候就先讀的情況?
既然NBT不靈了,那麽我們就得用別的辦法來存讀數據,比如DataWatcher.
DataWatcher除了存讀數據以外,它還有保證客戶端與服務器端之間數據同步的功能.DataWatcher有0至31共計32個可用的數據值(其實我喜歡把它叫"通道"或"頻道"),每個數據值可以存儲一個數據,通過底層實現,DataWatcher會盡量保證一個數據值在客戶端和服務器端都是相同的.
DataWatcher的使用非常酷似C#的訪問器,即通過setXXX來修改XXX變量,getXXX來獲取XXX變量的值.在Minecraft中你能看到類似的寫法.
使用DataWatcher前,先要在實體的DataWatcher中注冊數據值,注冊數據值通常在entityInit中進行,以EntityCreeper爲例,0~15號數據值被基類使用了,16號數據值被用來儲存它爆炸前蓄力時間,17號用來存儲它是否被閃電充能過.因此它的entityInit是:
protected void entityInit()
{
super.entityInit();
this.dataWatcher.addObject(16, Byte.valueOf((byte) - 1));
this.dataWatcher.addObject(17, Byte.valueOf((byte)0));
}
addObject是向DataWatcher中注冊一個數據值(萬不可用它來修改值),數據類型只能爲Byte,Short,Int,Float,String,ItemStack和ChunkCoordinates中的一種.
修改數據值通過updateObject來進行.獲取數據值通過getWatchableObjectXXX來進行,XXX爲類型名.
一個數據值在被注冊時以及被修改後會被自動設爲待更新狀態,DataWatcher會盡快完成同步並撤銷狀態,你也可以通過setObjectWatched來手動將一個數據值設爲待更新狀態.
另外,有人之前和我聊過這個問題,我想可能你也會問,就是DataWatcher怎麽和Entity中的各個字段(或者叫變量吧,雖然很不准確.我不知道Field在台灣翻譯爲什麽.)保持關聯的,事實上,它們並沒有關聯,開發者可以手動將DataWatcher中的數據刷新入字段,也可以完全不管字段,直接從DataWatcher中讀取值來用.通常來說是采用直接從DataWatcher中讀取值來用的方式.
總結來說,NBT用來將數據保存在硬盤當中,DataWatcher用于遊戲中實際使用.
感謝精闢的解說, 看來, 我又得去研讀DataWatcher的詳細資料了.
非常謝謝你提供此資訊讓我了解, 不然, 我老是在NBT那裡打轉XD
請問一下
如果說我想了解類別下的所有屬性的話
我可以怎麼做呢?
例如哪個網站有資料?
亦或如何查詢?
看名字,看注释~
不过名字也有起错或含糊不清的时候(比如很早以前GuiPlayerInfo就是个被MCP组起错的名字,它和Gui毫无关系,现在应该已经修正了),注释也有写错的时候(比如MovingObjectPosition的sideHit的注释,现在似乎还没更正呢).这时就要靠Eclipse的神器:References和Hierarchy了.
References是查找一个东西(可以是Field,可以是Method,也可以是Class)在哪里被引用到了,它在右键菜单里,默认快捷键Ctrl+Shift+G是在整个Workspace中查找引用.
Hierarchy分为Type Hierarchy和Call Hierarchy,前者是查找一个类的所有派生类和基类以及它们之间的继承关系,后者类似于References,只不过它能一口气查到底,并给出调用关系.它们同样在右键菜单里.
我主要通过这些东西来分析一个Class或Field或Method的功能...至于文档和网站...文档已经被自动融合进代码里了,网站...似乎真缺乏这类网站,日本Wiki上有很少的一些解释资料(而且大部分已经过期了)
感謝你的回覆!
也謝謝你的文章造福大眾!
繼上次的DataWatcher研究時, 額外碰到 setEntityState 的問題, 我在許多怪物和動物的Entity代碼裡發現許多地方有用到這指令, 而且影響蠻大的, 我也找了很多關於 setEntityState 的解答, 但老外那邊似乎沒有人在提這東西, 他們只知道要用這指令, 但不知道指令裡的參數為何要這樣子用.
我想請教你, setEntityState 這指令是World類裡的, 我點進去看, 查了它相關的參照, 沒有一個地方會顯示他的參數"代表意義".
我舉例 EntityIronGolem 代碼裡用的參數是 this.worldObj.setEntityState(this, (byte)4), 而 EntityWolf 代碼裡用的參數是 this.worldObj.setEntityState(this, (byte)7) 和 this.worldObj.setEntityState(this, (byte)6)
我很想知道為什麼有的數值用4, 有的用6 或 7 , 我上網想查那些數值代表意義, 但都沒有任何資料可查, 可否幫我解惑一下?
setEntityState的作用是從服務器端發一個信號給客戶端,該信號代表某Entity的某個狀態發生變化,換句話說這是個輕量級的數據傳輸方案~只不過是單向的(其實DataWatcher應該也是單向的吧...)
setEntityState的第二個參數代表信號類型.任何一個繼承了Entity類的類都可以通過重寫handleHealthUpdate方法來處理信號(別忘了將不能處理的信號通過super.handleHealthUpdate傳給基類去處理).只有2(代表遭受攻擊)和3(代表被殺死)這兩個信號是"公用"的,是所有EntityLivingBase類的派生類都能識別的,其它的則是由接受的類來實現.
舉例,當服務器端判斷某Entity受到攻擊時,會調用world.setEntityState(this,(byte)2) (假設world是其所處世界,this指該Entity).服務器會將信息廣播給有機會與此Entity互動的玩家,玩家的客戶端收到信息後會調用entity.handleHealthUpdate((byte)2) (假設entity爲此Entity).效果是讓它做出被傷害的特效.
對于各個信號值的含義,只要知道2和3分別代表遭受攻擊和被殺死就可以了,因爲除此之外的信號值是不通用的,盡管Minecraft爲每種信號值只規定了一種含義...不過我還是整理了一下部分信號值的含義
2 遭受攻擊/受到傷害
3 死亡
4 IronGolem發起攻擊
6 馴服失敗
7 被馴服
8 狼甩掉身上的水
9 物品使用完畢
10 一個"小動作"(吃草)
11 IronGolem拿到玫瑰
13 村民憤怒
14 村民高興
15 Witch身旁産生粒子特效
16 僵屍被轉化爲村民
18 被馴養的動物開始繁殖
感謝說明, 這樣子至少我有個底了.
不過, 我很好奇, 你是如何得知這些信號是做為哪些作用的?
例如信號4和信號11, 它固定都是IronGolem在用的嗎?
因為, 我目前卡在類似IronGolem的手擺動描繪問題, 我必須借由setEntityState才能使我自製的其它動作在客戶端上看得到, 因為我是仿照IronGolem的手擺動設計方式來寫我自己想要的動作, 如果不依靠setEntityState這指令, 在客戶端就看不到動作的改變.
請問, 你有什麼好建議可以提供參考一下?
看它們的handleHealthUpdate來判斷他們是怎麽解析信號值,或者看它們怎麽調用setEntityState.只有IronGolem的handleHealthUpdate能解析4和11,所以4和11兩個信號值可以說是在原版Minecraft中IronGolem專用的.然而對開發者而言不必死守"一個信號值只對應一個實體的一種動作"這個規定...
除了setEntityState以外,還有其他一些從服務器向客戶端發送信息的方式,比如自定義封包等...教程可以看這個http://www.minecraftforge.net/wiki/Packet_Handling,不過就是太麻煩了.
謝謝說明, 我再去研讀一下.
我想知道蛋糕這些半格高的東西會講解嗎?還有像蛋糕的功能能否也講解下呢?