跳至正文

物块制作第一节:单物块与平台

大家好,欢迎来到物块制作教程系列。我是瓶中微光,或者可以叫我珊瑚。

话不多说,让我们开始吧!


创建你的物块

就像新建物品,弹幕那样,新建一个物块类,并继承自ModTile。如果你有学习过与物品和弹幕相关的教程的话应该对于这个操作非常熟悉了。但如果你没有学习过任何的物品教程,我推荐你先去看物品教程,因为在这篇教程中同样会用到相关内容,并且我不会着重介绍它们。

using Terraria.ModLoader;

namespace MyMod
{
    public class MyTile : ModTile
    {
        public override void SetStaticDefaults()
        {
            //在这里写物块相关设置
        }
    }
}

需要注意的是物块只有SetStaticDefaults,并没有SetDefaults

基础单物块

我这里使用的贴图为原版中的呆萌紫气球,虽说它并非标准的物块贴图,但是在这个教程中使用不会有什么问题。标准的物块贴图可以参考原版的云的贴图。(克劳德:没错,正是在下)

实际上,就算不设置任何属性,这个物块也已经能正常的被摆放出来了,只需要随便找一个物品,然后在它的SetDefaults 中写上下面这一行,进入游戏,你就会发现它可以摆放出来了。

请注意你的物品需要是一个“可以使用”的物品,至于什么是可以使用的物品在这里不多讲,具体请去物品教程中学习。

尖括号内请填上你自己的物块类名

Item.createTile = ModContent.TileType<MyTile>();
游戏内的样子

设置属性

但仅仅是这样还完全不够,接下来介绍一些较为常用的物块属性,以下内容请写在SetStaticDefaults

//帧重要,如果你的物块并非普通的单物块请务必设置为true!!!
Main.tileFrameImportant[Type] = false;
//是否为实心物块
Main.tileSolid[Type] = true;
//为true时就不能在它旁边放上物块,例如插火把之类的就不行了
Main.tileNoAttach[Type] = false;
//顶部是否为实心,就和平台一样,玩家按下“下”时就可以从上面下来
//请注意这个属性会覆盖掉tileSolid
Main.tileSolidTop[Type] = false;
//是否会被近战攻击,弹幕等所破坏
Main.tileCut[Type] = false;
//是否是桌子,为true的话就可以在上面放上玻璃瓶之类的东西
Main.tileTable[Type] = false;
//是否会和泥土“融合”,请注意这将改变贴图的读取方式
Main.tileMergeDirt[Type] = false;
//是否会被水冲掉
Main.tileWaterDeath[Type] = true;
//是否会被岩浆烫掉
Main.tileLavaDeath[Type] = true;
//是否阻挡光
Main.tileBlockLight[Type] = true;
//是否能发出闪光粒子,就像矿物一样,另外光照如果太低的话将无法发出粒子
Main.tileShine2[Type] = true;
//发出闪亮粒子的“频率”,这个数字越大则“频率”越低
Main.tileShine[Type] = 1000;

设置完以上属性之后,看看在游戏内的效果吧!

可以看到效果还是可以的

Main 中还有一大堆的物块设置,但是他们大多用到的比较少,这部分我放在文章末尾来介绍。


消耗与回收

现在我们有了一个可以放置的物块,以及一个能放置这个物块的物品,是不是感觉少了点什么?

你可能已经注意到了,这个物块被破坏的时候不会掉落任何物品,同时放置它时也不会消耗。这并不是我们想要的效果,除非你想做一个能无限放置物块的魔杖。

让我们来修改一下物块和物品设置吧,在物块中加入以下部分,这些都是ModTile 中自带的属性。

//掉落物品的type,尖括号内请填上你的物品的类名
//如果要掉落原版物品,请使用ItemID.XXX
ItemDrop = ModContent.ItemType<MyItem>();
//物块被挖掘时的声音
HitSound = SoundID.Dig;
//挖掘时产生的粒子
DustType = DustID.Dirt;
//物块被挖掘时受到“伤害”的系数,越大则越难以破坏
MineResist = 1f;
//能被挖掘需要的最小镐力,默认0
MinPick = 20;

同时在物品内加入以下部分

//表示该物品为消耗品
Item.consumable = true;
//设置最大堆叠数为999
Item.maxStack = 999;
完美!

平台

教程中平台使用的贴图为气球平台,原版的平台基本都放在同一个贴图里,所以我单独把它裁剪了出来。

就像创建物块文件一样,创建一个平台类吧!以下是这个平台的全部属性设置,同样的属性将不再赘述。

using Terraria;
using Terraria.DataStructures;
using Terraria.ID;
using Terraria.ModLoader;
using Terraria.ObjectData;

namespace MyMod
{
    public class MyPlatform : ModTile
    {
        public override void SetStaticDefaults()
        {
            Main.tileFrameImportant[Type] = true;
            Main.tileLighted[Type] = false;
            Main.tileSolidTop[Type] = true;
            Main.tileSolid[Type] = true;
            Main.tileNoAttach[Type] = true;
            Main.tileTable[Type] = true;
            Main.tileLavaDeath[Type] = true;

            TileID.Sets.Platforms[Type] = true;
            TileID.Sets.DisableSmartCursor[Type] = true;
            AddToArray(ref TileID.Sets.RoomNeeds.CountsAsDoor);

            TileObjectData.newTile.Width = 1;
            TileObjectData.newTile.Height = 1;
            TileObjectData.newTile.CoordinateHeights = new[] { 16 };
            TileObjectData.newTile.CoordinateWidth = 16;
            TileObjectData.newTile.CoordinatePadding = 2;
            TileObjectData.newTile.StyleMultiplier = 27;
            TileObjectData.newTile.StyleWrapLimit = 27;
            TileObjectData.newTile.StyleHorizontal = true;
            TileObjectData.newTile.UsesCustomCanPlace = false;
            TileObjectData.newTile.LavaDeath = true;
            TileObjectData.addTile(Type);

            AdjTiles = new int[] { TileID.Platforms };
            ItemDrop = ModContent.ItemType<MyItem>();
            HitSound = SoundID.Dig;
            DustType = DustID.Dirt;
            MineResist = 1f;
            MinPick = 0;

        }
    }
}

等等,怎么和说好的不一样啊,多了一大堆不认识的东西啊!

别急,让我们来逐条解析一下。


TileID.Sets

除了使用Main.XXX 来设置物块属性之外,TileID.Sets.XXX 同样可以用于设置这个物块的属性,并且这里面的属性超级超级多,但很多都可能是在制作模组时完全用不到的属性。

//是否是一个平台,你同样可以使用这个属性来检测某物块是不是个平台
TileID.Sets.Platforms[Type] = true;
//当光标悬停在此物块上时,是否禁用智能光标功能
TileID.Sets.DisableSmartCursor[Type] = true;

TileID.Set 中的其他属性都是用来干什么的等到后续教程中用到了再说吧。

AddToArray

当TML在加载模组的时候,会自动为各种静态数组扩容以容纳模组物块,因为这个操作,所以我们才得以直接使用Main.XXX[Type] = true; 这样的操作去给物块设置属性。但凡事总有例外,有些数组由于它们用法上的区别TML并不会为它自动扩容,如果你也直接去设置它的话分分钟就给你报错说数组越界。

这时就需要使用TML在ModTile 中为我们准备的AddToArray 方法了,使用方法如下

//这将在房屋检测时将该物块记为一个“门”
AddToArray(ref TileID.Sets.RoomNeeds.CountsAsDoor);

平台居然都算作“门”,泰拉瑞亚,很神奇吧!同时这也是那种监狱式房屋能被房屋检测记为合适的房间的原因。除此之外TileID.Sets.RoomNeeds 中还有3个数组,分别如下所示

//记为椅子
AddToArray(ref TileID.Sets.RoomNeeds.CountsAsChair);
//记为桌子
AddToArray(ref TileID.Sets.RoomNeeds.CountsAsTable);
//记为火把
AddToArray(ref TileID.Sets.RoomNeeds.CountsAsTorch);

TileObjectData

TileObjectData 是物块内容中一个极为重要的部分,你可以使用它来更加自定义化你的物块,但鉴于这是一个基础教程,所以我只讲一些基本的内容,更多有趣的内容将在后续教程中介绍。

首先,TileObjectData 的使用基本可以分为3部分,分别是newTilenewSubTilenewAlternate

绝大多数时候我们只需要使用newTilenewSubTile 仅在你想把多个物块贴图塞在一张图中并且想让这些物块拥有不同的属性时才会使用到。至于最后的newAlternate,只有在你想让一个物块能有多种摆放方式时才会用到它。举个例子,我想让一个告示牌既可以插在地上,又能吊在天花板上,就可以使用newAlternate 去设置对应的摆放状态。

扯远了,还是详细说一下newTile 中可以设置的各种属性吧。

物块大小

//物块宽多少格
TileObjectData.newTile.Width = 1;
//物块高多少格
TileObjectData.newTile.Height = 1;
//物块每一格的高度,请保证它和你的贴图相匹配
TileObjectData.newTile.CoordinateHeights = new[] { 16 };
//物块每一格的宽度
TileObjectData.newTile.CoordinateWidth = 16;
//贴图读取时的间隔
TileObjectData.newTile.CoordinatePadding = 2;

上面的这些设置会改变贴图读取的方式,具体方式如下图所示

这只是一个示例,你也可以试着跟着设置一下看看效果。但是请保证你的贴图大小和你的设置相匹配,否则将导致绘制时出现BUG,甚至会使游戏直接崩溃。

应该看得懂吧

使用上图中的设置,在游戏内的效果。

哦!看上去很奇怪欸!这是因为泰拉瑞亚的每一格物块都是16 x 16大小的,这个绿色爱心的上面那一格的高度被设置为了6,而物块绘制时默认以左上角为基准,所以中间的地方就直接空了,来稍稍修改一下。

现在就正常了,另外中间的粉红色条条时我自己加的,留空也行,建议在整张图片的右边和下边都留出2格像素以防止出现奇奇怪怪的错误,留出的像素多少取决于你CoordinatePadding 所设置的值。

物块Style

//比较玄幻的属性,一般就和下面的那个保持一致就可以
//只有当你的贴图中包含多个物块的贴图时才有作用
TileObjectData.newTile.StyleMultiplier = 27;
//每行或列有多少张贴图,平台的贴图一般就27钟
TileObjectData.newTile.StyleWrapLimit = 27;
//贴图是否为横向排列的,为true时将横向读取贴图
//如何设置这个属性请问问你的贴图是什么样的
TileObjectData.newTile.StyleHorizontal = true;

什么是Style呢,直译的话可以理解为摆放样式,例如平台在隔空放置和在其他物块边上时的样式就是不同的。一般来说,不需要我们去手动改变物块样式,只要你正确地设置了物块的属性。上面的两个属性为27是因为平台一共有27种不同的摆放样式,你可以数一数上面的气球平台贴图,正好就是27种。

不同Style的平台

其他设置

TileObjectData.newTile.UsesCustomCanPlace = false; 一般来讲,这个属性都应当设置为true。但为什么我这里设置为了false 呢?是因为在测试过程中发现如果为true 的话这个平台就可以隔空摆放…这大概是因为没有设置与摆放相关的属性造成的,至于与摆放相关的AnchorXXX 系列属性将在之后的教程中挨个介绍。

TileObjectData.newTile.LavaDeath = true; 和在Main 中一样,这里也有一个判断物块是否会被岩浆破坏的属性,但这里的主要用于子物块的设置。举个例子,大多数平台共用一个物块ID,即19,但在这一堆平台中仅有黑曜石平台和石平台是防岩浆的,就是因为使用newSubTile 额外设置了该属性。

TileObjectData.addTile(Type); 这一行极为重要!极为重要!极为重要!它的作用是将上面你对newTile 所做的所有修改“注册”上去。请确保修改设置后加上这一行,同时在这一行之后就不要再对newTile 进行任何的修改了。

AdjTiles = new int[] { TileID.Platforms }; 这是一个由ModTile 提供的属性,它的作用是将该物块标记为你所指定的制作站,只需要在后面的大括号内添加物块ID就可以了,中间用逗号隔开。我这里的意思是该物块会在搜寻制作站是做为平台。

试着修改它让你的物块能被当作一个铁砧。(提示:铁砧的物块ID叫做Anvils

测试效果

呼呼,终于讲完这一堆的设置了,进入游戏内看看效果吧!

别忘了去做一个物品来放置此物块!我相信这对你来说不难。

真不错啊

Main中的其他设置

鉴于内容比较多,所以不重要的属性就暂时不介绍了。未注明的均为bool 类型,且默认false

//使用示例
Main.tileLighted[Type] = true;

Main.tileLighted 是否会发光,为true的话就可以使用以下的重写方法来自定义发出的光。

public override void ModifyLight(int i, int j, ref float r, ref float g, ref float b)
{
    //在这里添加自定义内容
}

Main.tileAlch 是否为草药,指的是太阳花,闪耀根等。记住Alch 代表草药这个点,以后的教程中还会用到。

Main.tileStonetrue 表示该物块能和石头融合,或者说材质就是石头。各种宝石矿物用到了它。

Main.tileAxe 是否能用斧头挖掘。

Main.tileHammer 是否能用锤子挖掘,原版仅有恶魔祭坛和暗影珠/猩红之心用到了这个。


Main.tileNoSunLight 是否会阻挡太阳光。类似玻璃,云等都设置为false 。如果tileSoildtrue,将自动把这个设置为true

Main.tileObsidianKill 是否会在生成黑曜石时被挤掉。如果tileLavaDeathtrue,将自动把这个设置为true

以上两个属性如果要更加进一步自定义的话,比方说tileSoildtruetileNoSunLightfalse,可以使用ModTile 中的这个重写方法进行设置。

public override void PostSetDefaults()
{
    //在这里进一步自定义上面两个属性
}

Main.tileDungeon 是否为地牢砖,会影响一些NPC的生成还有物块被挖掘时受到的“伤害”,这也是地牢砖比较难挖掉的原因。

Main.tileSpelunker 是否能被洞穴探测药水等探测到。要想更加自定义这个属性可以使用下面这个重写方法。

public override bool IsTileSpelunkable(int i, int j)
{
    //我这里写的是只有在白天才会被探测到
    if (Main.dayTime)
        return true;

    return false;
}

Main.tileBouncy 物块是否Q弹。为true时玩家掉落到上面之后就能弹起来。

int Main.tileOreFinderPriority 默认0。是被金属探测器所检测到的优先度,具体该设置为多少请去查阅泰拉Wiki的金属探测器界面:金属探测器 – 官方中文 Terraria Wiki。这个数越大则越优先显示。

Main.tileRope 是否为绳子。为true的话玩家就可以爬在上面。

Main.tileNoFail 物块不会失败?其实是为true 时该物块就一定能被挖掉,无视镐力之类的,确实是不失败嗷。


不知不觉就写了一大堆,作业就不留了,各位可以尝试着设置一下各种属性看看效果。

我们下期见!

发表回复