SQwatermark 发表于 2020-7-15 21:53:01

【森罗万象】一个Minecraft资源包制作指南

本帖最后由 SQwatermark 于 2021-1-19 14:29 编辑



前言README




森罗万象是什么

森罗万象是一个Minecraft java 版资源包制作指南。力图囊括Minecraft原版资源包、OptiFine 资源包的技术细节,并适当扩充相关知识。

网页版:https://sqwatermark.gitee.io/resguide/
由于人工更新的缘故,两个版本的进度未必完全一致,网页版会更快一些。

看这篇指南需要什么前置知识

关于资源包的唯一要求就是用过资源包,知道怎么安装并加载资源包。

但是在资源包之外,我希望读者拥有几项基本的技能:


[*]使用搜索引擎的技能。
[*]一定程度的语言理解能力,我知道我的表达能力并不是很好。
[*]自己动手的能力。
[*]计算机基础知识。

重要的不是你已经有了多少知识,而是学习的能力。

这篇指南适用的游戏版本

Minecraft java版 1.15.2,不适合基岩版,如果你用的是java版但版本号不同,可能你需要自己解决版本间的差异,这也算是一件基本技能了。

同时安装了OptiFine1.15.2-HD-U-G1-pre30,并没有安装forge。

注意:目前1.14和1.15的OptiFine与Forge兼容性不佳,有些功能会直接丢失。
目前已知的问题:
- cit功能无法使用(fabric似乎没问题)

对于纯新手,我建议在学习时和我用一样的版本,版本间的差异可能会造成干扰。

鸣谢

案例提供:
冰箱(提供CTM章节的案例)
掀夏希(提供原版动态材质章节的案例)
JerryLee (提供动态GUI的案例)

交流群
点击链接加入群聊【OptiFine资源包交流群】:https://jq.qq.com/?_wv=1027&k=G6ay1biD

文档和示例资源包下载
资源包的部分文件因考虑到版权因素而未包含在内
https://github.com/SQwatermark/ResGuideSource





准备Propaedeutics




在动手制作资源包之前,请先确保拥有以下工具:


[*]Minecraft Java Edition
[*]一个纯文本编辑器
[*]一个图片编辑软件
[*]一个压缩软件

Minecraft Java Edition
如果你使用的是基岩版,十分抱歉,这是一个给Java版的教程。

纯文本编辑器
纯文本
纯文本文档,就是没有任何文本修饰的,没有任何粗体,下划线,斜体,图形,符号或特殊字符及特殊打印格式的文本,只保存文本,不保存其格式设置。将所有的分节符、分页符、新行字符转换为段落标记。

打个比方,类似于txt这样的文件是纯文本。而类似于word文档、pdf之类的文件都不是纯文本。

我们在资源包中会遇到许多格式的文本文件,例如xxx.mcmeta、xxx.json、xxx.properties,它们都是纯文本文件,需要用纯文本编辑器进行编辑。

推荐的编辑器
在windows操作系统上都会安装的纯文本编辑器是notepad,也就是记事本。如果未经过特殊设置,一般情况下双击txt文件打开的编辑器就是记事本。

不要让编辑器成为绊脚石,为了在制作资源包时用一款功能强大的编辑器而耗费大量脑汁在学习编辑器的使用方法上是不值得的。


[*]如果你是计算机小白,面对复杂一些的软件的界面已经十分头大,可以使用记事本。
[*]如果你对计算机还算上手,记事本就已经显得非常不方便了,可以使用notepad++或者VS Code等软件。
[*]如果你计算机非常上手,It's up to you.

图片编辑软件
有两个要求:


[*]能够编辑和导出png格式的图片。
[*]支持alpha通道(透明度)。

能做到这两点,用什么都无所谓,我在示例中会使用Aseprite,这是一个专门用于画像素画的软件。可以在steam购买。

压缩软件
推荐Bandizip,用其他的也行。

但有一点要注意:解压的时候请选择“解压到与压缩包同名的文件夹”这一类的选项。我看到过不止一个人解压出一吨乱七八糟的东西,和原先的文件混在一起凌乱不堪了。

确保压缩包和解压后的文件是这样的关系:
https://i.loli.net/2020/07/27/fPHs3UqpMNBkuIC.png

而不是这样的关系:
https://i.loli.net/2020/07/27/vwhAXCGBuMcoLpT.png






基础Basics



在本节中,我们将着手制作一个最基础的资源包。

我们的目标是:


[*]让Minecraft识别并加载它
[*]找到原版资源文件,方便我们模仿和修改
[*]着手修改一个简单的材质
[*]更换资源包的图标





让资源包可以被游戏加载pack.mcmeta



首先,随便找个地方新建一个文件夹,并随意取个名字,这里我们取名为“从零开始的资源包”。

https://i.loli.net/2020/07/28/nsGuFo2xrg4ZLiJ.png

这个就是我们要放在resourcepacks文件夹下的资源包了,现在这个资源包还无法被读取。

提示:可能有读者会认为这是因为资源包没压缩——发布出去的资源包基本上都是zip压缩文件格式的,但事实上Minecraft资源包可以是文件夹,也可以打包成zip文件。

Minecraft资源包需要有一个名为pack.mcmeta的文件,只有找到了pack.mcmeta,正确读取了里面的信息,Minecraft才会认为这个文件夹是一个资源包。

让我们在资源包文件夹里新建一个文本文件,并改名叫pack.mcmeta

https://i.loli.net/2020/07/28/8N9sQS1nv3D4LTU.png

将下面这几行代码复制进去并保存:
{
   "pack":{
      "pack_format":5,
      "description":"这是资源包的简介"
   }
}
了解json格式的朋友可能已经发现这实际上就是一个json文件了,不知道也没关系,现在就这样写。

注意:你必须使用英文半角去输入代码,中文全角格式无法被Minecraft识别!

现在解释一下这几行代码的意思:


[*]pack_format 后面的数字用于表示资源包的版本,这里的版本不是你决定的版本,而是根据Minecraft版本决定的。在加载资源包时,如果资源包版本和游戏版本不匹配,就会显示出一个错误,需要进行确认后才能加载资源包。在13w24a至1.8.9中需要的数值为1,在15w31a至1.10.2为2,在16w32a至17w47b中为3(大版本1.11-1.12.2),在17w48a至19w46b为4(大版本1.13-1.14.4),在1.15-pre1至1.16.2-pre3则为5,在1.16.2-rc1至1.16.4则为6,在20w45a至20w51a则为7(大版本1.17)。我这里以1.15.2作为例子,所以pack_format为5。
[*]description 为资源包的简介,后面的文字可以随意修改,但是不得修改文字两端的双引号,也不能在文字中添加半角双引号。

现在我们打开游戏,选项——资源包——打开资源包文件夹,将我们的资源包放进文件夹中。

我们可以看到,资源包被游戏识别出来了。

https://i.loli.net/2020/07/28/ubldBs8VfHyIYLE.png

如果你的资源包没有被读取出来:

[*]请反复检查pack.mcmeta中是不是用了全角标点符号,漏了或者多了引号、冒号和花括号或方括号。
[*]可能你的电脑没有显示扩展名,文件格式仍然是txt,游戏是无法读取的。

提示:显示文件扩展名的方法:

[*]点击“查看”
[*]勾选“文件扩展名”,此时文件名应该变成了pack.mcmeta.txt
[*]将文件名再次改为pack.mcmeta

https://i.loli.net/2020/07/28/A3boRnOuLCkNEgf.png

我建议把“显示文件的扩展名”一直勾选着,对制作资源包和其他计算机操作都有好处。





原版资源文件的位置Vanilla Resources




现在我们有了一个一穷二白的资源包,方便起见,我们要找到原版的资源文件,放到资源包里作为我们的资源包的默认资源。如果想要修改某个材质,直接在材质包里修改就行了,非常方便。

虽然这会导致资源包从一开始就很占内存,无论如何,知晓原版资源文件在什么地方是非常必要的,因为资源包无非是对原版资源文件的覆盖,不知道原版是什么样,也就不知道如何去覆盖。

打开.minecraft文件夹,再打开versions子文件夹,里面可以看到你安装的所有游戏版本:

https://i.loli.net/2020/07/28/oKWbEFjX7p6Rwka.png

提示:如果你不知道.minecraft文件夹在哪,最简单的方式就是打开资源包所在的文件夹,它的上一级文件夹叫.minecraft,我们要找的就是这个文件夹。

在这个示例中我要为1.15.2版本制作资源包,打开图中名为1.15.2的文件夹,里面有两个文件:1.15.2.jar和1.15.2.json。用任何解压软件解压1.15.2.jar。

打开刚刚解压出的文件夹,里面有一个assets文件夹:

https://i.loli.net/2020/07/28/KHXgs3eda5y9nuL.png

把assets文件夹剪切到刚才创建的资源包文件夹中,现在我们的资源包中已经有了原版的资源。可以把刚才解压出的多余的文件删除了。

https://i.loli.net/2020/07/28/xOG5gwLrACVDSME.png

重新加载资源包,游戏画面不会有任何变化,我们虽然有一个材质包,却是直接复制的原版,在下一节中我们将着手修改一张材质,让这个材质包有那么点实际意义。

提示:可以使用快捷键F3+T重载资源包。
对于部分笔记本电脑的键盘,F3是音量增大键,需要同时按下Fn+F3+T才能正常使用。

解决主菜单全景图丢失的问题:
如果你和我一样用的是1.15.2版本,你会发现主菜单全景图不见了。

解决问题的方式是打开资源包的assets/minecraft/textures/gui/title/background文件夹,把里面的图片全部删除,再重新加载资源包。

https://i.loli.net/2020/07/28/5wuyUizDdaTc8H6.png

1.15.2将主菜单全景图存放在jar文件外部,为了防止外置文件丢失而产生异常,jar文件里放上了备用的灰幕。

我们复制了jar文件里的资源,相当于用灰幕覆盖了外置的全景图文件。




简单地修改一张材质Common Textures



让我们打开assets/minecraft/textures/block,然后找到我们要修改的材质,这里我们选择了acacia_log.png。

提示:如果你实际看到的路径是assets/minecraft/textures/blocks,别担心,Minecraft的不同版本的资源路径并不相同,用你实际看到的路径即可。

打开图片编辑器,对其修改一番,并按照原名保存,覆盖掉原先的文件。

我这里仅仅简单调了下色:

https://sqwatermark.gitee.io/resguide/assets/img/rec4HboaVg8C6Q2.04013ae1.png

重载资源包,放出金合欢木,看看修改后的材质吧!

https://i.loli.net/2020/07/28/Ny8IpD74nbHdc2O.png





资源包的图标icon.png



不想用默认的资源包图标,想要换一个好看的图标?


[*]首先我们需要一个正方形的png格式图片,必需是正常保存的png格式,别的格式把后缀改成png那不叫png格式。图片的边长应为2的次方,例如16,32, 64等。
[*]然后改名为pack.png。
[*]放入资源包文件夹下。

https://i.loli.net/2020/07/28/xd3e1Jb7mKk8fco.png

再打开游戏的资源包界面,我们看到资源包的图标已经改成了刚刚设置的图标,一只可爱的琴吹夢!

https://i.loli.net/2020/07/28/97YLwQtFPRHzrCg.png

到现在为止,如果你有一定的美术基础,已经可以做出一款不错的材质包了,前面的虽然是最最基础的内容,却也是最有用的内容。

但既然资源包可以实现许许多多的内容,何必束手束脚的呢?看看conquest、cocricot、ITP等诸多资源包,它们所做到的显然不是改改材质就能实现的。

如果你有很多想法苦于不知道怎么实现,继续看下去吧,学习愉快!






资源包的文件夹结构Structure




下面这张图片展示了资源包中的绝大部分文件夹的结构,主要参考1.15.2版本,综合了1.12.2版本:

https://i.loli.net/2020/07/28/ynUOHsEhXp9kYTR.jpg

提示:点击这里可以查看最新版本的最完整的文件结构。




动态材质(前篇)Animation




本节我们将实现类似于原版流水和命令方块的动态材质。

https://sqwatermark.gitee.io/resguide/assets/img/0b49e5b5212444509e87ec77524cfd10.28d4d5cb.gif

动态材质就像老式电影的胶卷一样,实际上是逐帧绘制,竖向连接成长条的单张图案。在游戏中,自上而下逐帧播放,反复循环。一共有多少帧,每一帧播放多长时间,都是可以自定义的。

https://sqwatermark.gitee.io/resguide/assets/img/image-20200709190728020.8f05edf6.png

提示:因为最后一帧之后紧接着就是第一帧,所以开头和结尾最好要能接上。

我们要用上图展示的蓝色呼吸灯替换原版的淡蓝色混凝土。

首先,将材质命名为light_blue_concrete.png,替换掉原先的混凝土材质。然后在旁边新建文件,命名为light_blue_concrete.png.mcmeta,并向文件中写入如下代码:

{
    "animation": {
      
    }
}
展开查看效果(闪烁警告):
https://sqwatermark.gitee.io/resguide/assets/img/5f06ffe684eee_5f06ffe79fce6.0b549c12.gif
闪烁过快,我们需要调慢闪烁速度。

将代码更改如下:

{
    "animation": {
      "frametime": 3
    }
}
在这段代码中我们添加了frametime,frametime表示材质的每一帧持续的时间(单位为0.05秒),默认值为1,也就是说一秒可以播放20帧,我们把它放慢三倍。

我们看到材质的变化柔和了许多。

展开查看效果:
https://sqwatermark.gitee.io/resguide/assets/img/5f070338c66e8_5f070339393a0.fee5606a.gif
接下来我们注意到这张材质是上下对称的,下面的四张材质显得有些浪费,我们作为清正廉洁的资源包作者,当然要能贪就贪,把这四张材质给去掉。

https://sqwatermark.gitee.io/resguide/assets/img/image-20200712092435178.ce91df36.png

然后我们要修改一下mcmeta文件:

light_blue_concrete.png.mcmeta
{
    "animation": {
      "frametime": 3,
      "frames":
    }
}
"frames"里填写帧播放的次序,最上面的一帧是第0帧,我让它先从0帧播放到5帧,再倒着从5帧返回0帧了。

提示:动态材质的更多写法请看wiki






模型和渲染Model & Render




模型如果要展开来说,内容实在太多了,而且有非常多翔实的资料,不需要我再唠一遍。所以在开始写这一章之前,我不知道如何入手,最终我决定专注于讲一些不太常被提起但容易造成迷惑的概念,至于建模的流程,我会给出论坛内外的教程,供各位参考。

这一章理论居多,例子偏少,等内容逐渐完善,将会补充更多实例。

本章不涉及实体模型,实体模型将单独分出一章进行阐述。

章节目录

[*]建模相关教程
[*]阴影
[*]环境光遮蔽
[*]面剔除
[*]tintindex
[*]不透明方块
[*]渲染类型
[*]物品展示框——附加模型包
[*]物品标签——添加更多物品
[*]蝶舞——盆栽艺术





建模相关教程Tutorials




建模软件教程

[*]【建模教程】Cubik Studio使用教程:功能强大的付费软件!
[*]【建模教程】Block bench使用教程:免费的mc3D建模软件!
[*][教程]Blockbench教程-做出属于自己的模型
[*]【教程】如何免费简单地制作原版3D模型!

原版模型

[*]【教程】【1.9Snap+】资源包模型教程及1.9新特性研究
[*]Minecraft中文wiki:模型
[*][教程]物品手持/背包显示不同材质
[*]【教程】奈子的超杂项材质教程:父子继承/动态物品模型/动态物品材质





阴影Shading




一位朋友在为模组制作竹子的模型的时候遇到了麻烦,促使我注意到Minecraft模型的阴影效果。

https://i.loli.net/2020/07/28/6Ch3jVm8OYegFBn.png

竹叶的底面出现了突兀的阴影,这原本也没什么,毕竟没阴影才不正常,但大面积种植后我们发现这样的阴影非常影响观感。

查阅了模型相关的资料之后我们发现方块模型的element有一个叫"shade"的选项,默认情况下为true,当它为false的时候,这个元素就不会渲染阴影了。

{
      "from": [-8, -12, 32],
      "to": ,
      "shade": false,
      "rotation": {"angle": -45, "axis": "x", "origin": },
      "faces": {
                "north": {"uv": , "texture": "#1"},
                "east": {"uv": , "texture": "#1"},
                "south": {"uv": , "texture": "#1"},
                "west": {"uv": , "texture": "#1"},
                "up": {"uv": , "texture": "#1"},
                "down": {"uv": , "texture": "#1"}
      }
}
我们将竹叶的所有元素的shade都设置成了false,效果如下。

https://i.loli.net/2020/07/28/njHIDWtNFPqeCfS.png

这让我对Minecraft的阴影产生了兴趣,后来我找到了一篇Blog,简单叙述了Minecraft的光照。我也翻译了这篇文章。有兴趣可以点击这里查看。

在这里节选一段,以供参考:
除了上述的每个方块的照明,Minecraft还可以为方块的每个面进行一些额外的计算,来增强照明的真实性。
这个方法根据面的朝向,用不同的光照强度渲染六个面:

[*]顶面(Y+)为最大亮度
[*]底面(Y-)为50%亮度
[*]Z轴上的面(Z+、Z-=南、北)为80%亮度
[*]Z轴上的面(X+、X-=东、西)为60%亮度
https://i.loli.net/2020/07/28/y1PfcdXoIj5nD6g.png






面剔除Face Culling



面剔除(Face culling)是游戏开发中提高渲染效率的重要方式,尤其是像Minecraft这样的游戏中。

想象一个高楼,如果将其简化为一个立方体,它拥有六个面。

但我们真的能看到六个面吗?首先它的底面我们是无论如何也看不到的,其次,我们无法同时看到超过多于三个面,通常情况下我们只能看到两个面——除非飞在高空中,否则我们看不到它的顶面。

https://sqwatermark.gitee.io/resguide/assets/img/image-20200706221351663.67038980.png

如果我们让游戏同时绘制六个面,岂不是太浪费性能!

再看Minecraft,光是地表的方块就非常多,如果算上被掩埋在地底的所有方块,渲染其所有面,计算量将会十分庞大。理所应当,看不到的面就不应该渲染,当我们开启旁观者模式,潜入地下,我们会直接透视到远处的岩浆湖。从我们的视点到远处的岩浆湖之间数不清的方块根本没有被渲染。

https://sqwatermark.gitee.io/resguide/assets/img/image-20200708101757000.b3f5663d.png

这之间的绝大部分面,都被面剔除算法给舍弃了,如果一个方块紧挨着另一个方块时,这两个方块的相邻的面都不应该被渲染。道理很简单,但实际操作时,还会出现很多麻烦的问题。例如,怎样才算是紧挨着?如果挨着的方块是透明的应该怎么办?如果紧挨着的面不够大,盖不全该怎么办?

这些问题从两方面解决了。

从对方考虑,如果对方不是一个完整的不透明方块,那我们认为它无法遮挡住一个面,也就不会发生面剔除。

如果对方是一个完整的不透明的方块,例如一块石头。我们就要从己方考虑,这边是一个什么样的方块?

拿下半台阶为例:

slab.json
{
"parent": "block/block",
"textures": {
    "particle": "#side"
},
"elements": [
    {
      "from": [ 0, 0, 0 ],
      "to": [ 16, 8, 16 ],
      "faces": {
      "down":{ "uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "down" },
      "up":    { "uv": [ 0, 0, 16, 16 ], "texture": "#top" },
      "north": { "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "north" },
      "south": { "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "south" },
      "west":{ "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "west" },
      "east":{ "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "east" }
      }
    }
]
}

https://sqwatermark.gitee.io/resguide/assets/img/image-20200708101857617.8f30e694.png

我们看到这个台阶的四面被放上了完整不透明方块。其顶部虽有方块,但并未被剔除。

但是我们来到一个与之对立的视点。就完全看不到这个台阶了,这个方向的三个面全部被剔除。

https://sqwatermark.gitee.io/resguide/assets/img/image-20200708101922214.fd12ccfa.png

由此我们可以完全理解模型文件中用于面的cullface参数:

如果一个面的cullface为south,当这个方块的南方为一个完整不透明方块时,这个面就会被剔除。
(真实情况要更为复杂,一个面是否被剔除是根据紧邻面的RenderShape等因素综合判断的)

而例子中的台阶的顶面没有cullface,当然也就不会被剔除。

提示:你说在最后一张图中台阶的顶面也未被渲染?
当一个面背对着玩家时,这个面就已经被宣判死刑了,哪里还需要判断这么多东西。





着色序数Tint Index



注意:本节大部分内容是我阅读源码后给出的理解,如有谬误请批评指正。

前置知识:
​ RGB颜色的概念
​ 如果有编程基础,会更容易理解一些

当一个方块或物品设置了tintindex后,它将被试图用硬编码着色。

提示:这意味着,如果你不想让已经人工上色的材质被硬编码着色搞得很难看,直接找到对应的模型,删去tintindex或者将tintindex的值改为-1(因为tintindex的默认值为-1)即可。

硬编码着色流程
如果一个材质被硬编码着色,那么这个材质原本的颜色会被乘以一个颜色乘数。得到一个新的颜色。

以云杉树叶为例,它的硬编码颜色是6396257,这是一个十进制值,对应着RGB颜色(97,153,97)

https://sqwatermark.gitee.io/resguide/assets/img/image-20200707100615893.8eab3119.png

然后将(97,153,97)除以255,得到颜色乘数(0.38039216, 0.6, 0.38039216)

云杉树叶的材质实际上是一张灰度图,对于它的每一个点,R、G、B三个值都相同。假如有一个像素的颜色为(179,179,179),它的RGB值与颜色乘数分别相乘,得到(179×0.38039216, 179×0.6, 179×0.38039216),保留整数为(68,107,68)

结果就是,这个像素被染成了如下颜色:

https://sqwatermark.gitee.io/resguide/assets/img/image-20200707102459192.5b4d0298.png

云杉树叶的每一个像素都经过了这样的乘法,最终成为我们在游戏中看到的颜色。

提示:我们可以这样理解:云杉树叶的硬编码颜色决定了它的色相和饱和度,而它的灰度图材质决定了每个像素的明度。(这样说只是便于理解,和实际定义并不一致)

上面提到,材质设置了tintindex后,它将被试图用硬编码着色。事实上对于大部分方块和物品,并不存在对应的硬编码颜色,如果这些方块或物品的材质被设置了tintindex,获得的颜色乘数其实为(1,1,1),假设材质上有一点原本的颜色为(a,b,c),与此颜色乘数相乘后依然是(a,b,c),什么都不会改变。

注意:请勿混淆tintindex和硬编码颜色,这两个是完全不同的概念。硬编码颜色是在Minecraft程序内部定义的,玩家通常无法修改(OptiFine提供了修改方法)。tintindex只是决定了材质是否要使用硬编码着色,这是玩家可以直接修改的。

tintindex的不同取值
我们知道tintindex决定了材质是否要使用硬编码着色,但如果tintindex只有这个作用,那为什么它不是一个布尔值(true或false)而是一个整数?

对于方块似乎是这样的,但是对于部分物品,tintindex的具体取值起到了作用。

被绝大部分物品继承的generated有五个渲染层,分别是layer0,layer1,layer2,layer3,layer4,它们的tintindex分别为0,1,2,3,4。原版模型最多使用了layer0和layer1,但另外几个渲染层是实际存在且可以使用的(在OptiFine的自定义物品材质章节中我们将看到渲染层的用法)。

以药水为例:

potion.json
{
    "parent": "item/generated",
    "textures": {
      "layer0": "item/potion_overlay",
      "layer1": "item/potion"
    }
}
我们看到药水有两个渲染层,截个图给大家看一下分别是什么:

potion.png
https://i.loli.net/2020/11/18/tAMxj8rfZCalw5Q.png

potion_overlay.png
https://i.loli.net/2020/11/18/9VOCnFxGHBWIN71.png

很显然,potion.png是瓶子,不会被染色,而potion_overlay.png是瓶子里的药水,而药水有各种各样的颜色。

在Minecraft内部的实现:
return tintindex > 0 ? -1 : PotionUtils.getColor(...);
即判断tintindex是否大于0,如果大于0(对应layer1),返回的颜色乘数为-1(转换后为(1,1,1),即不染色);如果不大于0(对应layer0),返回药水的颜色乘数(即染色)。

而刷怪蛋的两个渲染层都会被染色(下图来源于一个Blog):

https://sqwatermark.gitee.io/resguide/assets/img/141229RvW-ItemLayerRenders.29b1b954.png

受到类似的规则影响的物品还有皮革盔甲,焰火之星,地图,药箭等。





不透明方块Opaque Cube




很多人在刚接触模型时,做了个方块模型就把原版的某些方块给替换掉了,但往往效果极其糟糕。

例如:
https://sqwatermark.gitee.io/resguide/assets/img/image-20200707131752103.3bd7c71d.png

我们发现模型和其他方块接触的面出现“透视”了,而且一部分材质出奇的黑。

这是因为我们替换的是原版不透明方块,它们占据了绝大部分原版方块。一方面,它们导致了面剔除,另一方面,不透明方块内部的亮度计算也和透明方块不同,导致一部分材质变黑。

由于这种事情是写死在代码里的,我们在做模型的时候只能规避这些方块了。

提示:学习做mod就可以摆脱束缚了。




渲染类型Render Type



除了不透明方块之外,替换原版模型/材质还容易遇到的一个问题就是渲染类型(Render Type)。

在旧版本中,渲染类型被称为渲染层(Render Layer),而渲染类型这个词表示另外的东西。

https://sqwatermark.gitee.io/resguide/assets/img/image-20200707133756597.d5d1959a.png

我们看到竹叶原本是透明的,但是在这个例子里,透明的部分被渲染为白色了。这是方块的渲染类型导致的。

方块有五种渲染类型:

[*]SOLID - 绝大部分完整不透明方块是这个渲染类型,不支持alpha通道,即透明度,即便原材质有透明部分,也会被渲染为白色。
[*]CUTOUT - 绝大部分非完整方块是这个渲染类型,它支持完全透明,但不支持半透明。
[*]CUTOUT_MIPPED - 带多级纹理的CUTOUT。
[*]TRANSLUCENT - 支持半透明材质,冰块就是这个渲染类型。
[*]TRANSLUCENT_NO_CRUMBLING - 高版本新增的半透明的变种。

下图来源于一个Blog:

https://sqwatermark.gitee.io/resguide/assets/img/141222RvW-BlockRenderLayers.98fc0bee.png

提示:通常情况下渲染类型也是资源包所无能为力的,总而言之,尽量不要用原版方块的模型做一些奇奇怪怪的东西,很容易出现渲染问题。

用OptiFine修改渲染类型
上面说了通常情况下资源包对渲染类型无能为力,但我们还是有个更改方块的渲染类型的方法的,那就是OptiFine的自定义方块。但是对固体不透明方块,此方法无效。这个方法比较明显的用途,也就是在仅支持纯透明的方块中实现半透明材质。

举个例子,我想让末地烛变成半透明的,先调整材质的不透明度,查看效果:

https://sqwatermark.gitee.io/resguide/assets/img/image-20200715142808173.225d6a98.png

由于其渲染类型为CUTOUT,理所当然没有显示出半透明材质,现在我们在optifine文件夹下直接新建文件名为block.properties:

block.properties
layer.translucent=end_rod
现在它的确是半透明的了:

https://sqwatermark.gitee.io/resguide/assets/img/image-20200715143211150.00987d1e.png





物品展示框——附加模型包Modelpack




在模型章节,我们认识到了原版方块模型存在诸多限制,而原版提供的渲染类型为cutout的方块并不多,那么我们想为资源包添加很多很多的方块模型是不是就没办法了呢?

确实没办法,但是我们可以添加很多很多物品模型,并通过物品展示框展示出来,达到类似方块模型的效果。从Minecraft1.8支持自定义模型起,IT-Project模型包就是这么个做法,不过后来使用了CustomStuff4,以通过模组增加更多的模型了。

现在请观赏IT-Project的宣传视频,感受一下模型包的强大之处:
https://www.bilibili.com/video/BV1js41167GP

下面我将演示一下如何利用物品展示框制作附加模型包。

注意:物品展示框是实体,放置过多可能会造成游戏严重卡顿。

我把博丽神社的奉纳箱偷了过来(材质是随便找的素材):

https://sqwatermark.gitee.io/resguide/assets/img/image-20200711115558032.742608d0.png

调整模型在物品展示框中的显示:

https://sqwatermark.gitee.io/resguide/assets/img/image-20200711123530207.bdf48ff0.png

对于其他模型,点击默认方块——应用于此插槽就好了。

https://sqwatermark.gitee.io/resguide/assets/img/image-20200711124536071.911565ec.png

导出为json模型,并改名为composer.json,替换掉原先的堆肥桶物品模型(注意是物品模型而不是方块模型),并把材质放到对应位置。

模型文件中textures的路径是相对于assets/minecraft/textures文件夹的路径,放在哪都行,不过我决定放在modelpack文件夹下:

{
      "credit": "Made with Blockbench",
      "textures": {
                "particle": "modelpack/wood_plank_1",
                "texture": "modelpack/wood_plank_1",
                "texture1": "modelpack/fengnaxiang"
      },
      "elements": [
                ...
      ],
      "display": {
                ...
      }
}
https://sqwatermark.gitee.io/resguide/assets/img/image-20200711125941227.ed2ff7e0.png

进入游戏,加载资源包,看看效果:

https://sqwatermark.gitee.io/resguide/assets/img/image-20200711130214220.ddf687f1.png

直接把它放出来,它还会是堆肥桶,因为方块模型没改,但是如果我们放出物品展示框,把它放到物品展示框里,模型就完全不一样了:

https://sqwatermark.gitee.io/resguide/assets/img/image-20200711130612635.234ac1dc.png

现在又出现两个问题,物品展示框会显示出来,物品展示框又必须依附于一个方块。

后一个问题很好解决:放出屏障就可以了。

物品展示框虽然是个实体,却拥有可以自定义的方块模型和材质,我们可以将其材质换成透明材质。

提示:从1.16版本起,物品展示框拥有了Invisible数据标签,可以直接设置不可见的物品展示框。

一个非常省事的做法是,直接把物品展示框的方块模型改成一对花括号:

item_frame.json
{
}
物品展示框的模型直接消失了,大成功!

https://sqwatermark.gitee.io/resguide/assets/img/image-20200711132702370.18902c2b.png





物品标签——添加更多物品Item Tags




参阅:Minecraft wiki

利用物品标签中的damage或custom_model_data,可以实现原版添加更多物品模型。

custom_model_data标签是1.14加入的,对于1.14之后的版本,建议使用custom_model_data而不是damage。

damage用法
以钻石锄为例,这里填写的damage的值为0-1之间的浮点数,对应整数类型的损害值Damage,对于有耐久度的物品,损害值为当前已消耗的耐久度。

[*]耐久度为1561时,损害值Damage为0,物品标签damage值为0。
[*]耐久度为0时,损害值为1561,物品标签damage值为1。

diamond_hoe.json
{
    "parent": "item/handheld",
    "textures": {
      "layer0": "item/diamond_hoe"
    },
    "overrides": [
      { "predicate": {"damage": 0}, "model": "item/diamond_hoe"},
      { "predicate": {"damage": 0.0006402048655569782}, "model": "item/jineng/1"},
      { "predicate": {"damage": 0.0012804097311139564}, "model": "item/jineng/2"},
      { "predicate": {"damage": 0.0019206145966709346}, "model": "item/jineng/3"},
      ...
    ]
}
https://sqwatermark.gitee.io/resguide/assets/img/image-20200722140339694.03e6fc8c.png

下面分别为Damage为1-9时,钻石锄显示的图案,Unbreakable标签设置为1,用于隐藏耐久度槽:

https://sqwatermark.gitee.io/resguide/assets/img/image-20200722140313561.70261a78.png

最令人头大的问题是,damage应该填哪些值,实际上游戏程序会将物品的实际damage值和predicate中给出的damage值作比较,predicate中给出的damage值可以比精确计算值小。这里有一个工具,可以帮你计算并生成json文件。

custom_model_data用法
一看名字我们就知道这个标签就是专门为模型准备的,不会和别的功能混到一块。所以在1.14之后的版本,建议用这个。

diamond_hoe.json
{
    "parent": "item/handheld",
    "textures": {
      "layer0": "item/diamond_hoe"
    },
    "overrides": [
      { "predicate": {"custom_model_data": 1}, "model": "item/jineng/1"}
    ]
}
custom_model_data由CustomModelData整数NBT字段直接支持。

https://sqwatermark.gitee.io/resguide/assets/img/image-20200722151801895.85322ef7.png

https://sqwatermark.gitee.io/resguide/assets/img/image-20200722151851998.371daa9b.png





蝶舞——修改模型代码Flower Pot




前置知识:
​      json的语法
蝴蝶的动态材质模仿了conquest材质包
我们的目标是制作下面视频展示的效果:

https://www.bilibili.com/video/BV1Nf4y1R7fz

首先准备好蝴蝶的动态材质:

https://i.loli.net/2020/07/27/QLBfEzKYCu6tJ8R.png

本节中我们要试着不使用Blockbench,徒手修改一下模型文件,以加深对模型文件本身的理解。

potted_blue_orchid.json
{
    "parent": "block/flower_pot_cross",
    "textures": {
      "plant": "block/blue_orchid"
    }
}
我们看到兰花盆栽的模型是继承了flower_pot_cross的,要在兰花上添加一对飞舞的蝴蝶,必须要修改flower_pot_cross.json。修改思路是把花盆中的花的模型复制一份,向上平移一段距离。

因为我们只想修改兰花盆栽,为了避免影响到其他盆栽,我们将文件复制一份,改个名叫flower_pot_butterfly.json,并令potted_blue_orchid继承flower_pot_butterfly。

flower_pot_butterfly.json
{
    "ambientocclusion": false,
    "textures": {
      "particle": "block/flower_pot",
      "flowerpot": "block/flower_pot",
      "dirt": "block/dirt"
    },
    "elements": [
      ...
      {   
            "from": [ 2.6, 4, 8 ],
            "to": [ 13.4, 16, 8 ],
            "rotation": { "origin": [ 8, 8, 8 ], "axis": "y", "angle": 45, "rescale": true },
            "faces": {
                "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" },
                "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" }
            }
      },
      {   
            "from": [ 8, 4, 2.6 ],
            "to": [ 8, 16, 13.4 ],
            "rotation": { "origin": [ 8, 8, 8 ], "axis": "y", "angle": 45, "rescale": true },
            "faces": {
                "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" },
                "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" }
            }
      }
    ]
}
忽略我们不需要修改的部分,我们注意到最后两个element的材质为#plant,而且绕y轴的旋转角度都是45度,表明它们是盆栽中花的交叉模型。

于是我们复制这两个element,粘贴到后边,接下来只展示这两个element的代码:
{   
      "from": [ 2.6, 4, 8 ],
      "to": [ 13.4, 16, 8 ],
      "rotation": { "origin": [ 8, 8, 8 ], "axis": "y", "angle": 45, "rescale": true },
      "faces": {
                "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" },
                "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" }
      }
},
{   
      "from": [ 8, 4, 2.6 ],
      "to": [ 8, 16, 13.4 ],
      "rotation": { "origin": [ 8, 8, 8 ], "axis": "y", "angle": 45, "rescale": true },
      "faces": {
                "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" },
                "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" }
      }
}
首先我们希望它使用与盆栽里的植物不同的材质,所以将"#plant"更改为"#butterfly"。

此外,"from"和"to"表示了三维空间中的一对坐标,要修改y轴坐标的值,让模型向上移动。我们先移动6个像素,看看情况。

于是我们把这两个元素修改成这样:
{   
      "from": [ 2.6, 10, 8 ],
      "to": [ 13.4, 22, 8 ],
      "rotation": { "origin": [ 8, 8, 8 ], "axis": "y", "angle": 45, "rescale": true },
      "faces": {
                "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#butterfly" },
                "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#butterfly" }
      }
},
{   
      "from": [ 8, 10, 2.6 ],
      "to": [ 8, 22, 13.4 ],
      "rotation": { "origin": [ 8, 8, 8 ], "axis": "y", "angle": 45, "rescale": true },
      "faces": {
                "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#butterfly" },
                "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#butterfly" }
      }
}
回到 potted_blue_orchid.json
{
    "parent": "block/flower_pot_butterfly",
    "textures": {
      "plant": "block/blue_orchid",
                "butterfly": "block/butterfly_blue"
    }
}
进入游戏查看效果:

https://i.loli.net/2020/07/27/1CfXHK58IY3nEkG.png

完成!

提示:花盆曾经是方块实体,每种类型的盆栽对应一种方块状态,在1.13扁平化更新中被拆分成了许多独立的方块。






额外的资源文件More Assets




Minecraft经常迭代版本,如果玩家经常更新版本的话,不可避免地要下载很多资源文件,我们知道很多时候这些资源是重复的,不需要每更新一次就下载一次。Minecraft开发团队显然也知道这一点,于是他们把部分占用空间较大的一部分资源文件从版本的jar包中给独立出去了。它们被放在了.minecraft/assets文件夹下,主要包括音效和语言文件等。即便它们丢失,也不会对游戏的正常启动造成影响,顶多没有声音或者没有相应语言的翻译(英文语言文件en_us.lang是放在jar包下的)。

圈内笑话:https://i.loli.net/2020/07/27/siHkrCB2RZUE5NP.jpg
打开这个文件夹你就会发现它和一个正常的资源包的结构完全不同。绝大部分资源文件名字变成了哈希值,被放在了一个叫做objects的文件夹下,根本无从下手。

目录文件

为了弄清楚这些文件的真面目,让我们打开.minecraft/assets/indexes文件夹,找到你在使用的版本的json文件。我的示例是1.15.2版本,所以我要打开1.15.json。

https://i.loli.net/2020/07/27/XFWb4aPRxqc5m9p.png

提示:我用IDEA展示这个文件只是为了展示的更清楚。因为原本的文件被压缩成了一行,几乎没什么可读性,我用IDEA的格式化功能(快捷键`Ctrl`+`Alt`+`L`)把它展开了。

撇去json的格式,我们搬出其中的一小段来进行解释:
"icons/icon_16x16.png": {
"hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a",
"size": 3665
}
这是整个文件的第一个有价值的片段,我们看到了一个文件的路径,我们看到了一个hash值,和一个size值。它们的含义是什么?

这里的文件路径是相对于版本的jar包的assets文件夹的路径,同样也就是相对于资源包的assets文件夹的路径。如果我们在我们的资源包下新建一个icons文件夹,在icons文件夹下放上一张名为icon_16x16.png的图片,那么理所应当原本的这张图片将会被我们的图片所覆盖。

根据信息寻找文件

那我们怎么找到原本的这张图片?通过这里给出的hash值寻找,这里给出的hash值的前两位是bd,那我们进入.minecraft/assets/objects/文件夹,找到名为bd的子文件夹,不出意外的话你会在里面找到一个名字和这个hash值一样的文件。

https://i.loli.net/2020/07/27/hZUBqctgOyXNHi4.png

我们知道它原本应该叫icon_16x16.png,为了防止我们正常的游戏出现问题,让我们复制一份,放到资源包中。

https://i.loli.net/2020/07/27/MlY4DICAKRyUzEc.png

把文件名改为icon_16x16.png,我们可以看到它的真面目了。

https://i.loli.net/2020/07/27/S1KqaxgnstiWlCz.png

注意:由于mc的图标只会在游戏刚启动时设置一遍,那时候资源包还未被加载,所以无法从资源包修改原版图标。但是音乐等文件是可以通过资源包覆盖的。(在2021/1/19之前我一直认为资源包可以修改图标,直到我亲自尝试了一遍,之前受到误导的各位,实在是对不起!)

警告:不要直接修改这些文件,即便你必须要修改,也请使用资源包。Minecraft 会在连接网络的时候根据index文件夹里的json文件还原你所做的更改。即便有办法绕过文件哈希值和大小检测,也仍然强烈建议不要直接修改这些文件,尤其是要将其分发出去时,发布擅自修改的 Minecraft资源文件是违反 EULA 的)






连接材质(CTM)Connected Textures Mod




至此我们来到了资源包最具魅力的部分之一。在此之前,我们制作的方块千篇一律——只要是同一个方块,长得都是一个样子,即便有随机材质,其能力也是有限的。而连接材质突破了这一限制,它使Minecraft材质更富于变化,更加自然,更为丰富多彩。它的完美应用可以参考Conquest\_,我们为Conquest\_带来的沉浸体验而惊叹,现在我们可以一窥究竟,探寻其神奇背后的机理了。

提示:从本章起,需要各位阅读OptiFine帮助文档了,教程不可能面面俱到,我也不会试图去把所有的东西都过一遍,这样很浪费时间,举一反三是必要的技能。

章节目录


[*]CTM方法
[*]枯山水——CTM
[*]枯山水——随机材质
[*]枯山水——生物群系
[*]枯山水——优先级
[*]简化CTM方法
[*]海鼠壁——竖向连接
[*]海鼠壁——横向连接
[*]砂砾——重复图案
[*]砂砾——覆盖材质
[*]卢恩符文——覆盖方法的变体
[*]杂项——补充


相关教程推荐

【PCD】Optifine 进阶技巧: 令方块在特定情况下显示特定模型






CTM方法CTM Method




CTM方法作为最基本的连接材质方法,常见于玻璃,通常情况下,OptiFine会内置玻璃的CTM材质(但常常会不显示)。

注意:CTM方法是最原始的连接材质方法,以至于它和连接材质同名,但二者是包含关系。

CTM方法看上去是这样的:

https://s1.ax1x.com/2020/07/27/aiteX9.png

在上面这张图片中,玻璃之间的接缝被完全抹除,只剩下作为整体的边框,这就是CTM的设计初衷,虽然现在许多创作者用CTM做出了完全不一样的东西,其实都是对这种方法的创新。

CTM的实现

CTM方法本质上就是穷举,它根据材质的位置,将所有可能的图样都穷举出来,总共有47种之多。

这导致CTM的绘制非常繁琐,为了简化操作,OptiFine还提供了一种简化CTM方法,只需要5张材质,将会在之后介绍。

https://s1.ax1x.com/2020/07/27/aitZ6J.png

方便起见,我们不妨就用OptiFine作者提供的模板制作一个CTM材质,并用于铁块。

先把这张图分割开:

https://s1.ax1x.com/2020/07/27/aitikV.png

现在让我们回到资源包文件夹,在assets文件夹下新建一个叫做optifine的文件夹,然后在optifine文件夹内新建名为ctm的文件夹,将图片拖进去。

https://s1.ax1x.com/2020/07/27/aitCT0.png

注意:在1.13以前的版本,ctm文件夹是放在名为mcpatcher的文件夹内的,路径为assets/minecraft/mcpatcher/ctm,1.13版本后,所有原先放在mcpatcher文件夹下的内容都转移至optifine文件夹下了。

在此处新建一个文本文件,命名为iron_block.properties(这样命名是为了方便识别,你可以在符合规定的前提下任意取一个名字,当然后缀名必须是properties),打开文件,输入以下代码:
method=ctm
tiles=0-46
matchBlocks=minecraft:iron_block
https://s1.ax1x.com/2020/07/27/ait9wq.png

保存,进入游戏加载资源包,放出铁块:

https://s1.ax1x.com/2020/07/27/aitFYT.png

非常完美!通过这个例子,我们可以轻易看出CTM的每张材质会出现在什么位置。

简单解释一下刚才写的properties文件,到目前为止还是第一次出现这种格式的文件,要制作OptiFine资源包,我们必须得和它打交道了。
# 这是一行注释
# properties文件就是由下面这样的 key=value 格式组成的,每一行都是一个键值对
# method表示我们使用的连接材质方法,这里我们使用ctm方法,所以填ctm
method=ctm
# tiles表示我们要使用哪些图片,0-46是一种简写,它表示从0.png到46.png这47张材质
tiles=0-46
# matchBlocks表示我们要让什么方块使用这些材质,填写的是铁块的方块id
matchBlocks=minecraft:iron_block
举一反三,相信你已经可以根据文档填写更多的属性(properties)了。

ctm材质和相应的属性文件可以放在ctm文件夹的任意一级子文件夹下,所以分门别类进行整理是一个良好的习惯。
https://s1.ax1x.com/2020/07/27/aitEpF.png






枯山水——CTMKaresansui——CTM




许多艺术创作者用CTM展示的图案不是简单的面与边框,而是更富有创造性的东西。

下图是我的朋友绘制的枯山水,这个图案由两种完全不同的方块构成,下图中被圈起来的是一种使用了CTM格式的方块,而没被圈起来的是另一种方块:

https://i.loli.net/2020/07/28/QpTUNVOxJ53qyiw.png

复杂CTM的思路梳理

我们要用两种方块制作枯山水,ctm格式的用红沙制作,非ctm格式的用沙子制作。

前面制作的铁块ctm这时起到了作用,我们用铁块摆出形状,从而推测出该如何绘制材质:

https://i.loli.net/2020/07/28/W1i8AdIszwJuv45.png

我们可以看出,ctm方块单独摆放时,显示0号材质,摆放成3×3的样式时,显示13,14,15,25,26,27,37,38,39号材质。

所以0号材质应该是这样的:https://i.loli.net/2020/07/28/jt13iwmbC6AWacB.png

而13,14,15,25,26,27,37,38,39号材质分别是这样的:

https://i.loli.net/2020/07/28/JFLR3VEiDUYe912.png

按照这种思路,设想可能的摆放方式,逐步画出所有材质:

https://i.loli.net/2020/07/28/so89nDwRmCHYIvM.png

将材质放入ctm文件夹内,再新建一个properties文件,我将其命名为karesansui1.properties(karesansui是枯山水的罗马音)

https://i.loli.net/2020/07/28/dhwbl5DUM6iZuPt.png

输入如下代码,保存
method=ctm
matchBlocks=minecraft:red_sand
tiles=0-47
让我们进入游戏看看效果吧!

https://i.loli.net/2020/07/28/uFX1qhlDCPZdAvR.png

结果和设想的不同,非常失败!

别急着投降,仔细观察,似乎所有材质的位置都是正确的,但是被旋转了若干个90°

插曲——随机旋转的沙子

我起初认为这是红沙拥有自然纹理,但是文档中并没有给沙子设置默认的自然纹理,游戏设置里甚至没有开启自然纹理的选项。

也就是说,红沙的随机旋转不是OptiFine造成的,而是原版的性质。

这给了我一个方向,我打开了assets/minecraft/blockstates/red_sand.json,果不其然,沙子被设置了随机模型:
{
    "variants": {
      "": [
            { "model": "block/red_sand" },
            { "model": "block/red_sand", "y": 90 },
            { "model": "block/red_sand", "y": 180 },
            { "model": "block/red_sand", "y": 270 }
      ]
    }
}
稍加修改,只保留其中的一种模型:
{
    "variants": {
      "": [
            { "model": "block/red_sand" }
      ]
    }
}
保存,重载资源包,十分完美:

https://i.loli.net/2020/07/28/pwB95zl8uHikfPV.png






枯山水——随机材质Karesansui——Random




接下来我们要实现枯山水的另一种方块:

https://i.loli.net/2020/07/28/AtsquXQk1dhSjyF.png

我们希望沙子的材质随机显示这四张材质中的一张,这一点用blockstates的随机模型很容易实现,但这里要介绍另一种连接材质方法:随机材质

karesansui2.properties
matchBlocks=minecraft:sand
# method要填random
method=random
# 随机材质方法可以用任意多张材质,我们用四张,分别名为1,2,3,4,所以可以写1-4
# 但实际上1-4只是一种简写,下面几种写法都是可行的:
# tiles = 1 2 3 4
# tiles = 1-2 3.png 4
# 前面的写法都要求图片和属性文件在同一个路径下,但实际上属性文件还可以引用不在同一个路径下的图片
# tiles = optifine/ctm/karesansui/2/1.png 2 3 4
tiles=1-4
# 此外,我们还可以设置每张材质出现的权重,例如我想让这四张材质出现的概率之比为3:2:1:1,可以写:
# weights=3 2 1 1
# 不过在这个例子中没有设置权重的必要,就不设置了
提示:图片路径的详细说明请看OptiFine帮助文档

同时把沙子的随机旋转取消掉:

sand.json
{
    "variants": {
      "": [
            { "model": "block/sand" }
      ]
    }
}
保存,重载材质包,查看一下效果吧。

https://i.loli.net/2020/07/28/xwzfdF9tZaAYPKp.png





枯山水——生物群系Karesansui——Biomes




我们不希望所有的沙子都是这样的枯山水材质,OptiFine允许限定连接材质出现的生物群系,本节我们要将枯山水材质限定在虚空(the_void)生物群系,因为虚空生物群系不会自然生成在普通世界中,我们可以用WorldEdit设置生物群系进行控制。

在两种枯山水的属性文件中分别加入这一行:
biomes=minecraft:the_void
保存,重载资源包,然后进入服务器(因为1.15.2的forge和optifine尚不兼容,只能开个本地服务器,用bukkit版本的worldedit修改生物群系)

https://i.loli.net/2020/07/27/1O8jRoUHkPr35pl.png

我们看到沙子和红沙的材质都恢复了,现在我们修改一下生物群系:

https://i.loli.net/2020/07/27/sDpaHN3Bfxg7JrV.png

我们看到被修改为the_void生物群系的区域材质变成了ctm材质。虽然生物群系生成的范围似乎有些问题,不过我们本节的目的已经达到了。

https://i.loli.net/2020/07/27/wtyc8J3CKd2PRmS.png

提示:生物群系列表可以查看Minecraft wiki
对于1.13以前的版本,生物群系的id大不相同,可以查阅扁平化前的Java版数据值






枯山水——优先级Karesansui——Weight




之前我们取消了沙子和红沙的随机模型,导致现在它们看上去非常栅格化,缺少随机性:

https://i.loli.net/2020/07/28/DFvn3ZaNBEqLAdu.png

我们要重新为它们设置随机材质

https://i.loli.net/2020/07/28/3cq2jGOnAgNFMBi.png

我们看到栅格化的问题确实减轻了不少,但是,我们之前放在这里的枯山水材质又被覆盖了。

https://i.loli.net/2020/07/28/SDV82uhxlLniKgC.png

这是因为如果不同的属性文件互相冲突,会使用文件路径排序靠前的属性文件。为了凑出这种情况,我做了点小把戏:

https://i.loli.net/2020/07/28/9gkn5yQ3hNUVE4T.png

原本sand文件夹排序在karesansui文件夹之后。我在前面加了个0,使沙子的属性文件排序在枯山水的属性文件之前。

我们把这个0给去掉,让sand排到karesansui的后边去:

https://i.loli.net/2020/07/28/2eSqiN7rc6vVlPX.png

回到游戏重载资源包,我们看到枯山水优先于沙子显示了:

https://i.loli.net/2020/07/28/Kux6MH7qkenR3iB.png

虽然这个案例是凑出来的,但如果碰到出现冲突的情况,我们确实可以通过更改文件夹的名称排序来确立属性文件的优先级。

注意:不要使用weight属性,虽然官方帮助文档里写了weight属性可以用于更改权重,但是实际上weight属性根本不会被解析,这已被sp614x承认。






简化CTM方法ctm compact




前面我们提到,对于一些简单的情况,制47张材质过于繁琐了,于是OptiFine提供了一种简化方法,只需要5张材质:

https://i.loli.net/2020/07/28/MEqVZPOyz1SdnLe.png

是不是很神奇?这五张材质是如何变成游戏中看到的样子的?
/**
* 借用一下CTM模组的设计,CTM模组是Chisel团队开发的独立于OptiFine的,可以实现连接材质的模组:
* ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
* │ 0.png         │ │ 1.png         │ │ 2.png         │
* │ ╔══════╤══════╗ │ │──────┼──────│ │ ║──────┼──────║ │
* │ ║      │      ║ │ │ │      │      │ │ │ ║      │      ║ │
* │ ║ 0    │ 1    ║ │ │ │ 4    │ 5    │ │ │ ║ 8    │ 9    ║ │
* │ ╟──────┼──────╢ │ │ │──────┼──────│ │ │ ║──────┼──────║ │
* │ ║      │      ║ │ │ │      │      │ │ │ ║      │      ║ │
* │ ║ 2    │ 3    ║ │ │ │ 6    │ 7    │ │ │ ║ 10   │11    ║ │
* │ ╚══════╧══════╝ │ │──────┼──────│ │ ║──────┼──────║ │
* └─────────────────┘ └─────────────────┘ └─────────────────┘
* ┌─────────────────┐ ┌─────────────────┐
* │ 3.png         │ │ 4.png         │
* │ ═══════╤═══════ │ │ ╝ ─────┼───── ╚ │
* │ │      │      │ │ │ │      │      │ │
* │ │ 12   │ 13   │ │ │ │ 16   │ 17   │ │
* │ │──────┼──────│ │ │ │──────┼──────│ │
* │ │      │      │ │ │ │      │      │ │
* │ │ 14   │ 15   │ │ │ │ 18   │ 19   │ │
* │ ═══════╧═══════ │ │ ╗ ─────┼───── ╔ │
* └─────────────────┘ └─────────────────┘
*
* 如图所示,在程序内部,这五张图片被横一刀竖一刀拆分成了20张小图片。
* 通过拼接这20张小图片,可以得到CTM方法的47张图片(然而开了脑洞的ctm是无法用简化ctm方法实现的)。
*
* 连接0,13,2,15,我们可以获得一张向右连接的材质(CTM方法的1号材质):
* ╔══════╤═══════
* ║      │      │
* ║ 0    │ 13   │
* ╟──────┼──────┼
* ║      │      │
* ║ 2    │ 15   │
* ╚══════╧═══════
*
* 连接8,17,2,15, 我们可以获得一张L形拐角材质,连接到右侧和上方(CTM方法的16号材质)
*
* ║ ─────┼───── ╚
* ║      │      │
* ║ 8    │ 17   │
* ╟──────┼──────┼
* ║      │      │
* ║ 2    │ 15   │
* ╚══════╧═══════
*
*/
注意:玻璃板无法使用简化CTM方法!详见这个issue:https://github.com/sp614x/optifine/issues/463

失败的尝试

虽然知道有bug,但是简化CTM还是很诱人的,我还是尝试用简化CTM方法制作了玻璃材质(1号材质黑了是因为它现在彻底透明了):

https://i.loli.net/2020/07/28/h4Vt1muqas6bR7E.png

glass.properties
method=ctm_compact
tiles=0-4
matchBlocks=minecraft:glass
我们看到简化CTM方法用于玻璃还是非常的稳健:

https://i.loli.net/2020/07/28/5XqORicLnowelB4.png

我们再试着用于玻璃板:

https://i.loli.net/2020/07/28/gQwRABM5U71DKik.png

它的顶部也被连接了,我们加上faces=sides将连接材质限制在侧面:
method=ctm_compact
tiles=0-4
matchBlocks=minecraft:glass_pane
faces=sides
https://i.loli.net/2020/07/28/kegdjGbQc7Fr6Nf.png

至此我以为简化CTM玻璃板不过如此,直到我转到了另一面:

https://i.loli.net/2020/07/28/QBfXY3qnCvORzg2.png

这种问题的确不是我有办法解决的,只能放弃了。

补充:十楼提出了通过修改模型实现的解决方案






海鼠壁——竖向连接namako——vertical




注:案例中海鼠壁的设计思路来源于Tachibana TEX 3D材质包。

本小节我们要把蓝色陶瓦替换成海鼠壁。

海鼠壁是一种日式墙壁,用于防火和防潮。

https://i.loli.net/2020/07/28/cos4pzF7VXQAM2B.jpg

观察其纹理,深蓝色打底,白色交叉网纹,上下都有白漆包边。这种中间纹理重复,顶端和底端不同的材质十分适合用竖向连接方法(vertical)做。

竖向连接方法的模板如下,它需要四张材质,相当于竖向放置的CTM。

https://i.loli.net/2020/07/28/sNu29RtjFY5rITX.png

我的朋友绘制了四张材质,与上面的模板一一对应。

https://i.loli.net/2020/07/28/BrdPbZ1lwHiRG3a.png

新建文件名为namako.properties(namako是海鼠壁的罗马音),输入以下代码:
method=vertical
tiles=0-3
# 这次我们用matchTiles,matchTiles表示我们要把什么材质换成连接材质。
# minecraft:blue_terracotta表示minecraft/textures/block/blue_terracotta.png
# 在此我们还看不出matchTiles方法相对于matchBlocks有什么独特的优势,但不久我们就会看到。
matchTiles=minecraft:blue_terracotta
有关matchTiles:经测试得出,如果材质在block文件夹的子文件夹下,需要用`block/子文件夹路径/文件名.png`这样的表示方法。

保存,重载资源包,进入游戏查看:

https://i.loli.net/2020/07/28/ES2okezL8KIjJDs.png

我们用海鼠壁围出一圈墙壁看看效果:

https://i.loli.net/2020/07/28/3UPzeYABTu2ZcF6.png

有点内味了,但我还希望在拐角处加一个包边图案。这但那是下一节的内容了,本节到此结束。





海鼠壁——横向连接namako——horizontal




在上一节中,我们为海鼠壁制作了竖向连接材质,但是我们认为它的拐角处还是太简陋了点,于是希望在拐角处也进行包边,形成横向连接,横向连接自然和纵向连接一样,需要四张材质,横向连接与纵向连接一组合,就需要4×4=16张材质。

我用Aseprite画好了这十六张材质,方便起见,是在一张画布上绘制的。

https://i.loli.net/2020/07/28/8bxyRcm4fZp1OBI.png

然后用图片剪切工具拆成16份:

https://i.loli.net/2020/07/28/nGHoPuiIX9xkzdy.png

由于之前设置的0-3号材质的序号已经发生变化,我们需要改一下namako.properties:
method=vertical
tiles=9 5 1 13
matchTiles=minecraft:blue_terracotta
接下来我们要为9号材质、5号材质、1号材质、13号材质分别设置横向连接材质。

横向连接模板:

https://i.loli.net/2020/07/28/z64W2OhQtUVAZ7R.png

pic5.properties
method=horizontal
tiles=4 5 6 7
# ./表示相对路径,./5.png表示与pic5.properties同一级文件夹下的5.png
# 除了这种表示方式外,还可以使用绝对路径表示:
# matchTiles=optifine/ctm/namako/5.png
matchTiles=./5.png
对于图片1、9、13,如法炮制:

https://i.loli.net/2020/07/28/9YDER1lb8jGwBfi.png

重载资源包看看效果:

https://i.loli.net/2020/07/28/1XaKUN4QH8mqLW7.png

虽然拐角的包边显得十分尖锐尚需打磨,但我们的重点还是了解海鼠壁材质的思路,能够举一反三。

提示:之前竖向连接使用的材质,如今可以作为被横向连接替换的材质,这种嵌套体现了连接材质的无穷可能






砂砾——重复图案gravel——repeat




为了防止同一种方块的材质千篇一律,又不显得完全随机,杂乱无章,重复图案材质是一个很好的选择。

我的朋友制作了这张128×128的石子路面,我们要用它替换砂砾:

https://i.loli.net/2020/07/28/EUW2gM6ID9Nxci7.png

我们将其分割为32×32的16份:

https://i.loli.net/2020/07/28/KfNEAagrl7YOdGb.png

dirt_stone.properties
method=repeat
# 选用matchTiles的另一个好处就是它可以很好地兼容雕刻一类的模组
# 此外,matchTiles相比matchBlocks具有优先权
# 所有matchTiles的属性文件会在macthBlocks的属性文件前检测
matchTiles=minecraft:gravel
# tiles的数量要等于height×width,顺序为从左到右,从上到下
tiles=0-15
height=4
width=4
于是我们成功将砂砾换成了大块的图案(离远了看有点像芝麻糖?):

https://i.loli.net/2020/07/28/ls2GI4U6a5qSmJi.png






砂砾——覆盖材质gravel——overlay




本节前置知识:
​      tintindex
​      渲染类型

覆盖方法乍一看并不容易有直观理解,我们首先以官方的范本为例,制作一个覆盖材质,看看效果是什么样的。

https://i.loli.net/2020/07/28/migUf95u2pIx3qv.png

为砂砾制作覆盖材质

我们要为之前制作的砂砾设置覆盖材质:

https://i.loli.net/2020/07/28/kIh93e8ur6TzVdY.png

将官方的范本裁切开,放到ctm文件夹下,并新建gravel_overlay.properties

gravel_overlay.properties
method=overlay
matchBlocks=minecraft:gravel
tiles=0-16
https://i.loli.net/2020/07/28/gtUG58uoKVMaxJl.png

很遗憾,什么都没发生!

属性文件的优先级

我们已经为砂砾设置了方法为repeat的properties文件,所以在这里产生了冲突。

在枯山水——优先级章节我们已经提到,可以通过修改属性文件的路径,路径排在前面的文件具有更高的优先级,在log文件中,我们也可以清晰看到属性文件的排序:

https://i.loli.net/2020/07/28/GoBz7xldLM49J3A.png

那么问题来了,既然我已经将gravel_overlay.properties设置为第一个加载,为什么它还是被dirt_stone.properties(砂砾——重复图案小节砂砾的属性文件)覆盖了?

这是因为matchTiles具有绝对优先级,任何macthTiles的属性文件都会在matchBlocks的属性文件之前加载。我们在dirt_stone.properties里设置的是macthTiles,而这里我们设置的是matchBlocks。

于是我们修改一下属性文件:

gravel_overlay.properties
method=overlay
matchTiles=minecraft:gravel
tiles=0-16
效果:

https://i.loli.net/2020/07/28/xAOj75VClnkdc16.png

至此我们可以看出覆盖材质出现在了砂砾和其他方块的交界处,适合用于方块间的过渡。

草方块的过渡

因为覆盖材质是出现在砂砾上,那覆盖材质本身应该是草皮的材质,否则无法实现砂砾和草方块之间的过渡。

为什么我说覆盖材质应该是草皮的材质而不是砂砾和草皮各占一部分?

这是因为overlay材质会在cutout_mipped层渲染,这一层是支持透明材质的,而且这一层会覆盖在solid也就是普通渲染层的上面,形象地说就是草皮蔓延了一部分覆盖在了砂砾上方。

提示:不了解渲染层可以看看模型和渲染——渲染类型小节。

了解了这一点,我们就可以根据模板,制作草皮覆盖层的材质了。

闭着眼瞎画的材质:

https://i.loli.net/2020/07/28/NJ6v8aZmIuMBYWf.png

我们看到虽然体现出草皮和砂砾的过渡了,但是草皮的颜色没了。

https://i.loli.net/2020/07/28/an76tdCzbwHFs3T.png

我们需要给属性文件加上tintIndex和tintBlock:

提示:不了解tintIndex可以看看模型和渲染——着色序数小节。

gravel_overlay.properties
method=overlay
matchTiles=minecraft:gravel
tiles=0-16
# 默认值为-1,也就是不使用染色
tintIndex=0
# 使用草方块的染色方式
tintBlock=minecraft:grass
很好,我们已经实现草方块和砂砾的过渡了!

https://i.loli.net/2020/07/28/wu1KoefznONHBlj.png

但现在砂砾不管遇到什么方块都会显示覆盖草方块的样子。

https://i.loli.net/2020/07/28/FzKxkuSeZcPWB2n.png

所以要加上connectBlocks或者connectTiles加以限制,我们选用connectTiles:

gravel_overlay.properties
method=overlay
matchTiles=minecraft:gravel
tiles=0-16
connectTiles=minecraft:grass_block_top
tintIndex=0
tintBlock=minecraft:grass
我们看到现在只有连接草方块时,砂砾才显示草方块的覆盖材质:

https://i.loli.net/2020/07/28/36KzfZIQj1cWgNX.png

既然如此,我们为什么不为其他方块也加上草方块的覆盖材质呢?

gravel_overlay.properties
method=overlay
# 想加多少加多少!
matchTiles=gravel sand red_sand stone
tiles=0-16
connectTiles=minecraft:grass_block_top
tintIndex=0
tintBlock=minecraft:grass
效果:

https://i.loli.net/2020/07/28/obT8kEvNZlUDIeF.png

现在看来,这个属性文件不是属于gravel的,反而是属于grass的,有了这个属性文件,可以让草方块蔓延到任何固体方块上。






卢恩符文——覆盖方法的变体overlay variants




覆盖方法还有四个变体,分别为overlay_ctm, overlay_random, overlay_repeat, overlay_fixed。它们分别和ctm, random, repeat, fixed对应,除了材质由替换变成了覆盖以外没什么区别。

我这里以overlay_random为例,其他的也就一通百通了。

卢恩符文

我打算把卢恩字母随机地印在红砂岩上,卢恩字母透明字形并不难寻找,Minecraft原版就自带了。位于unicode_page_16.png中。

我将其提取出来,新建文件名为ruins.properties:

https://i.loli.net/2020/07/28/oxk1DyVFSYZOnTM.png

ruins.properties
method=overlay_random
tiles=0-80
matchBlocks=minecraft:cut_red_sandstone
# 限定在红砂岩的侧面
faces=sides
https://i.loli.net/2020/07/28/zVjeNLiBgC35rx6.png

要让同一块红砂岩只显示一种卢恩字母,加入symmetry=all:
method=overlay_random
tiles=0-80
matchBlocks=minecraft:cut_red_sandstone
faces=sides
# 默认值为none,即每个面都单独进行随机
# 如果设为opposite,则相反的面有相同的材质
# 如果设为all,则所有面都有相同材质
symmetry=all
https://i.loli.net/2020/07/28/2tUioI8OvJfNK3V.png

享元模式

享元模式,简而言之就是从不同的结构中找出共同的东西,尽可能重复利用。

overlay材质和享元模式是相当契合的。在上一小节,我们做了草的覆盖材质,原先我们是为砂砾制作的,但最终我们将其用于许许多多的方块——只要敲几个字母,就可以添加一种方块,十分便利。

如果conquest和cocricot模组都可以使用overlay_ctm方法的话,就可以省去相当多的材质,它们的木骨架材质是木骨架与墙面材质的组合——墙面本身是不含ctm的,如果换一种墙面就得多做一套ctm材质,既浪费精力,也浪费电脑的运算能力。那不如就做一套不含墙面的ctm材质,覆盖在墙面材质上。

cocricot mod1.12.2 的木骨架

https://i.loli.net/2020/07/28/7rxAJ28oyacvGCF.png

自发光纹理

虽然有些跑题,但我觉得这里引入自发光纹理还是很舒服的。

我们可以在方块材质上添加永远以最大亮度渲染的渲染层,用于模拟材质的发光部分。

我打算让这些文字 “发光”。

首先,在optifine文件夹下新建一个名为emissive.properties的文件。

emissive.properties
# 这个文件只有这一个属性,用于设置自发光纹理的后缀。
suffix.emissive=_e
然后,将卢恩文字的材质单独复制一份,放到一个临时创建的文件夹中,然后重命名这些文件,加上_e后缀。

提示:建议使用ReNamer进行批量重命名
https://i.loli.net/2020/07/28/6wqeGf4kdPxmzjt.png

将重命名完成的文件放回原文件夹:

https://i.loli.net/2020/07/28/p6lVUHQvTS83DeP.png

重载资源包,将时间调整至晚上,查看效果:

https://i.loli.net/2020/07/28/At5GKyUEFdqP7xs.png

既然用上发光的材质了,那我们可以把原先的材质改一改,节省一点空间,能省一点是一点,万一做的是高清材质呢:

将原先的材质更换成边长为1的完全透明的材质:

https://i.loli.net/2020/07/28/P1au3gtq6prZx8I.png

重载资源包,依然不影响效果:

https://i.loli.net/2020/07/28/xsqZJUI2OFb3zVT.png

注意:这种发光没有任何照明效果。






杂项misc




之前的例子中未涉及的一些东西:


[*]内边缘接缝
[*]horizontal+vertical与vertical+horizontal
[*]method的一些其他写法


内边缘接缝

innerSeams是ctm方法和简化ctm方法的一个参数,默认值为false:

innerSeams=false:

https://i.loli.net/2020/07/28/aKQE4fhXFC9xN1o.png

innerSeams=true:

https://i.loli.net/2020/07/28/Ap7FIToDJhu6BrZ.png

horizontal+vertical与vertical+horizontal

上面一张是horizontal+vertical,下面一张是vertical+horizontal:图片来源

https://i.loli.net/2020/07/28/w1xd3vC9tAWK8rI.png

method的一些其他写法


[*]ctm也可写为glass
[*]horizontal也可写为bookshelf
[*]horizontal+vertical也可写为h+v
[*]vertical+horizontal也可写为v+h






语言和文字language and font




//TODO




自定义语言language




本章前置知识:
​      额外的资源文件
​      JSON的基本语法

修改现有的语言文件

如果你不知道怎么做,甚至还头疼原版资源包里怎么只有英语语言文件的话,看看本章前置知识。

提示:从1.13起,语言文件的格式从lang(本质properties格式)变成了json格式。

如何添加一门语言

Minecraft 是支持添加新语言的,在这里有专业的说明。

pack.memeta 编写示例:
{
"pack":{
    "pack_format":4,
    "description":"添加自定义语言"
},
"language":{
    "my_lang":{               //语言的编号,也就是语言文件的名字,例如简体中文是 "zh_cn"
      "name":"语言的完整名称",
      "region":"国卝家或地区名称",
      "bidirectional":false   //若为true,语言将会从右到左显示。
    }
}
}
注意:
1. 务必注意 json 的语法,`pack.mcmeta` 的格式不正确的话,可能连资源包都无法加载,在 1.13 以上的版本中,语言文件也使用 json 格式,同样需要注意语法。
2. json规范本身是不支持注释的,请不要把注释也抄上。我在这里添加注释只是为了方便解说。
3. 语言文件中 `language.code`,`language.name`,`language.region` 三个键对应的值需要和 `pack.mcmeta `中的一致。

拓展阅读

Minecraft中文wiki:语言

圈内笑话

Minecraft中文WIKI关于1.3.1中部分译名错乱的说明






自定义字体font




本期将介绍Minecraft字体文件的构成,以及如何制作一个字体资源包。

字体资源简介

要了解如何修改 Minecraft 字体,有必要先了解一下相关的文件,打开原版资源包的 assets/minecraft/textures/font 文件夹,里面有 224 张 png 图片。分别为 ascii.png,asscii_sga.png 和 unicode_page_xx.png(xx从00到ff,但并不完全),在1.13以上的版本中,还要多两张图片,accented.png 和 nonlatin_european.png。


[*]`ascii.png` 用于存储 ASCII 编码字符
[*]`ascii_sga.png` 用于存储标准银河字母(Standard Galactic Alphabet,SGA),也就是附魔台周围浮现出的文字(修改这些字体不会在附魔台以外的任何地方造成不良后果)
[*]`unicode_page_xx.png` 用于存储 Unicode 编码字符,其中也包括了 ASCII 编码的所有字符,但是字体并不相同,`ascii.png` 中的字体是 Minecraft 开发团队制作的,看起来更像素一些。而 `unicode_page_xx.png` 中的字体为 GNU Unifont,由 Roman Czyborra 制作,涵盖了基本多文种平面中的所有字符。ASCII 字符默认优先于 Unicode 字符显示,但是当在语言设置界面选择 `强制使用Unicode字体` 时,只会使用 `unicode_page_xx.png` 中的字符。
[*]`accented.png` 用于存储注音文字
[*]`nonlatin_europea.png` 用于存储非拉丁语欧洲文字

此外,在assets\minecraft\font 文件夹下还有名为 glyph_sizes.bin 的十六进制文件,用于存储字符的宽度

Minecraft 渲染 Unicode 文字的思路是把`0x00000000`到`0xFFFFFFFF`的每个文字都做成材质贴图,按照高 16 位进行分割,形成`assets/minectaft/textures/font/unicode_page_ff.png`这样的图片,每个文字都是方格的;

为了解决文字宽度不同的问题,设置了一个文件(`assets/minecraft/font/glyph_sizes.bin`)来保存对应图像的水平坐标区间,采用一个字节来保存(高 4 位保存 xMin ,低 4 位保存 xMax ,当然精度方面也就只有 4 位了,但是考虑到 mc 的马赛克性质,确实足够用了),这些字节直接按照 Unicode 值作为文件中位置。

——引用自 https://www.v2ex.com/t/310750

基本多文种平面(Basic Multilingual Plane)

https://i.loli.net/2020/07/28/HYEkIFiptUfZBGL.png

这张表格与 unicode_page_xx.png 形成对应关系,但是 Minecraft 并未使用其全部内容(不包含08、D8-F8)

制作字体资源包

我写字超丑的,所以这里只介绍如何将现成的字体加入资源包,而不会教怎么自己制作字体(教程百度就有)。

注意:

[*]从 1.6 开始,Minecraft 支持高清字体,所以可以放心把分辨率调高(游戏时可能会卡)
[*]字体可以是彩色的,但是会让样式代码生成的彩色文本变得奇怪

1.13 之前

使用MINECRAFT字体生成器,贴内有使用教程,用起来还蛮方便的,就是容易卡死

1.13 之后

1.13 版本对字体进行了改进:

1.13-pre6:Improved fonts(改进了字体)

1.13-pre7:Fixed outstanding issues with the new improved fonts(修复了新优化的字体造成的较突出的问题)
这两段话几乎没说啥,但实际上这次改动极大地方便了玩家自定义字体。

我这里使用 1.15.2 版本进行演示如何添加一个 tff 字体:


[*]打开原版的 assets/minecraft/font 文件夹,里面比1.12 版本多了两个文件:`default.json` 和 `alt.json` ,前者用于控制普通文字的字体,后者用于控制附魔台文字的字体。(下面有对这两个文件格式的详解)
[*]将 tff 字体放到资源包的 assets/minecraft/font 路径下,我从网上下载了一款丁永康硬笔楷书用于演示,并命名为 `name_of_the_font.ttf`

注意:文件名不能含有小写英文字母、阿拉伯数字、下划线、横杠、小数点以外的字符(满足正则表达式),也就是说不能含有大写字母(这一点对于资源包的所有文件都适用)。

1. 在资源包的 assets/minecraft/font 路径下新建 default.json,填写以下内容:

{
    "providers": [
      {
            "type": "ttf",
            "file": "minecraft:name_of_the_font.ttf",
                        "shift": ,
                         "size": 11.0,
                         "oversample": 2.0
      }
    ]
}

   此处的 shift,size 和 oversample 都使用了默认值

2. 进入游戏查看,发现字体位置偏上,且过于模糊:

   https://i.loli.net/2020/07/28/TiJCuxF7Ueqs9Mr.png

3. 调整 default.json 中设定的值(shift和oversample):

{
    "providers": [
      {
            "type": "ttf",
            "file": "minecraft:name_of_the_font.ttf",
                     "shift": ,
                     "size": 11.0,
                     "oversample": 4.0
      }
    ]
}
4. 重新进入游戏观察,OK(但是这个字体本身过细,不适合 mc)

   https://i.loli.net/2020/07/28/Zeb9WJcmjSoFVCs.png

如何对单个字进行调整

如果你觉得某个字写的不好看,想要自己修改,但苦于在浩如烟海的文字中寻找,可以使用这里介绍的方法快速锁定字符的位置:


[*]随便在网上搜一个 Unicode 编码转换工具,将想要修改的文字转换为 Unicode 编码,例如汉字 "囧" 的 Unicode 码是 `\u56e7`
[*]忽略 "\u",根据 "56" 找到 `unicode_page_56.png`,根据 "e7" 找到第15行第8个字符(e和7分别为十六进制第15和第8个数字),就是汉字 "囧" 了

在 1.13 以上的版本,可以直接用 default.json 文件指定字符对应的图片,详见这篇文章

p.s. 可以用这种方法将一些文字替换成形象的图画

OptiFine 的改进

参考:OptiFine帮助文档:高清字体,这部分没什么需要解释的。

拓展阅读


[*]一些关于glyph_sizes.bin的信息
[*]潜影盒内容预览 - 字体黑科技
[*]【1.13】地图制作技巧——字体艺术





default.jsondefault.json




翻译自 reddit 上的一篇文章 的节选,并作了一些补充:

让我们看看这两个文件。默认的`default.json` 是这样的(我省略了一些东西,接下来会谈到):

{
"providers": [
{
"type": "bitmap",
"file": "minecraft:font/nonlatin_european.png",
"ascent": 7
"chars": [
   ...
]
},
{
"type": "bitmap",
"file": "minecraft:font/accented.png",
"height": 12,
"ascent": 10,
"chars": [
   ...
]
},
{
"type": "bitmap",
"file": "minecraft:font/ascii.png",
"ascent": 7,
"chars": [
   ...
]
},
{
"type": "legacy_unicode",
"sizes": "minecraft:font/glyph_sizes.bin",
"template": "minecraft:font/unicode_page_%s.png"
}
]
}
这个文件包含一系列字体提供器(providers)。当游戏想要渲染一个字符,它会在每个提供器中(从上到下地)寻找这个字符。有三种类型的提供器。

第一种类型:


[*]`"type": "bitmap"` 这表示包含很多字符的位图
[*]`file` 表示相对于 `assets/<命名空间/textures/` 的路径而且应该指向一张 png 图片
[*]`ascent` 是字符向上偏移的像素值
[*]`height` 是字符的像素高度
[*]`chars` 是位图包含的一系列字符(用 Unicode 编码表示),和图片进行对照不难理解其含义

第二种类型:


[*]`"type": "legacy_unicode"` 仅用于 Unicode 字体
[*]`sizes` 是相对于 `assets/<命名空间/` 的资源路径,指向包含字形尺寸的二进制文件。默认为 `glyph_sizes.bin`
[*]`template` 是相对于 `assets/<命名空间/textures/` 的资源路径,其中 `%s` 将被替换为包含 Unicode 字形的图片的页码

第三种类型:


[*]`"type": "ttf"` 表示 TTF 字体文件
[*]`file` 是相对于 `assets/<命名空间/font/` 的资源路径,且应该是一个 ttf 字体文件
[*]`shift` 是有两个值的数组。分别为水平方向偏移和竖直方向偏移。默认值为 ``
[*]`size` 是字符的像素尺寸。默认值为 `11.0`
[*]`oversample` 是字体的分辨率。当你提升这个值,字体将会渲染得更清晰,分辨率会更高。默认为 `2.0`
[*]`skip` 是提供器不应该提供的字符。例如,如果写 `"skip": "a"`,那么字符 "a" 就会以默认的 Minecraft 字体渲染,但是其他文字将会使用你提供的字体。

注意:
- 上述资源路径都要加命名空间,原版的命名空间表示方法为在路径前加 `minecraft:`
- 文件名不能含有小写英文字母、阿拉伯数字、下划线、横杠、小数点以外的字符(满足正则表达式),也就是说不能含有大写字母(这一点对于资源包的所有文件都适用)。
- 当游戏想要渲染一个字符,它会在每个提供器中(从上到下地)寻找这个字符,这意味着只要在一个提供器中找到某个字符,就不会使用后面的提供器的这个字符。资源包中的提供器会优先加载。






音效Sounds




本章前置知识:
      额外的资源文件
      JSON的基本语法




替换现有的音效replace sounds




打开 Minecraft中文wiki 的资源包模板,这里展示了原版资源包真正的文件夹结构,由于音效文件数目实在是很多,看起来容易晕,我在这里列出不包含文件的文件夹层次:

https://i.loli.net/2020/07/28/SucswAW8f7gzpZk.jpg

提示:原版音效试听:音乐,环境音效,全部音轨

替换现有的音效的方法和制作普通的资源包没什么两样,如果你还不清楚原版的音效文件在哪,请看一下额外的资源文件章节。

这里简单做出一个示例:

我想把击杀末影龙后的显示的credits界面的背景音乐换掉。


[*]我估计credits界面的bgm所在的路径应该含有credits这个单词,于是我在1.15.json中搜索credits。
[*]https://i.loli.net/2020/07/28/TpyUGfXwvNZJMkr.png
[*]现在我知道了这个音乐的路径,只需要按照这个路径在资源包里建立文件夹。

https://i.loli.net/2020/07/28/mgiAwWMcQDHukBe.png

3. 文件夹建立好了,只需要放进新的credits.ogg就可以了,但一般情况下我们手头的音源不是ogg格式的,需要进行转换。我使用的转换工具是5.3.0版本的格式工厂(一些老版本转换出来的游戏可能读取不出来)。

https://i.loli.net/2020/07/28/zYQBmrgDjsJMLNo.png

4. 把转换好的音乐改名为credits.ogg,放进刚才创建的文件夹中。

https://i.loli.net/2020/07/28/cswtIYOR2f9aWZT.png

这样我们的第一个音效就替换完成了!打开游戏试一下吧。

不知道有没有读者不知道右下角这里是可以点击的,点击后就能进入credits界面。

https://i.loli.net/2020/07/28/1nm3dhjygeoAcKx.png

非常完美。(反正你们只能看到图片就听我描述吧)

https://i.loli.net/2020/07/28/NsLPbVyjU9iWlz5.png

注意版权:在使用非自制的音源时,请一定要尊重作品的版权,未开放使用的音源请勿公开发布。

相关视频

【Uncle Jam】Minecraft-如何替换音效!(资源包教程)
https://www.bilibili.com/video/BV1yk4y1q7Ln





添加音效add sounds




Minecraft的音效资源是和一个文件密切相关的:sounds.json

sounds.json

Minecraft使用音效时,并非直接找到音效的资源路径,Minecraft内部使用的是`entity.cow.hurt`之类的格式,sounds.json则储存着这种格式和音效资源文件之间的对应关系。

和音效文件相同,sounds.json也是一个放在jar文件之外的资源文件。

提示:sounds.json最详细的说明当然得看看Wiki,比这里涉及到的用法全面多了。

撇去json的格式,我们来看看其中的一小段:
"entity.cow.hurt": {
"sounds": [
    "mob/cow/hurt1",
    "mob/cow/hurt2",
    "mob/cow/hurt3"
],
"subtitle": "subtitles.entity.cow.hurt"
}
下面进行解说:


[*]`"entity.cow.hurt"`是Minecraft内部的声音事件,可以通过`/playsound`命令播放。
[*]`"sounds"`是一个数组,里面是几个资源路径,`"mob/cow/hurt1"`实际表示`"assets/minecraft/sounds/mob/cow/hurt1.ogg"`。`sounds`数组内有3个资源路径,表示当声音事件`entity.cow.hurt`触发时,将随机播放这三个音效中的一个。
[*]`"subtitle"`是一个用于翻译的本地化键,只有游戏开启了"显示字幕"时才会显示。我们可以不加这一行。


用sounds.json添加声音事件

没错,sounds.json可以被我们用于添加声音事件,并不是真正意义上的添加音效。当我们向资源包的sounds文件夹中添加本不存在的音效文件时,事实上已经向游戏中添加音效了。但是如果不编辑sounds.json,我们没有任何播放这些音效的方式。声音事件就是我们播放音效的窗口。

这里推荐爱给素材网,这里有许多按照CC协议共享的音效文件。

注意版权:在使用非自制的音源时,请一定要尊重作品的版权,未开放使用的音源请勿公开发布。

我下载了3段鲸吼声,转码后放在了如下路径:

https://i.loli.net/2020/07/28/kHOXVKGAizJC5cx.png

在assets/minecraft路径下新建sounds.json

https://i.loli.net/2020/07/28/e6MS3o7yH9DaTY1.png

令人高兴的是,sounds.json和大部分资源文件不同,游戏会将所有sounds.json叠加起来,最终游戏中的声音事件包含了每一个sounds.json中的声音事件。

打开sounds.json,向其中添加一个基本的框架:
{
}
就这么简单,前面我们说过一个声音事件是如何定义的,现在我们来照葫芦画瓢,为鲸吼定义一个声音事件:
"entity.whale.ambient": {
"sounds": [
    "mob/whale/ambient/say1",
    "mob/whale/ambient/say2",
    "mob/whale/ambient/say3"
],
"subtitle": "subtitles.entity.whale.ambient"
}
把这段声音事件的定义插入基本框架中:
{
"entity.whale.ambient": {
    "sounds": [
      "mob/whale/ambient/say1",
      "mob/whale/ambient/say2",
      "mob/whale/ambient/say3"
    ],
    "subtitle": "subtitles.entity.whale.ambient"
}
}
现在让我们进入游戏,重载资源包,并用/playsound指令播放音效吧。

甚至出现在自动补全列表中了:

https://i.loli.net/2020/07/28/WeSatwcKMiARVTv.png

完整指令

https://i.loli.net/2020/07/28/27KIPZ8lWsJRxoy.png

虽然你们只能看到图片,反正我是听见了。

https://i.loli.net/2020/07/28/E3DsyMn6paYvHdg.png

提示:原版sounds.json文件是最好的老师。
如果还不知道json文件怎么写的话,充分发挥你的学习能力吧,去学习一下json的基本语法。

注意:文件名不能含有小写英文字母、阿拉伯数字、下划线、横杠、小数点以外的字符(满足正则表达式),也就是说不能含有大写字母(这一点对于资源包的所有文件都适用)。






自定义实体README




本章包含两个部分——随机实体材质和自定义实体模型,由于材质和模型通常是耦合的,所以放到一块讲解。






随机实体材质random entities




随机实体材质的属性文件和方块CTM、物品的CIT相比,要更为难理解一些。这是因为对于同一个实体,只有一个属性文件,不管要改多少东西,设置多少种不同属性的随机材质,都要在一个文件内完成。而CTM和CIT更为自由——可以无限制的添加属性文件的数量,一个属性文件内只需专注于一个规则。

问题1: 多个规则被放到一个属性文件内,要怎么处理呢?

例如:我想让僵尸在平原地区显示材质A,在雪原显示材质B,在雨林显示材质C,要怎么做?

随机实体材质的属性文件用序号划分不同的规则,就上边的例子而言,属性文件大概是这样的:

zombie.properties
# 这不是一个有效的属性文件,仅仅是一个便于理解的例子
# 随机实体材质的属性文件需要命名为实体的id,可以在wiki上查阅
textures.1=平原僵尸的材质
biomes.1=平原
textures.2=雪原僵尸的材质
biomes.2=雪原
textures.3=雨林僵尸的材质
biomes.3=雨林
问题2: 材质的路径怎么表示?

随机实体材质的表示方法是和CTM与CIT不同的。随机实体材质的表示方法也是序号。其中序号1表示默认材质,也就是"assets/minecraft/texture"文件夹中的材质。假如我想让平原僵尸就显示默认材质,我可以这样写:
# 这不是一个有效的属性文件,仅仅是一个便于理解的例子
textures.1=1
biomes.1=平原
textures.2=雪原僵尸的材质
biomes.2=雪原
textures.3=雨林僵尸的材质
biomes.3=雨林
其他序号表示与属性文件在相同文件夹下的材质,文件名为默认材质的文件名+序号,例如僵尸的默认材质的文件名为zombie.png,那序号2就表示与属性文件在相同文件夹下的zombie2.png,序号3表示zombie3.png,以此类推。

如果一个生物本身就有多个材质,例如狼,它有下列四张材质:

https://i.loli.net/2020/07/28/3DtSsE7LPcXmeTu.png

那序号2就表示wolf2.png,wolf_angry2.png,wolf_collar2.png,wolf_tame2.png。

如果还是觉得不太好理解,接下来将会展示几个例子。






苦力怕——生物群系random creeper




我们要让苦力怕在不同生物群系拥有不同的颜色,在寒冷的生物群系出生的苦力怕是蓝色的,在炎热的生物群系出生的苦力怕是红色的,在其他群系出生的苦力怕是绿色的。包括高压苦力怕的电火花材质。

在optifine文件夹下新建名为mob的文件夹,在mob文件夹下新建creeper文件夹以便于管理。

绘制好材质,简单调色就可以了,将其放置在creeper文件夹下,并按照如下命名:

https://i.loli.net/2020/07/28/2LtroCNF6ypdVRg.png

这里只放了红色和蓝色苦力怕,绿色苦力怕是默认材质,我将默认的电光材质也改成了绿色。

新建文件名为creeper.properties,与ctm和cit不同,这个文件名是强制的,必须用实体id,不可用别的命名。

creeper.properties
# 第一套随机材质
# 材质序号为2
textures.1=2
# 出现在沙漠(偷懒就只写了沙漠)
biomes.1=minecraft:desert
# 第二套随机材质
# 材质序号为3
textures.2=3
# 出现在各种寒冷的群系
biomes.2=frozen_ocean frozen_river snowy_mountains snowy_tundra snowy_beach snowy_taiga snowy_taiga_hills deep_frozen_ocean ice_spikes snowy_taiga_mountains
现在我们找到一块寒冷的群系,生成一只苦力怕:

https://i.loli.net/2020/07/28/82i1EmoJ9wFRXyD.png
/summon minecraft:lightning_bolt
雪原中最靓的崽:

https://i.loli.net/2020/07/28/8kjdOoxfUmAvKtQ.png

雨林中最靓的崽:

https://i.loli.net/2020/07/28/SJjZiUOLYx4u6Cn.png

沙漠中最靓的崽:

https://i.loli.net/2020/07/28/Q6amwGTtP4IbeBo.png






自定义玩家模型cpm




示例用天子皮肤是几何的作品,发布帖

本章前置知识:

​      自定义实体模型
OptiFine的cem功能不包含玩家模型,但玩家模型并非不可以自定义,但是其方法有不小的区别。

注意:接下来的内容不属于严格意义上的资源包,甚至和resourcepacks文件夹没有任何关系。请酌情阅读。

本节我们要在比那名居天子的旁边加上一块漂浮的要石:

https://i.loli.net/2020/07/28/ITBH1iV9NeKf75p.png

设置JVM启动参数

第一步,~~换上天子的皮肤~~,在游戏JVM参数中添加以下两个配置,并启动游戏。
-Dplayer.models.local=true -Dplayer.models.reload=true
上面这两个配置的第一个用于开启自定义玩家模型功能,第二个用于每五秒刷新一次模型以便测试。平时使用时,只需要添加第一个参数,测试模型时把第二个也加上吧。

https://i.loli.net/2020/07/28/lx8JDTZQNUAfG4Y.png

制作模型

在.minecraft文件夹下新建名为playermodels的文件夹,然后在playermodels文件夹里新建items文件夹和users文件夹,其中items文件夹用于存放模型和材质,users文件加用于存放玩家信息。

https://i.loli.net/2020/07/28/GQeNRsHdb2tMwSa.png

因为我们要制作的是一块石头的模型,在items文件夹内新建文件夹名为stone,在stone文件夹内新建文本文件,命名为model.cfg,这本质上也是一个json格式的文件。这个文件包含有关模型的全部信息。

model.cfg
{
// 这不是一个有效的配置文件,仅用于讲解概念
"type" : "PlayerItem", // 固定的,不要改
"usePlayerTexture": <true|false>, //是否使用玩家材质,一般填false
"textureSize": [<width>, <height>], //材质的尺寸
"models": //子模型列表
[
    { //这是一个子模型
      "baseId": <parent_id>, //父类模型的id
      "id": <id>, //模型的id
      "type": "ModelBox", //固定的,不要改
      "attachTo": "<head|body|leftArm|rightArm|leftLeg|rightLeg>", //附着到哪个部位
          "invertAxis": <axis_to_invert>, //反转轴
      "translate": [<x>, <y>, <z>], //平移
      "rotate": [<angle_x>, <angle_y>, <angle_z>], //旋转
      "mirrorTexture": <axis_to_mirror>, //镜像材质,通常不需要
      "scale": <scale>, //缩放比例,默认值为1.0
      // 部件模型的定义,详见自定义实体模型部件
      ...
    },
    ...
]
}
我们先把一些默认的值填上,以便修改:
{
// 这不是一个有效的配置文件,仅用于讲解概念
"type" : "PlayerItem",
"usePlayerTexture": false,
"textureSize": [<width>, <height>], //材质的尺寸
"models": //子模型列表
[
    {
      //不需要继承什么父类模型,所以不填baseId
      "id": "stone",
      "type": "ModelBox", //固定的,不要改
      "attachTo": "body", //附着到身体
      "translate": , //平移量
      "rotate": , //旋转
      "scale": 1.0, //缩放比例,默认值为1.0
      // 部件模型的定义,详见自定义实体模型部件
      ...
    }
]
}
自定义玩家模型的关键部分的格式也是cem模型,所以可以用BlockBench制作jpm模型,再手动修改文件。

由于默认看这一小节的读者已经掌握了jpm模型的做法,我就不演示了。现在我有一个做好的jpm模型,是用MagicaVoxel做了体素然后用Voxel Importer插件导入Blockbench的,丑,但是能用。

https://i.loli.net/2020/07/28/T8QzMiOu9dZblXs.png

我们打开jpm文件,将boxes的内容整个复制下来,粘贴到model.cfg文件中,并将textureSize给抄上。

https://i.loli.net/2020/07/28/yR4vlwP1CY2hnDq.png

model.cfg
{
// 这不是一个有效的配置文件,仅用于讲解概念
"type" : "PlayerItem",
"usePlayerTexture": false,
"textureSize": ,
"models":
[
    {
      "id": "stone",
      "type": "ModelBox",
      "attachTo": "body",
      "translate": ,
      "rotate": ,
      "scale": 1.0,
      "boxes": [
                {
                        "coordinates": [-9, -5, 7, 2, 1, 2],
                        "uvNorth": ,
                        "uvEast": ,
                        "uvSouth": ,
                        "uvWest": ,
                        "uvUp": ,
                        "uvDown":
                },
      ...//此处省略一大堆内容
      ]
    }
]
}
然后在model.cfg旁边放上材质,我们注意到model.cfg本身只规范了材质的大小,没写具体用什么材质,也就是说我们可以给不同的玩家用不同的模型,也可以给不同玩家用相同的模型,不同的材质。

https://i.loli.net/2020/07/28/ol9fVp7UTQ6indO.png

使用模型

我们的素材准备好了,接下来要让具体的玩家使用上模型和材质,进入先前创建的users文件夹,新建文件名为username.cfg,username换成自己的游戏id,我的游戏id是SQwatermark,所以文件名为SQwatermark.cfg。

username.cfg
{
"items":
[
    {
      "type": "stone", //type和items文件夹下的子文件夹相对应
      //"model": "<model.cfg>", //填playermodels文件夹下的路径,默认路径就是我们刚刚用的路径。
      //texture的默认路径看下面的文档,不建议用默认路径,因为默认路径的材质要命名为玩家名。
          "texture": "items/stone/stone.png",
      "active": true //一般填true就行了,填false就是不显示
    }
]
}
进入游戏看看效果

https://i.loli.net/2020/07/28/M7I9aSogNFH16xp.png

要石的位置还不太理想,我们调整一下model.cfg,改一下偏移值:
"translate":
好的,达到想要的效果了,平时使用模型时,可以把jvm参数`-Dplayer.models.reload=true`删除了。

https://i.loli.net/2020/07/28/kpZJCPIu2X6q8Dy.png

我们再灵活地使用一下模型的继承,增加几个要石:

https://i.loli.net/2020/07/28/xDT6JK4nPtr219V.png

效果:

https://i.loli.net/2020/07/28/abJ7IezAT1DW29d.png

注意:隐身药水对自定义的模型无效。

附:非官方说明文档
######################################################################
# Add -Dplayer.models.local=true -Dplayer.models.reload=true to your JVM arguments
# https://github.com/sp614x/optifine/blob/master/OptiFineDoc/doc/system_properties.txt
# https://github.com/sp614x/optifine/blob/master/OptiFineDoc/doc/cem_model.txt
# https://github.com/sp614x/optifine/blob/master/OptiFineDoc/doc/cem_part.txt
######################################################################
# Custom Player Model Add-on
# User Config <user>.cfg
# Located in ".minecraft/playermodels/users"
#
# Textures can be specified as:
#         "texture" - relative to folder ".minecraft/playermodels/"
#         "folder/texture" - relative to folder ".minecraft/playermodels/"
# Models can be specified as:
#         "model" - relative to folder ".minecraft/playermodels/"
#         "folder/model" - relative to folder ".minecraft/playermodels/"
#         
# If nothing is set for the texture it'll use ".minecraft/playermodels/items/<name set for type>/users/<user.png>"
# If nothing is set for the model it'll use ".minecraft/playermodels/items/<name set for type>/model.cfg"
######################################################################
{
"items":
[
    {
      "type": "<name>",                                                - Name of folder in ".minecraft/playermodels/items/"
          "model": "<model.cfg>",                              - Part model file, from which to load the part model definition
      "texture": "<texture.png>",                        - Texture used by the model
      "active": "<true|false>"                              - True: active for user, False: not active for user
    },
      ...
]
}
######################################################################
# Custom Player Model Add-on
# User Part Model <model>.cfg
# Located in ".minecraft/playermodels/items/<name set for type>"
######################################################################
{
"type" : "PlayerItem",
"usePlayerTexture": <true|false>,                                                                              - Uses player texture if set to true
"textureSize": [<width>, <height>],                                 - Texture size in pixels
"models":                                                             - List of models
[
    {
      "baseId": <parent_id>,                                          - Model parent ID, all parent properties are inherited
      "id": <id>,                                                       - Model ID, can be used to reference the model as parent
      "type": "ModelBox",
      "attachTo": "<head|body|leftArm|rightArm|leftLeg|rightLeg>",      - Player part to which the part model is attached
          "invertAxis": <axis_to_invert>,                                 - Axis to invert, for example "xyz" inverts all axes
      "translate": [<x>, <y>, <z>],                                     - Translate (pixels)
      "rotate": [<angle_x>, <angle_y>, <angle_z>],                      - Rotate (degrees)
      "mirrorTexture": <axis_to_mirror>,                              - Texture axis to mirror, for example "uv" mirrors both U and V axis
      "scale": <scale>,                                                 - Render scale, default is 1.0
      # Part Model definition, see "cem_part.txt" for details         - Part model definition (inline)
      ...
    },
    ...
]
}





动态材质(后篇)animation




前置知识

​      动态材质(前篇)
Minecraft原版只可以为物品和方块添加动态材质,OptiFine拓展了动态材质的功能,使动态材质可以用于几乎一切材质。

本章将展示若干实例,展示动态材质在方块和物品之外的各种应用。

章节目录


[*]猫猫——生存模式物品栏

其他案例


[*]Conquest_资源包
[*]the Mojangs loading screen 资源包





猫猫——生存模式物品栏inventory




这次我们要实现如下效果:

https://www.bilibili.com/video/BV1WK411H7o4

和一般的动态材质没多少区别,首先,都要逐帧制作动画,拼接成一个长条胶卷:

https://i.loli.net/2020/07/28/PQT1yerzMc3VwdD.png

然后在optifine文件夹下新建一个名为anim的文件夹,将做好的材质放进去。

注意:1.13之前anim文件夹应该放在mcpatcher文件夹下。

新建一个properties文件,为了方便辨识,我将其取名为inventory.properties:

inventory.properties
# form是动态材质的文件路径
from=./inventory_anim.png
# to是将动态材质用于哪张图片,我将这张图片称为目标材质。
# form和to连起来就是,将inventory_anim.png作为inventory.png的动态材质。
# 当游戏显示inventory.png时,其部分区域会变成inventory_anim.png的动态材质。
# 注意,只是部分区域,和普通的动态材质不同,optifine的动态材质不是将整张图片动态化,而是将某个区域动态化。
# 一张很大的图片,也可以动态化它的一个小区域。
to=textures/gui/container/inventory.png
# 动态化区域是目标材质的一个长方形区域,它的左上角称为动态化区域的原点,x和y是原点在目标材质中的坐标。
x=0
y=0
# w和h是动态化区域的长和宽。这个尺寸要和动态材质的一帧的尺寸吻合。
w=256
h=256
# duration是每帧的持续时长,相当于原版动态材质的frametime。
duration=2
如果对图片的坐标不太理解,我简单解释一下,png图片最左上方的点坐标为(0,0),如果图片的尺寸是16×16,那最右下方的点的坐标为(15,15)。

https://i.loli.net/2020/07/28/cIzAo35ufqJBKUE.png

注意:一件非常怪异的事是,似乎必须把目标材质放在材质包内,optifine才会识别出目标材质。
https://i.loli.net/2020/07/28/9BJRtfaOF7kH8GM.png
上图说的“长啥样并不影响结果”并不准确,它的分辨率必须得和属性文件里的保持一致。
我猜测这样做是为了防止单打独斗的动态材质覆盖到分辨率和画风完全不同的材质包上而出现非常离谱的画面,于是强制材质包作者放上一张材质。

进入游戏看看效果,效果是很好的:

https://i.loli.net/2020/07/28/zUNB1FHRPbL79yI.png

既然这张材质的动态化区域实际上只有右上方的流水和右下角的猫猫,那么我们就可以压缩一下动态材质占用的空间。用某一帧覆盖掉原先的GUI,然后用两个属性文件分别将两段小的动态材质贴到GUI上。

在aseprite里,分割动态材质的某个区域的方式是调整画布大小,将画布外的给剔除掉,得到的就是裁切后的动态材质了,然后导出动画的每一帧,拼接成一张小的长条形图片。

https://i.loli.net/2020/07/28/P6rjNexSiwnOuCB.png

由于比较枯燥,我就不详细演示了。

还有一个测试出来的情报就是,动态材质不是覆盖在动态区域上,而是直接将动态区域给替换了:

https://i.loli.net/2020/07/28/vYw2PXFi5lSUMfx.png






GUIgui




//TODO




主菜单全景图panorama




主菜单全景图就是主菜单的背景,其材质位于 assets/minecraft/textures/gui/title/background 文件夹下,共六张。

把这六张图片拼接一下,不难看出这是一个盒子的展开图。想象将其折叠成一个盒子,将玩家视角置于盒子正中央,就好似玩家声临其境一样。这就是游戏中常用的 CubeMapping 技术。

https://i.loli.net/2020/07/28/TZB52HWRr8K6kiu.png

如何制作主菜单全景图

常规方式:截图

最简单的方法就是在游戏里看向六个方向,分别截一张图:

1. 首先要在启动器中将游戏窗口大小改为 512 x 512。
2. 拍摄前要阻止各种可能的变化,防止不同截图衔接不上,最好将世界变成彻底静态的。


[*]/gamerule doDaylightCycle false 停止昼夜交替
[*]关闭飘来飘去的云
[*]如果开的光影中有各种晃动元素,也要关掉

3. 将角视场调整为82
4. 按下 F1(有些笔记本需要 Fn + F1)隐藏一切 UI
5. 选择合适的位置,选择合适的时间,调整光影的各种设置以达到最符合氛围的效果


[*]`/tp @p ~ ~ ~ -90 0`,按下 F2(有些笔记本需要 Fn + F2)截图得到 `panorama_0.png`
[*]`/tp @p ~ ~ ~ 0 0`,截图得到 `panorama_1.png`
[*]`/tp @p ~ ~ ~ 180 0`,截图得到 `panorama_2.png`
[*]`/tp @p ~ ~ ~ 90 0`,截图得到 `panorama_3.png`
[*]`/tp @p ~ ~ ~ -90 -90`,截图得到 `panorama_4.png`
[*]`/tp @p ~ ~ ~ -90 90`,截图得到 `panorama_5.png`

6. 将截图放到材质包的 assets/minecraft/textures/gui/title/background 文件夹下,测试,如果感觉图片之间衔接不好,可以试着调整一下光影设置,很多光影会模糊加深屏幕边缘,要将这类设置关掉。

extra:使用mod

1.9.x 到 1.12.x 版本可以使用 Quark 模组中的全景图生成器功能(Ctrl+Shift+截屏键),但实际上这个功能只相当于瞬间执行上述方法的第五步。

如果使用1.14.4-1.15.x版本,可以使用 Runorama——全景截图背景 mod。

extra2:画

Conquest_ 材质包的主菜单全景图是画出来的。

https://i.loli.net/2020/07/28/GysUjbtpQAx29Ni.png

进阶:使用软件

全景图本质上是一个天空盒(skybox),它自然可以用3D渲染软件渲染出来,这部分需要一定专业知识,在此不多赘述。

使用 OptiFine 制作随机的主菜单全景图

详见 OptiFine 帮助文档的自定义全景图章节






自定义加载界面loading




我们可以通过自定义加载画面修改Minecraft加载世界时显示的背景图片。

随便截一张图,放到optifine/gui/loading文件夹下,并命名为background0.png,这表示主世界的加载画面,如果是下界,命名为background-1.png,如果是末地,命名为background1.png,模组添加的维度也可以用这种background+id.png的方式命名。

https://i.loli.net/2020/07/28/aM3KTs6dDILZ8xA.png

重载资源包,通过传送门从下界传送回主世界:

https://i.loli.net/2020/07/28/WXpmgicv4sEGb98.png

吐槽一下,1.15.2除了最初一次加载,加载速度都快到来不及截图。

显然我们不想要这种效果,所以要通过属性文件进行调整。

在loading文件夹下新建文件,命名为loading.properties

loading.properties
# 缩放模式,默认的缩放模式为fixed,full表示全屏,但是会造成图片拉伸
scaleMode=full
https://i.loli.net/2020/07/28/o6fRICiSw3HpNVW.png

修改一下缩放模式:
# stretch也表示全屏,但不会造成拉伸,通常情况下选这个就好了。
scaleMode=stretch
https://i.loli.net/2020/07/28/dmqWFLQHygtPYOn.png

提示:本节的属性文件的详细内容见OptiFine帮助文档






环境光遮蔽ambientocclusion




以下内容为这篇Blog的译文节选,完整译文发布在mcbbs。

环境光遮蔽(Ambient Occlusion)是一种基于附近的方块计算表面的光照的方法。这儿有Minecraft如何实现这个方法的简介。简而言之,对于每个顶点,计算出三个毗邻的方块和接触这个面的方块的平均光照强度。在下方的例子中,对于这个用红色圈起来的顶点,是以白色显示的这三个方块和这个面正上方的方块(未显示)的平均亮度。如果这四个方块都是透明的,”环境光遮蔽“光照强度将会是1.0。每个不透明方块都会降低顶点接收到的光照 - 如果四个方块都是不透明的那么这个强度将会是0.2。”环境光遮蔽“值会与顶点的颜色乘数相乘。顶点的混合亮度被计算为四个相邻方块的平均值。(还有一些其他有关对角方块是否被计算在内的细节,但是你已经知道了基本的思路)在绘制面时,OpenGL提供了平滑,所以环境光遮蔽在不同顶点上的产生的阴影在整个平面上被平滑了。
https://i.loli.net/2020/07/29/E5w6cS4VGk8uiUv.png
Minecraft的方块模型有一个"ambientocclusion"选项,默认为true。

反常识的一点是,如果设置了"parent",则其父模型的ambientocclusion标签会覆盖该模型的ambientocclusion标签。代码实现如下。

net.minecraft.client.renderer.model.BlockModel(1.15.2)
public boolean isAmbientOcclusion() {
    return this.parent != null ? this.parent.isAmbientOcclusion() : this.ambientOcclusion;
}
提示:通常情况下,一个非完整方块,我们要考虑一下是否将环境光遮蔽设置为false,例如原版的例子template_torch.json,直立的火把。上一节涉及的shade在这里也得到了体现。
Blockbench中设置环境光遮蔽的方法:文件——项目——环境光遮蔽。

template_torch.json
{
    "ambientocclusion": false,
    "textures": {
      "particle": "#torch"
    },
    "elements": [
      {   
            "from": [ 7, 0, 7 ],
            "to": [ 9, 10, 9 ],
            "shade": false,
            "faces": {
                "down": { "uv": [ 7, 13, 9, 15 ], "texture": "#torch" },
                "up":   { "uv": [ 7,6, 9,8 ], "texture": "#torch" }
            }
      },
      {   
            "from": [ 7, 0, 0 ],
            "to": [ 9, 16, 16 ],
            "shade": false,
            "faces": {
                "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#torch" },
                "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#torch" }
            }
      },
      {   
            "from": [ 0, 0, 7 ],
            "to": [ 16, 16, 9 ],
            "shade": false,
            "faces": {
                "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#torch" },
                "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#torch" }
            }
      }
    ]
}


GeForceLegend 发表于 2020-7-29 11:16:15

本帖最后由 GeForceLegend 于 2020-7-29 12:07 编辑

如果一个面的cullface为south,当这个方块的南方为一个完整不透明方块时,这个面就会被剔除。
其实Minecraft的cullface系统小改过很多次,早就远不止对完整不透明方块生效了,大部分情况下不可见的面(比如两个下半砖之间,或者是半砖旁边挨了个活板门之类的)都会被剔除,当然也有时候会有bug,比如1.14.4里面铁栏杆会在旁边有半砖的时候产生面剔除(现在还是不是这样就不知道了),而在1.14之前斜铁轨不会触发顶部的cullface

另外,对于设置了"ambientocclusion"=false的方块模型,cullface控制了这个表面亮度的渲染,这个表面的亮度会等同于这个方块的cullface指向面的亮度(但是大部分时候能影响亮度话基本上都是直接剔除了,不过对没有设置cullface(默认取用面朝向的亮度)的效果很明显,比如给玻璃关闭环境光遮蔽之后弄个双边渲染,不给里面那边加cullface,左边放个方块右边朝向里面的那面就会黑,加了cullface之后就正常了)

不过ambientocclusion为true的模型的亮度渲染机制似乎被替换成了默认的环境光遮蔽机制,cullface不会影响到亮度的渲染




我怕再发下去给发卡了,剩下的都发这了

着色序数章节里面的案例(药水)里面有一个地方写成了皮革盔甲

另外下面的return tintindex > 0 ? -1 : PotionUtils.getColor(...);是tintindex大于1的话返回-1,这也和上面的"layer1": "item/potion"对应,tintindex=0(也就是"layer0": "item/potion_overlay")时才是获取药水颜色

最后就是简化CTM模型里面的玻璃板应该可以通过修改方块模型实现,因为CTM只是改变了材质本身而不会修改模型对材质的引用区域,由于原版MC的玻璃板模型的一边是x轴镜像的,所以会产生有一边反过来的问题(但是Optifine自带的CTM为什么没问题我就不知道了)
原版MC的两个玻璃板部件:
{
    "ambientocclusion": false,
    "textures": {
      "particle": "#pane"
    },
    "elements": [
      {   "from": [ 7, 0, 0 ],
            "to": [ 9, 16, 7 ],
            "faces": {
                "down":{ "uv": , "texture": "#edge" },
                "up":    { "uv": , "texture": "#edge" },
                "north": { "uv": , "texture": "#edge", "cullface": "north" },
                "west":{ "uv": [ 16, 0,9, 16 ], "texture": "#pane" },
                "east":{ "uv": , "texture": "#pane" }
            }
      }
    ]
}
{
    "ambientocclusion": false,
    "textures": {
      "particle": "#pane"
    },
    "elements": [
      {   "from": [ 7, 0, 9 ],
            "to": [ 9, 16, 16 ],
            "faces": {
                "down":{ "uv": , "texture": "#edge" },
                "up":    { "uv": , "texture": "#edge" },
                "south": { "uv": , "texture": "#edge", "cullface": "south" },
                "west":{ "uv": , "texture": "#pane" },
                "east":{ "uv": , "texture": "#pane" }
            }
      }
    ]
}
把第一个里面"west":{ "uv": [ 16, 0,9, 16 ], "texture": "#pane" }的uv改成,第二个里面"west":{ "uv": , "texture": "#pane" }的uv改成应该可以解决问题

似乎编辑回复不会有通知,艾特一下试试@SQwatermark


叶洛莉兰 发表于 2020-7-15 22:32:32

是大佬   

沉默的秋水 发表于 2020-7-16 12:24:04

很详细,真的很详细,谢谢大佬

布偶熊熊 发表于 2020-7-18 15:13:22

谢谢分享,辛苦了 !!

liuminghao 发表于 2020-7-28 21:31:23

vd

sdgsdgg

Basin. 发表于 2020-7-29 09:36:35

谢谢楼主的教程

江赛 发表于 2020-7-29 10:02:13

🐂 sbbcm 彩精更你有

GeForceLegend 发表于 2020-7-29 10:54:22

而类似于world文档、pdf之类的文件都不是纯文本
事word罢(

Origin_s 发表于 2020-7-29 11:03:53

感谢大佬,收藏了。希望能一直更新。讲讲视差贴图和反光贴图的做法,我也想制作我的材质了。

SQwatermark 发表于 2020-7-29 12:51:10

origin-s 发表于 2020-7-29 11:03
感谢大佬,收藏了。希望能一直更新。讲讲视差贴图和反光贴图的做法,我也想制作我的材质了。 ...

可以看看:https://www.bilibili.com/read/cv847631
我对视差贴图的了解也仅限于此了

浆果沙拉 发表于 2020-7-29 14:33:35

本帖最后由 浆果沙拉 于 2020-7-30 12:59 编辑

正好需要,感谢大佬们!
提醒~基础那篇资源包图标部分有个错别字 “图的变长”→“边长”

许灬愿 发表于 2020-7-29 14:52:27

哇塞,对我现在很有用
恭喜楼主大佬~

Origin_s 发表于 2020-7-30 08:44:01

SQwatermark 发表于 2020-7-29 12:51
可以看看:https://www.bilibili.com/read/cv847631
我对视差贴图的了解也仅限于此了 ...

嗯,谢谢大佬。

墨染若流云 发表于 2020-7-30 09:15:31

学到了学到了,谢谢大佬
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 【森罗万象】一个Minecraft资源包制作指南