前置知识: Steve_666:从零开始的NPC教程
好,从这里开始,我们的零基础Boss制作之旅
Boss的创建
好,既然是Boss教程,那我们就先来创建一个Boss,Boss是什么呢,其实就是一个NPC,要用 public class XXX : ModNPC
来创建一个基础的npc,但是boss需要一个特殊的属性,[AutoloadBossHead]
,这个属性干嘛的呢?就是加载npc地图上的大头贴,这个需要你自备图片
注意两点: [AutoloadBossHead]
要写在public class外,还有大头贴的命名格式:Boss名字_Head_Boss
public override void SetDefaults() { NPC.width = 114; NPC.height = 514;//这两个代表这个NPC的碰撞箱宽高,以及tr会从你的贴图里扣多大的图 NPC.damage = 191; NPC.lifeMax = 9810;//npc的血量上限 NPC.defense = 38; NPC.scale = 2.33f;//npc的贴图和碰撞箱的放缩倍率 NPC.knockBackResist = 0f; NPC.HitSound = SoundID.NPCHit5;//挨打时发出的声音 NPC.DeathSound = SoundID.NPCDeath7;//趋势时发出的声音 NPC.value = Item.buyPrice(0, 4, 50, 0);//NPC的爆出来的MONEY的数量,四个空从左到右是铂金,金,银,铜 NPC.lavaImmune = true;//对岩浆免疫 NPC.noGravity = true;//不受重力影响。一般BOSS都是无重力的 NPC.noTileCollide = true;//可穿墙 NPC.npcSlots = 20; //NPC所占用的NPC数量,在TR世界里,NPC上限是200个,通常,这个用来限制Boss战时敌怪数量,填个10,20什么的 NPC.boss = true; //将npc设为boss 会掉弱治药水和心,会显示xxx已被击败,会有血条 NPC.dontTakeDamage = true;//为true则为无敌,这里的无敌意思是弹幕不会打到npc,并且npc的血条也不会显示了 Music = MusicLoader.GetMusicSlot("xxx/Assets/Music/xxx"); }
还有几个基础属性
life
是npc的当前血量
realLife
是多体共血,比如蠕虫,之后我会细说
aiStyle
为-1代表不使用原版AI,如果不写这一条,则会默认为0,aistyle为0时代表的是束缚状态的NPC,X轴速度会趋于0
music = :BOSS战的背景音乐,AI里面可以修改这个来切换BOSS音乐,所以换阶段可以换音乐了,这个要写自己的路径,然后音乐名无后缀,什么?如果要用原版音乐怎么办?
Music = MusicID.Boss1;
接下来是SetStaticDefaults
public override void SetStaticDefaults() { DisplayName.SetDefault("114514"); DisplayName.AddTranslation(7, "你是一个一个~"); Main.npcFrameCount[NPC.type] = 3; NPCDebuffImmunityData debuffData = new NPCDebuffImmunityData { SpecificallyImmuneTo = new int[] { BuffID.Poisoned, } }; }
DisplayName.SetDefault
是npc在游戏内显示的名字
DisplayName.AddTranslation
是给npc的名字加一个翻译,这里的7shi
Silence,wench!I do not want to see magic figures anymore,I just want to be code-concise.
我们使用 DisplayName.AddTranslation((int)GameCulture.CultureName.Chinese, "谢谢cheems");
有人问SetDefaults
和SetStaticDefaults
有什么区别,答案是SetDefaults
是实例的,SetStaticDefaults
是静态的
什么意思呢?举个例子,场上有两个boss,你如果在SetDefaults
里设置一个变量,那么两个boss用的是不同的变量,在SetStaticDefaults
里设置,那么两个boss用的是同一个变量
buffimmune[ID]
:免疫BUFF。例如BUFFID.Poisoned
,你可以这么写
NPCDebuffImmunityData debuffData = new NPCDebuffImmunityData { SpecificallyImmuneTo = new int[] { BuffID.Poisoned, } };
npcFrameCount[NPC.type]
,这个就填你的boss有几帧,什么?不填可不可以?
各种重写函数
首先我们来了解一下经典,专家和大师的npc增强
经典 | 专家 | 大师 | |
伤害 | 1倍 | 2倍 | 3倍 |
防御 | 1倍 | 1倍 | 1倍 |
生命值 | 1倍 | 2倍 | 3倍 |
弹幕伤害 | 2倍 | 4倍 | 6倍 |
如果我们想自己调专家大师的数据,那我们可以使用这个重写函数
public override void ScaleExpertStats(int numPlayers, float bossLifeScale) { NPC.life = (int)(NPC.life * 0.5f); }
但是这个只能改变专家模式下的数据,而大师模式则需要用另一种方法
public static int Life() { if (Main.masterMode) return 114514 / 3; else if (Main.expertMode) return 114514 / 2; else return 114514; } public override void SetDefaults() { NPC.lifeMax = Life(); }
这种方法可以更改所有模式的所有数据,我喜欢这一种,再来提一嘴,当大师模式时,Main.expertMode
和Main.masterMode
都是true
另外,两个方法不能同时使用,如果有人因为这个出问题我当场噶了他
下一个,AI
函数,这是游戏非暂停模式下每帧执行的函数,下面会细讲
public override void AI() { //你想写什么都可以 }
好,来了一个重点,FindFrame
public override void FindFrame(int frameHeight) { NPC.frame.Y = x * frameHeight; }
只要用这段代码就可以切换到npc的第x帧辣!注意,这个x是从零开始算的,所以x=0就是第一帧,以此类推
NPC.frame.Y
他是按像素算的,不要被坑了啊
HitEffect
在BOSS被挨打时执行
public override void HitEffect(int hitDirection, double damage) { }
ModifyNPCLoot
,在NPCLoot
之前执行的函数,可以更改掉落
public override void ModifyNPCLoot(NPCLoot npcLoot) { }
OnHitPlayer
:boss本身击中玩家时执行,仅限npc碰撞时产生
public override void OnHitPlayer(Player target, int damage, bool crit) { }
ModifyHitbyItem
:boss被玩家的物品(真近战)击中前执行
public override void ModifyHitByItem(Player player, Item item, ref int damage, ref float knockback, ref bool crit) { }
OnHitbyItem
:boss被玩家的物品(真近战)击中时执行
public override void OnHitByItem(Player player, Item item, int damage, float knockback, bool crit) { }
ModifyHitByProjectile
:被玩家的弹幕命中前执行
public override void ModifyHitByProjectile(Projectile projectile, ref int damage, ref float knockback, ref bool crit, ref int hitDirection) { }
OnHitByProjectile
:被玩家的弹幕命中时执行
public override void OnHitByProjectile(Projectile projectile, int damage, float knockback, bool crit) { }
OnKill
:boss被干掉时执行,这个我用来写尸块,我们来解析一下
public override void OnKill() { int Gore1 = Mod.Find<ModGore>("Gore1").Type; int Gore2 = Mod.Find<ModGore>("Gore2").Type; var entitySource = NPC.GetSource_Death(); for (int i = 0; i < 4; i++) { Gore.NewGore(entitySource, NPC.position, new Vector2(Main.rand.Next(-6, 7), Main.rand.Next(-6, 7)), Gore1); } Gore.NewGore(entitySource, NPC.position, new Vector2(Main.rand.Next(-6, 7), Main.rand.Next(-6, 7)), Gore2); SoundEngine.PlaySound(SoundID.Roar, NPC.Center); }
第一第二行,是定义两个尸块,尸块需要单独的图片
下一行是定义npc的死亡源,这个不用管
接下来就是生成4个gore1,一个gore2,播放一个吼叫音效
来看看效果
肥肠滴好啊!
啊哈哈哈,重点来喽!StrikeNPC
函数
public override bool StrikeNPC(ref double damage, int defense, ref float knockback, int hitDirection, ref bool crit) { damage = 114514; return true; }
讲解一下,这个函数在npc挨打前执行,damage
是造成的伤害,defense
就是受击时的防御,knockback
就是受到的击退,hitDirection
是攻击的方向,1是右,-1是左,crit
是是否暴击,如果return false
的话则是不受伤,哦对了,damage
为0仍然会让npc受1点伤,暴击2点
还有一个函数,PreDraw
函数,这个函数在npc绘制前执行,特效拖尾都要写在里面,后面细讲,返回false可以干掉原版绘制
public override bool PreDraw(SpriteBatch spriteBatch, Vector2 screenPos, Color drawColor) { return true; }
PostDraw
函数,在npc绘制后执行
public override void PostDraw(SpriteBatch spriteBatch, Vector2 screenPos, Color drawColor) { }
BossLoot
,这个是boss的药水掉落函数,说是药水掉落,其实你填什么都可以
public override void BossLoot(ref string name, ref int potionType) { potionType = ItemID.LovePotion; name = "下北泽"; }
这个name
,就是boss被干掉时的名字
下面一个是CheckDead
,在npc趋势时触发,返回false会导致npc死亡啥也不掉,连尸块都没有,经常用来制作尾杀
public override bool CheckDead() { return false; }
DrawHealthBar
这个函数,控制Boss的血条绘制 ,scale
是血条的大小,position
是血条的位置,return false
可以阻止原版血条的绘制
注意,这个血条是所有npc都有的那个绿色的,在npc下方的,不是屏幕下面那个boss血条啊喂!
public override bool? DrawHealthBar(byte hbPosition, ref float scale, ref Vector2 position) { scale = 3f; position += new Vector2(0, -32); return true; }
重点来辣!NPCLoot
函数
public override void ModifyNPCLoot(NPCLoot npcLoot) { LeadingConditionRule notExpertRule = new LeadingConditionRule(new Conditions.NotExpert()); notExpertRule.OnSuccess(ItemDropRule.Common(ItemID.StoneBlock, 3, 5, 15)); npcLoot.Add(notExpertRule); npcLoot.Add(ItemDropRule.BossBag(ItemID.StoneBlock)); npcLoot.Add(ItemDropRule.MasterModeCommonDrop(ItemID.StoneBlock)); npcLoot.Add(ItemDropRule.MasterModeDropOnAllPlayers(ItemID.StoneBlock)); }
好,我来细细地讲解一下,第一行是创建一个掉落规则,在非专家大师模式时掉落,第二行是向这个掉落规则里放一个掉落,1/3概率掉落,一次掉落5~15个石头,第三行是把这个掉落规则放入总掉落里,第四行是向总掉落里放一个石头,这个石头以宝藏袋的标准掉落出来(只在专家大师掉落),第五行,在大师模式下掉落一个石头,第六行,在大师模式下,给所有玩家,都掉落一个石头,人人有份,每个人在自己的客户端都有一个
如果想要写出拥有更多花样的掉落,以及宝藏袋本身,请参考这篇教程
小作业
写出你的第一个Boss,要求:
Boss血条缩小,显示在boss头上,在大师模式有1/10的概率掉落0-11个石头,可以反弹所有攻击
很简单吧,我走了,我不打扰()
试着用计时器写了一个小boss,雏作勿喷:
https://www.bilibili.com/video/BV14e4y1s7NA?share_source=copy_web