《泰拉瑞亚》饰品制作方法
游戏攻略【基础篇:饰品创建】
一个mod道具需要有json文件和png文件嘛~
png就不多说求生实录,不要找太大的就行了,反例请参见ctx的just a simple boss里的BOSS召唤物——骷髅下巴,因为直接用《游戏风暴》二,太大十八期《泰拉瑞亚》求生实录得不成样子
当然还有一点:背景色必须是透明,比如:
下面《json拉写泞;以求生实ctxmove作范例:
{
"code": "ctxmove",——名字,要游json暄攻略网一致
"displayName": "CTXs Movement",——显示名称,最好区分大小写以显得很正式
"size": [28,14],——尺寸,两个数字分别对应png图像的长和宽
"value": [0,0,0,0],——价值,四个数字分别对应铂、金、银、铜币的数量(注意这里是买入价值,卖出时会有折扣,使用打折卡买入也会有折扣)
"rare": 9,——稀有度,⑨就是最稀有的天蓝了好像
"toolTip": "I hate json.",——第一行道具说明(想加第二行就是toolTip2)
"accessory":true,——这句是重点,它决定了这个道具可以被塞进饰品栏
"recipes": [——合成表
{
"items": ["0 Wood"],——用0个木头
"creates": 1——合成1个饰品
}
]——这里的recipe明显不合格好吧!连tile也没有——嘛,这不是重点
}
此外,还可以加一些附加的语句,比如"wings":12,让玩家佩戴后获得蒸汽朋克翅膀,"lifeRegen":增加回血速度,"defense":加防御,好像也就这些了。
【进阶篇:cs创建】
写一个和png以及json相同名字的cs文件
首先,在里面写:
using System;
using TAPI;
using Microsoft.Xna.Framework.Graphics;
namespace TAPI.【MOD名称】{
public class 【道具名称】:ModItem{
public 【道具名称】(ModBase modbase,Item i):base(modbase,i){}
public override void Effects(Player p){
//等会就是在这里插入饰品信息滴~
}
}
}
那些【mod名称】和【道具名称】需要换成什么,就看实际情况和个人喜好了……顺便:【道具名称】也要和文件名一样
然后嘞……这个Effects究竟能干什么?答案:几乎所有!一个玩家能干的事!
看见后面括号里那个Player p了么?这代表接下来Effects里发生的一切事情,都将施予那个代号为p的玩家——也就是带上这个饰品的玩家~(当然那个p改成player,ctx,lzw神马的都行,只是切记要和后面保持一致)
至于Effects究竟能干啥嘛……先来几个基本的吧:
首先是对玩家身上的数据进行操作
写p.statLifeMax=500;就是玩家佩戴时生命最大值将瞬间飙到500
p.statManaMax=200;同理,玩家最大魔法值飙到200
p.statManaMax2=+200;这个就像一般的法师饰品一样暂时增加了玩家的最大魔法,卸掉饰品就会还原(而前两个不会)
p.wings = 12;给玩家戴上蒸汽朋克翅膀
p.statDefense+=233;玩家防御增加233
p.lifeRegen+=233;玩家回血速度增加233(再注:这里的回血速度最大也只能增加到每秒一点,是TR原版回血机制的问题)
当然,还有很多……
【娱乐篇:鬼畜cs】
接上篇,继续往Effects里加条条——
p.statDefense+=2333333333;神马的就不用我说了吧~改数字而已,大家自便,不要让自己失去游戏乐趣就可以了
首先是更加神奇的回血:
p.statLife++;
这条代码能让玩家每帧都回复一点血,相当于每秒60点,而且这个回血和玩家自动回血以及幽灵套、吸血飞刀、HP药水之类的完全无关,相当于是每秒免费送的60血~
同理,写p.statMana++;就是每帧回复1蓝,每秒额外60蓝
另外,如果想看到效果的话可以再加一句p.HealEffect(1);或者p.ManaEffect(1);,分别是回血和回蓝1的效果,当然括号里的数字随意填,填几就跳几的绿/蓝数字(最好和写的回血/回蓝数字相同,不然会有困扰)
——什么?还是太弱了?你手残所以每秒60血根本不够?
p.longInvince = true;
p.lavaImmune = true;
p.fireWalk = true;
p.noFallDmg = true;
四句分别是延长无敌时间(神圣十字效果),免疫岩浆(黑曜石药水效果),免疫热砖块(黑曜石头颅效果)以及免疫掉落伤害(马掌效果)
——什么?你还想站撸霜月?
记得我说过Effects可以对玩家数据任意操控吧~
我们可以简单地做一个内置锁血锁蓝的效果:
p.statLife=p.statLifeMax;和p.statMana=p.p.statManaMax2;
原理:每帧都读一下玩家当前最大血/蓝是多少,再赋给剩余的血/蓝
够鬼畜了吧~现在只要没有喵杀你的攻击,对你来说都是浮云了
——什么?你还要白天去同时站撸骷髅王、地牢守护者和机械骷髅王?
【丧病篇:鬼畜cs进阶】
这一篇教程就是一些更华丽的效果了……
首先是上期遗留的问题:如何站撸白天吴克?答案就是……诶有人敲门?我的快递到了,去收一下……
……刚才送快递的LZW小哥告诉我,不能把这个说出来,不然会(ta)极(hui)大(zai)地(jin)影(wan)响(lai)游(gang)戏(le)平(wo)衡,所以就只能给一点提示了:给玩家强行上无敌~
子提示①:无敌和免疫的意思很接近
子提示②:TR的变量基本是用英文命名的
子提示③:无敌也需要时间限定
那么,就这样吧,作为一个思考题,接下来是——
1.时装
想穿乌龟套又懒得收集龟甲?万能的Effects满足你!
p.head=99;
p.body=65;
p.legs=54;
想穿幽灵套又不想下地牢?万能的Effects满足你!
p.head=101;
p.body=66;
p.legs=55;
其它时装同理,不过实际使用时优先级是时装栏高于下面的饰品高于上面的饰品高于装备栏,请务必注意
2.套装效果
想反伤又嫌乌龟套好难看而且不想穿时装?还是万能的Effects满足你!
p.thorns = true;
p.turtleThorns = true;
想吸血而且觉得幽灵套太丑?依然是是万能的Effects满足你!
p.ghostHeal=true;
什么?嫌幽灵套吸血太慢了?那就再来一句:
p.lifeSteal=233f;——瞬间还原至旧版幽灵套的吸血频率我会说?
3.视觉特效
还是那句话,Effects是万能的。
想在身上点上火?
for (int i=0;i<9;i++){
int m = Dust.NewDust(p.position, p.width, p.height, 6);
Main.dust[m].noGravity = true;
Main.dust[m].position += p.velocity * Main.rand.Next(-50, 51) * 0.01f;
}
上面这段效果大概就是在身上放上火焰的粒子特效,并且会依据玩家的速度而延伸成一条火链;至于每一句都是起的什么作用——字面意思,不懂英文的请自行google翻译,不懂英文又秀优越秀爱国的请右上角
想让自己身上冒星星?
Gore.NewGore(p.position+new Vector2(Main.rand.Next(p.width),Main.rand.Next(p.height)), p.velocity,Main.rand.Next(16,18));
上面这句就是在玩家身上随机的地方冒16号或者17号gore,也就是星星
【DEBUG-查错阶段】
首先是同时挂上蒸汽朋克翅膀、火焰粒子以及星星gore的照片一张
这里需要注意一点:cs开头的using应该是using Microsoft.Xna.Framework;
也就是说,模板应该是如下:
using System;
using TAPI;
using Microsoft.Xna.Framework;
namespace TAPI.【MOD名称】{
public class 【道具名称】:ModItem{
public 【道具名称】(ModBase modbase,Item i):base(modbase,i){}
public override void Effects(Player p){
//等会就是在这里插入饰品信息滴~
}
}
}
这篇主要讲述的是运用TR原版的一些方法组来答到寓守于攻、不战而退敌的效果
首先……先介绍一些需要用到的代码
1.Rectangle(组词法:rect是“四”,angle是“角”,四个墙角即为矩形~)
要想快速而方便地判定范围,首先要在TR里表示一个矩形(Rectangle)。
TR里声明一个矩形的方法是使用new方法组,也就是new Rectangle(坐标X,坐标Y,宽,高)
举例:new Rectangle(100,200,300,400)表示的是如下图的矩形:
左上顶点坐标(100,200),长300宽400,右下顶点坐标(100+300,200+400),表示的是一个范围(嗯没错那个就是66ccff)
注:矩形的坐标和长宽必须是整数,在使用到玩家或npc坐标的地方请在前面加个(int),表示取整
2.Rectangle.Intersects(Rectangle r1,Rectangle r2)
intersects方法组,隶属于xna特有的数学系(重申一遍前面cs格式的.Graphics一定要删掉!),用来判定两个矩形是否相交,输出逻辑值(真或假)
p.s.判定是否在范围内时好像比直接计算距离略快一点,缺点就是判定是方的不好看
……嘛,战斗时谁还管好不好看呢?
3.foreach(类型 代号 in 组)
就像这个语句的名字一样,它可以把某个组里的所有东西都拉出来给一个代号,比如foreach(NPC n in Main.npc)就是检索游戏中所有NPC并给每个NPC起个名字:n;接下来这个n将要受到什么代码的操(rou)作(lin),就完全是个人喜好的问题了
4.NPC.AddBuff(int BUFF类型,int BUFF持续时间)
是时候让那些可恶的怪物们尝尝FFF团的怒火了!有了这个方法组,给NPC随意加debuff将不是梦想!
·BUFF类型就是想要加的BUFF的编号,比如燃烧是24,诅咒燃烧是39,霜火燃烧是44,等等
·BUFF持续时间是按帧算的,60帧=1秒(0处理落率),如果这里写60就是一秒,写3600是一分钟,写23333333就是约4.50102874天,依据自己需求来决定时间吧
5.if(条件)
直译:如果满足条件就执行后面的内容,条件应为逻辑值
5.2.else
“否则”是“如果”的好姬友,用于在if的条件不成立时执行后面的语句
5.2.1.多层判定
那啥……else后面也能跟if,所以如果多重判定就可以写成:
if(条件1){代码段1}
else if(条件2){代码段2}
else if(条件3){代码段3}
else……
……
……else{都不满足时的代码段}
5.2.逻辑值的运算
如果需要同时判定多个条件成立,几个条件就套上几个if?如果需要多个条件任一成立,几个条件就写几个else,然后在里面写相同语句?乃们out了!其实许多条件也是可以合并成一个条件的
需要用到的就是逻辑运算符:且(&&)、或(||)和非(!)
且就是数字7上面那个符号打两遍,或就是右shift上面那个按键+shift打出来的(绝对值括号),非就是感叹号(全都是半角)
且和或用于连接两个逻辑值,放于两个逻辑值之间;非放在一个逻辑值的前面,用于否定这个逻辑值(真假颠倒)
5.2.1.几个逻辑运算的法则
1.!!a就是a
2.!(a&&b)就是(!a)||(!b)
3.!(a||b)就是(!a)&&(!b)
例题1:输出一个逻辑值M,在a和b同时不成立的前提下要求满足以下三点中任意两点便成立:①b和c任一成立②d和e同时成立③f成立
解1:M=(!a)&&(!b)&&(((b||c)&&d&&e)||(f&&d&&e)||((b||c)&&f))
评价:简单粗♂暴,适用于对多重括号很熟练的兄贵们
解2:int m=0;
if(b||c)m++;
if(d&&e)m++;
if(f)m++;
M=(!(a||b))&&(m>=2)
评价:略运用了化简的技巧,适用于懒♂的兄贵们
6.反击时刻!
现在让我们把这四条组成一个给附近300像素以内NPC上霜火debuff的效果吧~
6.1.思路(伸手党可以跳过这一段直接复制后方的成品,感兴趣的还是看一下吧~)
先遍历所有NPC记作n:foreach(NPC n in Main.npc)
操♂作开始,首先是确定范围:
NPC所在的矩形是new Rectangle((int)n.position.X,(int)n.position.Y,n.width,n.height)
玩家上下左右300像素的矩形是new Rectangle((int)p.position.X-300,(int)p.position.Y-300,p.width+600,p.height+600)
接下来是判定相交同时确保n还活着,而且是敌对NPC:
if(n.active&&!n.friendly&&new Rectangle((int)n.position.X,(int)n.position.Y,n.width,n.height).Intersects(new Rectangle((int)p.position.X-300,(int)p.position.Y-300,p.width+600,p.height+600)))
最后嘛……尝尝来自北国的FFF团精神吧!
n.AddBuff(44,300);
6.2.成品代码:
foreach(NPC n in Main.npc){
if(n.active&&!n.friendly&&new Rectangle((int)n.position.X,(int)n.position.Y,n.width,n.height).Intersects(new Rectangle((int)p.position.X-300,(int)p.position.Y-300,p.width+600,p.height+600))){
n.AddBuff(44,300);
}
}
6.3.效果图:
BURN BABY,BURN!
嗯……本期教程就此结束,下期将是:【猎奇篇:读取操作】,如何用饰品给予玩家可控的额外火力,尽请期待~
附思考题:
如何做出十动然拒徽章的效果?(排除危险NPC和proj到安全距离以外)(neta自拥着转)
提示:
①proj对应的组是Main.projectile,proj的类型是Projectile
②proj和NPC一样有位置和宽高数据,位置可以被任意操作
③敌对NPC特征是friendly逻辑值为假,敌对proj特征是hostile逻辑值为真
【猎奇篇:读取操作】
前言:
其实这一篇严格地说并不是为增强火力而存在的;换句话说,它不只是为增强火力存在的。
只要读取了电脑前玩家的操作,加上自定的一些代码,玩家就可以做到扩展整个人物的功能,就像K键召唤所有BOSS,F键瞬移,等等……
下面是正题:
1.读取玩家行动
读取玩家行动,其实只需熟记表示玩家状态的几个逻辑值,就能对玩家相应的行动做到加强或改变;平时需要用的基本就是这几条了:
controlLeft;控制向左
controlRight;控制向右
controlUp;控制向上
controlDown;控制向下
controlJump;控制跳
controlHook;控制钩子
例:如何做一个让玩家移动速度加快的饰品?
大体思路就是读取玩家按下的方向键,并相应地适当改变玩家速度,如下:
if (p.controlLeft)
{
if (p.velocity.X > 0) p.velocity.X *= 0.95f;
else if (p.velocity.X > -30) p.velocity.X -= p.velocity.Y == 0 ? 0.5f : 1f;
}
if (p.controlRight)
{
if (p.velocity.X < 0) p.velocity.X *= 0.95f;
else if (p.velocity.X < 30) p.velocity.X += p.velocity.Y == 0 ? 0.5f : 1f;
}
if (p.controlUp)
{
p.controlJump = true;
if (p.velocity.Y > 0) p.velocity.Y *= 0.95f;
else if (p.velocity.Y > -30) p.velocity.Y -= 1f;
}
以上的作用是:玩家控制向左时就增加向左速度,玩家控制向右就增加向右速度;将控制向上和控制跳跃绑定,控制向上就增加向上的速度
2.读取鼠标状态
游戏中自带显示鼠标状态的逻辑值,Main.mouseLeft和Main.mouseRight分别表示鼠标左键和右键是否按下;相应的还有Main.mouseLeftRelease和Main.mouseRightRelease表示左右键是否未按下,可以理解为两者相反
思考题:如何制作按下鼠标右键就回血的饰品?运用前面的教程,题目较为简单,这里就不写示例了
3.读取键盘状态
Main.keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.【按键代号】)
这个语句用来读取键盘状态,如果当前按下了指定【按键代号】的键就等于真;反之,假。一般来说,提取字母键的状态只需把按键代号写成相应大写字母;F1-F12也是直接写大写F加数字就可以了
【猎奇+篇:辅助火力】
提示:这一篇将会有大量的英语和数学成分,不过LZ相信你们会比上学期英语倒10的LZ看得更明白的~
当然最开始还是喜闻乐见地分条讲解代码:
1.Projectile.NewProjectile(坐标X,坐标Y,速度X,速度Y,种类,伤害,击退,所属玩家,ai0,ai1)
字面意思,用来在世界里生成一个proj,中心对准给定的坐标,速度为给定速度,种类、伤害、击退等等都是可以指定的,所属玩家一般写Main.myPlayer或者p.whoAmI(当自己被编号为p时)至于那个ai0和ai1,目前还不需要它们,在写的时候可以纯粹省略这两项,或者填0
这句话可以说是附加火力的核心,没有它一切都是免谈,想象一下,对准怪物按下鼠标,想让火球飞过去就飞火球,想飞激光就飞激光……
桥豆麻袋!电脑怎么知道你想让那个proj飞到哪里去?下面是一些有关确定速度方向的讲解:
①三角函数天才,或者平面向量万年挂科,请前往条目2
②平面向量学霸或者痛恨三角函数,请前往条目3
1.1.先确定自己和鼠标的位置吧~
Vector2 pc=p.position+new Vector2(p.width,p.height)/2;
Vector2 mc=Main.screenPosition+new Vector2(Main.mouseX,Main.mouseY);
2.Math.Atan2(对边,斜边)
两个向量作差,得到了什么?向量?错!是直角三角形!
float r=(float)Math.Atan2(mc.Y-pc.Y,mc.X-pc.X);
(这个函数是反正切,又不是反正切,因为它的对边和斜边随便哪个取0都是可以输出结果的;而且它的值域是2π,也就是一圈!)
接下来,Vector2 v=new Vector2((float)Math.Cos(r),(float)Math.Sin(r));
这就是单位长度,方向从玩家中心指向鼠标的速度向量
3.Vector2.Distance(向量1,向量2)
这个语句是用来求两个向量距离的,同样的效果也可以用求向量长度的语句((向量1-向量2).Length())来实现
Q:没事求向量长度干啥?
A:向量除以长度等于单位向量
也就是:Vector2 v=(mc-pc)/Vector2.Distance(mc,pc);
一发♂入魂的单位向量~
下面就可以组装起来了:
Vector2 pc=p.position+new Vector2(p.width,p.height)/2;
Vector2 mc=Main.screenPosition+new Vector2(Main.mouseX,Main.mouseY);
Vector2 v=(mc-pc)/Vector2.Distance(mc,pc);
Projectile.NewProjectile(pc.X,pc.Y,v.X*6,v.Y*6,255,50,0,p.whoAmI);
示例中为使用单位向量法,速度大小为6,伤害为50,无击退的磁球激光
直接写进去?不不不那样会鬼畜的,激光还好,如果写的是雷♂管之类的和谐物品的话,想象一下每秒60个雷♂管不受控制地射出去的景象……
【猎奇+篇:控制法】
前方大量声明,注意
要控制火力嘛……不然会鬼畜不是吗?所以这里就能利用上前面说的读取键盘状态了
这里可以分为几类触发机制,以下用F键作为例子
0.特别说明
务必加上p.whoAmI==Main.myPlayer的判定,如果你不想自己按键时全图玩家都开始放弹幕,想象一下全图按键会发生什么
1.单发
原理是记录上一帧的F键状态,如果两个状态不同,代表按下(弹起)了F,键,就可以放弹幕了
具体:
using System;
using TAPI;
using Microsoft.Xna.Framework;
namespace TAPI.【MOD名称】{
public class 【道具名称】:ModItem{
public 【道具名称】(ModBase modbase,Item i):base(modbase,i){}
bool oldF=false;
public override void Effects(Player p){
if(p.whoAmI==Main.myPlayer&&!oldF&&Main.keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.F)){
//这里面就是写前面发射子弹代码的地方
}
oldF=Main.keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.F;
}
}
}
这种触发方式的频率是取决于按键速度的,也就是按得越快射♂得越快,理论射速峰值为30发每秒
2.连发
原理是添加一个计时变量,至于具体怎么计时就看个人喜好了,这里提供一种解决方案:
using System;
using TAPI;
using Microsoft.Xna.Framework;
namespace TAPI.【MOD名称】{
public class 【道具名称】:ModItem{
public 【道具名称】(ModBase modbase,Item i):base(modbase,i){}
int timer=0;
public override void Effects(Player p){
if(timer>0)timer--;
else{
if(p.whoAmI==Main.myPlayer&&Main.keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.F)){
timer=10;
//这里面就是写前面发射子弹代码的地方
}
}
}
}
}
按住F就可以连射了~示例为每秒触发6次的写法
3.自动
这里就略复杂了点,需要具有想象力以及数学功底,当然还有对于弹幕的热爱=w=
简单地来一个施放旋转镰刀的脚本吧~
using System;
using TAPI;
using Microsoft.Xna.Framework;
namespace TAPI.ctx{
public class ctxbullet : ModItem{
int timer = 0;
float r = 0;
float rv = 0;
float ra = 0.001f;
bool barrage = false;
public ctxbullet(ModBase modbase, Item i) : base(modbase, i) { }
public override void Effects(Player p){
rv += ra;
if (rv > 3) rv -= (float)Math.PI * 2f;
r += rv;
if (r > 3) r -= (float)Math.PI * 2f;
timer++;
if (timer > 3) timer -= 3;
if (barrage){
if (p.statMana < 3 || p.whoAmI != Main.myPlayer){
barrage = false;
goto outOfMana;
}
else p.statMana -= 3;
if (timer % 3 == 0){
Vector2 pc = p.position + new Vector2(p.width, p.height) / 2;
Vector2 v = new Vector2((float)Math.Cos(r), (float)Math.Sin(r));
Projectile.NewProjectile(pc.X, pc.Y, v.X * 20f, v.Y * 20f, 274, 100, 0, p.whoAmI);
Projectile.NewProjectile(pc.X, pc.Y, -v.X * 20f, -v.Y * 20f, 274, 100, 0, p.whoAmI);
}
}
outOfMana:
if (Main.keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.F)) barrage = !barrage;
}
}
}
因为需要美观,就多定义了几个角度增速,角度加速度,角度急动度,角度痉挛度神马的……
效果图:
(所以说弹幕可以培养数学和想象力=w=)
当然,自动类弹幕的形式是非常多的,从上文简单的喷环环,到折返射、西行寺无余涅盘、梦想天生、原子崩坏、金刚枪破,等等……只要看懂弹幕的组成,tAPI就能写出来!
嘛……作为饰品部分的教程基本已经结束了,从道具json的创建一直到在cs里声明和操作变量,基本现在已经可以复原所有原版饰品的效果了,玩家们也能首夜裸装站撸机械三王了