跳至正文

第一个远程武器——手枪

你应该能在Items文件夹下发现TemplateGun.cs,它就是我们今天要讲的主题。


合成表

这把枪,好像并不能在游戏里面被合成出来。。。

因为这是我特意设置的,为了让你们知道如何创建合成表(笑)

我们先回顾一下上一章那把剑的合成表:

// 物品合成表的设置部分
public override void AddRecipes() {
    ModRecipe recipe = new ModRecipe(mod);
    // 合成材料,需要10个泥土块
    recipe.AddIngredient(ItemID.DirtBlock, 10);
    // 以及在工作台旁边
    recipe.AddTile(TileID.WorkBenches);
    // 生成1个这种物品
    recipe.SetResult(this);
    // 这样可以生成50个
    // recipe.SetResult(this, 50);

    // 把这个合成表装进tr的系统里
    recipe.AddRecipe();
}

ModRecipe就类似一个合成表管理器,这是TML自带的,可以对原版合成表进行操作。具体来说,常用的操作有

AddIngredient

这个函数的作用是向合成表添加原料,比如

// 合成材料,需要10个泥土块
recipe.AddIngredient(ItemID.DirtBlock, 10);
// 合成材料,还需要10个铁块
recipe.AddIngredient(ItemID.IronBar, 10);
// 合成材料,还需要100个金块
recipe.AddIngredient(ItemID.GoldBar, 100);

这个函数可以写多个,这样对于同一个ModRecipe,原料会叠加,上面这个代码在游戏中是这样的:

注意我这里的ItemID.XXX,这些其实就是物品的ID

如果你使用右键->查看定义

你就会发现,其实ItemID.IronBar,对应的就是一个数字,代表了铁锭的物品ID

知道了铁锭ID是22,那么我可以写 recipe.AddIngredient(22, 10);也可以写recipe.AddIngredient(ItemID.IronBar, 10);

后者的好处是意义很清晰,因为22这个数字可以代表的意思太多了,很可能你之后看这段代码就想不起来合成材料是什么了,但是如果你看到ItemID.IronBar你就能立刻知道这个东西是铁锭。 具体的物品ID可以在wiki找到: https://terraria-zh.gamepedia.com/%E7%89%A9%E5%93%81_ID

那么如果我想要添加Mod物品呢?首先我们必须明确一点,就是Mod物品不存在固定的ID。Mod物品的ID可能随着你加载的Mod数量而 变化所以我们只能动态获取。

TML当然考虑到了这一点,于是我们有了ModContent.ItemType()。这个函数的使用方法看上去可能有点奇怪,如果我们想把上一章的那把剑加入合成表,我们需要这么写:

// SkirtSword是教程那把剑的类名!!
recipe.AddIngredient(ModContent.ItemType<SkirtSword>(), 1);

注意:使用ModContent.XXXType<T>()这类函数通常会让T里面的东西被VS画上红线,此时要用小灯泡进行修复。因为T的值通常需要引用其他命名空间的类。

这么写是最推荐的写法,原因有点复杂,目前阶段不太好解释。如果你不喜欢这种写法,还可以这么写:

recipe.AddIngredient(mod, "SkirtSword", 1);

这两种都可以,但是后者只能获取当前Mod的物品,如果要获取其他Mod的物品后面会讲到。你最多可以加14个合成材料,这是泰拉瑞亚本体规定的。

总之,目前的方法我们已经可以添加Mod物品作为合成原料,效果如图

AddTile

与AddIngredient相似,只不过这个函数是添加合成环境的

当然,你也可以添加两个合成环境

// 在工作台和水晶球旁边才能合成
recipe.AddTile(TileID.WorkBenches);
recipe.AddTile(TileID.CrystalBall);

那么我们可以推测出TileID其实就是一个储存物块ID的类,剩下的就和AddIngredient没区别了。物块ID可以在wiki找到: https://terraria-zh.gamepedia.com/%E5%9B%BE%E6%A0%BC_ID

注:如果要在液体(水,岩浆,蜂蜜)中合成,因为液体并不具备实体物块ID,所以我们不能用TileID完成。但是我们可以使用:

// 水边合成
recipe.needWater = true;
// 蜂蜜合成
recipe.needHoney = true;
// 岩浆合成
recipe.needLava = true;

AddRecipeGroup

(来自某天使写的)

AddRecipeGroup函数的作用是,向合成表添加入一个合成组,那么什么是合成组?

合成组是一堆合成物品的集合,通过写一个合成组,可以削减一大堆的 AddIngredient

这样的好处,举个例子是可以说明的,你绝对不想给一部分是用火把合成的物品,去一个一个火把添加合成表吧(

首先我们找到mod主类,输入

这个东西的作用就是向mod添加合成组

接着里面New一个RecipeGroup

就这一堆东西是火把

是不是很长?别急,我们先一个一个分析

第一个参数是一个Func委托,直接打个括号,用=,后输入合成组在游戏的名字即可Language.GetTextValue(“LegacyMisc.37”)是根据游戏的语言改变的一个叫“任意”的名字(当然也可以直接打一个”任意火把”来代替)

第二个参数是一个int[]类型,具体就是让你输入一大堆物品的ID(mod物品也可以)

最后需要用 RecipeGroup.RegisterGroup("MOD:Torch", group)来加载这个合成表,这个东西第一个参数是名字,第二个参数是上面的合成组

最后给出一个 金和铁 做合成组的代码

public override void AddRecipeGroups()
{
    base.AddRecipeGroups();//这里是偷懒留下来的产物
    RecipeGroup recipeGroup = new RecipeGroup(() => "金或铁", //游戏显示合成组的名字
                new int[]
                {
                ItemID.GoldOre,//金块
                ItemID.IronOre//铁块
                });//添加ID
    RecipeGroup.RegisterGroup("GlodAndIron", recipeGroup);//向游戏添加合成组
}

但是我们有了合成组,我们还没有用呢

我们就要用AddRecipeGroup

recipe.AddRecipeGroup("MOD:Torch",10); 第一个参数是合成组的名字(不是游戏里面的名字),第二个是所需数量

如果想用原版的合成组,可以这样

没错,它还有一个第一个参数是int类型的重载

SetResult

SetResult函数的作用就是确定合成表要合成的物品,当然,一个ModRecipe只能有一个合成出来的物品。

这个函数的第一个参数可以是一个ItemID,或者ModItem,而这段代码用的就是后者

// 生成1个这种物品
recipe.SetResult(this);
// 这样可以生成50个
// recipe.SetResult(this, 50);

这个this在这里指代的就是当前我们正在写的这个物品,当然你可以不合成这个物品,而是合成原版物品,这时候就需要:

// 合成原版铁剑
recipe.SetResult(ItemID.IronBroadsword);

使用ItemID了,ItemID真是一个非常常用的类呢,大家一定要学会如何使用它。

AddRecipe

没啥好说的,就是告诉ModRecipe,这个合成表的信息填完了,可以加入进去了。

如果想要给一个物品加多个合成表也很简单,只要新建一个名字不同的ModReceipe就好了:

// 第一个合成表
ModRecipe recipe1 = new ModRecipe(mod);
// ...
recipe1.AddRecipe();

// 第二个合成表
ModRecipe recipe2 = new ModRecipe(mod);
// ...
recipe2.AddRecipe();

修改远程武器属性

首先,你需要给这个物品一个合成表,这样你才能在游戏里观察它。(废话)

与刚才神秘之剑的代码相比,枪类武器做出了一点点改动。首先就是useStyle这个属性变成了5。由于这个改动,我们的贴图也不再是剑类贴图的45斜向上了,而是水平的。

同时item.ranged也被设为了true,代表这是个远程武器,受远程伤害,暴击加成。当然你也可以让item.meleetrue,对效果没有影响,就是……

你这是要拿手枪敲人吗

如果你真的要拿手枪敲人,那么一定要记得把item.noMelee设为false。这个属性的作用是物品的贴图造不造成伤害,如果true就是贴图不造成伤害,反之就是造成伤害。如果这是你想要的,那么就像这样

请问,什么杀死了它

如果需要改暴击属性可以看item.crit属性,一般来说,武器都有默认的4点暴击率,如果item.crit = 0,那么就会有默认的4%暴击率。注意,这个crit是个整数,而不是小数了。

接下来就是一个非常重要的属性了,shoot,也就是射出的弹幕

// 决定枪射出点什么和射出的速度的量
// 这里我让枪射出子弹,并且以 (7像素 / 帧) 的速度射出去
item.shoot = ProjectileID.Bullet;
item.shootSpeed = 7f;

我们又一次看到XXXID,这种格式了,这次是ProjectileID,也就是弹幕的ID。具体的ID表可以在wiki找到: https://terraria-zh.gamepedia.com/%E5%B0%84%E5%BC%B9_ID 。需要注意的是如果你设置了shoot,那么必须要设置一个非0的shootSpeed才会发射弹幕。shootSpeed的意思就是弹幕会以什么样的速度从枪口射出,具体多快可能需要你自己感受了,反正7不算很快。尝试修改一下它的属性,看看多快比较合适。

如果要指定一个Mod弹幕,可以使用ModContent.ProjectileType<弹幕的类名>(),这和Mod物品的ID获取方法是相似的。

最后一个属性稍微有点难理解,item.useAmmo = AmmoID.Bullet;让这把枪使用任何子弹作为弹幕,并且实际射出的弹幕类型由你背包里的子弹决定,也就是说,假设我背包里有高速子弹,那么这枪就消耗高速子弹射出高速子弹,如果是普通子弹,那么就会消耗普通子弹射出普通子弹。

设置了这个属性前面的item.shoot填什么已经不重要了,后面讲到弹药会解释shoot到底会变成什么。

那么同样,这又是一个ID,我们叫它弹药类型ID,但是这个ID在wiki上没有,所以就用VS的自动补全将就一下吧

练习

现在,对这把枪的属性进行一些修改,来验证自己是否真的理解了这篇教程,如果有不懂的欢迎提问哦。

  1. 把这把枪射速改为两秒17发,并且百分百暴击
    首先,计算一下两秒17发大概要多快的速度。
    两秒=\(120\)帧,所以每一发需要\(120/17 = 7.05\)帧,于是我们有
    item.useTime = 7;
    item.useAnimation = 7;
    

    接下来改暴击率,由于有4点基础暴击率,所以我们只需要把crit设为96就行了

  2. 让这把枪射出火箭
    你可以通过修改shoot属性或者useAmmo属性来达成
    // 方法1
    item.shoot = ProjectileID.RocketIV;
    // 方法2,背包里要有火箭
    item.useAmmo = AmmoID.Rocket;
  3. 把这把枪的合成方式改为,5个金锭,5个铁锭,25个火把,在铁砧和工作台和水旁边合成,一次可以合成99个
    参考代码
    ModRecipe recipe = new ModRecipe(mod);
    recipe.AddIngredient(ItemID.GoldBar, 5);
    recipe.AddIngredient(ItemID.IronBar, 5);
    recipe.AddIngredient(ItemID.Torch, 25);
    recipe.AddTile(TileID.WorkBenches);
    recipe.AddTile(TileID.Anvils);
    recipe.needWater = true;
    recipe.SetResult(this, 99);
    recipe.AddRecipe();
    
  4. 把上一章的模板剑改为射出泰拉之刃的弹幕
    // 注意添加合适的射出速度
    item.shoot = ProjectileID.TerraBeam;

进阶

  1. 探索一下item.shootSpeed,这个属性,观察一下它在速度比较大(超过16),和比较小的情况下会出现什么现象。试一下不同的弹幕,看看有哪些弹幕会被这个射速所影响?
  2. 如果想让魔法武器拥有蓝耗,应该设置什么属性?魔法武器能不能使用弹药?
  3. 如果想让物品在铁砧或者工作台合成,应该怎么做?如果想让物品支持铅块铁块合成,应该怎么做?
  4. 修改一下item.widthitem.height,看看它对武器的使用有什么影响。
  5. 如果想让item.useStyle = 5,同时贴图又要斜着,该怎么做?(想想法杖)
  6. 有了item.useTurn = true;以后最好就不要加弹幕了,要有弹幕的武器最好把item.useTurn这个属性设为false,为什么呢?

《第一个远程武器——手枪》有16个想法

发表回复