mina好,我是苏雪,我这次来细嗦一下星尘龙的运动逻辑。
隔壁多节召唤物码太长了拿一点出来
他大致分为三个部分:索敌-攻击-游荡。来看看状态图:
以下是润色过(numXXX大屠杀!)的代码:
public static void StarDustDragonAI(Projectile Projectile, Player Player, float MaxDisToPlayer = 2000, float SearchDis = 700, float MaxSpeed_NoTarget = 15f, float MaxSpeed_HasTarget = 30f) { // 弄成了一个方法,可以直接给弹幕使用哦。前两个参数就不说了,第三个是离玩家的最大距离,第四个是索敌距离(不要超过1000),第五个是无目标限速,最后一个是有目标限速。接着往下看吧。下边的代码都是放在这个方法里面的———— }
// 弹幕超出玩家2000f自动回归,并同步数据 if (Vector2.Distance(Player.Center, Projectile.Center) > MaxDisToPlayer) { Projectile.Center = Player.Center; Projectile.netUpdate = true; }
当 netUpdate
这个属性为 true
的时候,会执行数据同步,并重置为 false
。
int TarWho = -1;// 目标whoAmI NPC target = Projectile.OwnerMinionAttackTargetNPC;// 召唤物自动索敌 if (target != null && target.CanBeChasedBy()) { // 虽然但是,为什么要用 两倍 索敌距离 if (Projectile.Distance(target.Center) < SearchDis * 2f) { TarWho = target.whoAmI; } } // 如果自动索敌没有找到目标,就搜索离玩家1000f内且在弹幕索敌距离内的目标 if (TarWho < 0) { foreach (NPC npc in Main.npc) { if (npc.CanBeChasedBy() && Player.Distance(npc.Center) < 1000f) { if (Projectile.Distance(npc.Center) < SearchDis) { TarWho = npc.whoAmI; } } } }
Projectile.OwnerMinionAttackTargetNPC
,这是一个弹幕自带的寻找攻击目标的属性。CanBeChaseBy()
是原版自带的用于判定 npc
能不能被打的 bool
方法,他包括了active,friendly,是否小动物等等。
if (TarWho != -1)// 有攻击目标 { NPC npc = Main.npc[TarWho]; Vector2 tarVel = npc.Center - Projectile.Center; float speed = 0.4f;// 追击速度系数 float dis = tarVel.Length(); // 越近越快 if (dis < 600f) { speed = 0.6f; } if (dis < 300f) { speed = 0.8f; } // 到npc的距离比npc的碰撞箱大小的0.75倍大,也就是说离NPC还比较远 // 这使得星尘龙在穿过NPC后才会调头,而不是直接粘在它身上鬼畜 if (dis > npc.Size.Length() * 0.75f) { // 弹幕速度加上朝着npc的单位向量*追击速度系数*1.5f Projectile.velocity += Vector2.Normalize(tarVel) * speed * 1.5f; //如果追踪方向和速度方向夹角过大,减速(向量点乘你们自己研究,绝对值越大两向量夹角越大,范围是正负1) if (Vector2.Dot(Projectile.velocity, tarVel) < 0.25f) { Projectile.velocity *= 0.8f; } } // 限制最大速度 if (Projectile.velocity.Length() > MaxSpeed_HasTarget) { Projectile.velocity = Vector2.Normalize(Projectile.velocity) * MaxSpeed_HasTarget; } }
注意这是一个连续的行为,虽然图上看起来挺生硬,但是运动起来还是比较丝滑的。
else// 无攻击目标 { float speed = 0.2f;// 游荡速度系数 float dis = Player.Center.Distance(Projectile.Center);// 弹幕到玩家距离,越近越慢 if (dis < 200f) { speed = 0.12f; } if (dis < 140f) { speed = 0.06f; } if (dis > 100f)// 在100f外朝着玩家飞 { // abs绝对值,sign正负 if (Math.Abs(Player.Center.X - Projectile.Center.X) > 20f)// 离玩家水平距离大于20,就给朝向玩家的水平速度 { Projectile.velocity.X += speed * Math.Sign(Player.Center.X - Projectile.Center.X); } if (Math.Abs(Player.Center.Y - Projectile.Center.Y) > 10f)// 离玩家垂直距离大于10,就给朝向玩家的垂直速度 { Projectile.velocity.Y += speed * Math.Sign(Player.Center.Y - Projectile.Center.Y); } } else if (Projectile.velocity.Length() > 2f)// 在100f内且速度较大就减速 { Projectile.velocity *= 0.96f; } if (Math.Abs(Projectile.velocity.Y) < 1f)// 没啥速度了就慢慢向上游动 { Projectile.velocity.Y -= 0.1f; } //无目标最大速度15f,有目标30f if (Projectile.velocity.Length() > MaxSpeed_NoTarget) { Projectile.velocity = Vector2.Normalize(Projectile.velocity) * MaxSpeed_NoTarget; } } Projectile.rotation = Projectile.velocity.ToRotation();
不难的逻辑。
但是能留小作业()
从这个运动逻辑中找出为什么星尘龙有的时候会停止运动?并试着修复这个问题。(提示:判定盲区)
没有目标直接攻击Player(不是
if(没目标)hostile = true;(x)
苏雪好棒