跳至正文

粘滞型弹幕

还记得原版的破晓之光吗?

有请受害者

今天咱们就来试着制作一个这种可以粘在敌人身上的弹幕。

老规矩,创建一个类继承 ModProjectile,写入基础属性。记得准备贴图哦~

送你们一个狙击步枪的贴图hhhhhh

public override void SetDefaults()
{
    Projectile.width = 16;
    Projectile.height = 16;
    Projectile.friendly = true;
    Projectile.tileCollide = true;
    Projectile.penetrate = 2;//穿透多个,不然刚打到就寄了,这样就粘不到嘞
    Projectile.ignoreWater = true;
    Projectile.timeLeft = 600:
    Projectile.DamageType = DamageClass.Ranged;
    //属性可以自己改
}

接下来进入弹幕AI部分,为了清晰的表示逻辑,这里用弹幕的 ai[0] 来识别弹幕是否处于粘在NPC身上的状态,用ai[1]来记录被命中的 NPC.whoAmI

public bool Sticking
{
    get { return Projectile.ai[0] != 0; }// 因为默认状态下ai[0]是 = 0,所以这里用 != 0进行判定
    set { Projectile.ai[0] = value ? 1 : 0; }// 三元运算符:当表达式值为true,返回前者,反之为后者
}
public int TargetWho
{
    get { return (int)Projectile.ai[1]; }
    set { Projectile.ai[1] = value; }
}
public override void AI()
{
    if (Sticking)// 当弹幕粘在NPC时执行
    {
        // 获取粘滞的NPC
        NPC target = Main.npc[TargetWho];

        // 如果目标NPC死了弹幕也一起死亡
        if (!target.active)
        {
            Projectile.Kill();
            return;// 结束函数
        }

        // 把目标的速度给弹幕,让弹幕跟着粘滞目标
        // 这就是粘住了(迫真
        Projectile.velocity = target.velocity;

        //然后你就可以在此对NPC造成伤害,也可以做一些其他效果比如生成粒子,给NPC上buff

        Projectile.ai[2]++;// 作为攻击计时器
        // 此处即为每10帧对NPC造成一次伤害,数值为5点
        if (Projectile.ai[2] >= 10)
        {
            target.SimpleStrikeNPC(5, 0);
            Projectile.ai[2] = 0;
        }
    }
    else// 弹幕正常行动时执行
    {
        // 这里可以不写任何东西,反正弹幕被射出来就会按着给的速度自己跑

        // 不过假如你想模拟重力,你可以这么写
        // 弹幕Y轴速度每帧增加,它就是个抛物线的感觉了
        Projectile.velocity.Y += 0.1f;
        // 让弹幕的角度等于速度的朝向,用于视觉(绘制)
        Projectile.rotation = Projectile.velocity.ToRotation();
    }
}

接下来,我们要让弹幕执行“粘上NPC”这个行为。使用之前介绍过的命中后效果那个函数:

public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone)
{
    // 打到某个目标之后
    // 把粘滞设为true,这样AI就会从正常行动切换到粘滞状态
    Sticking = true;
    // 把被命中目标的身份记录下来
    TargetWho = target.whoAmI;
    // 并重置弹幕的存活时间,60为一秒,要多久看你们
    Projectile.timeLeft = 360;
}

但此时还有一个问题,我们要保证弹幕粘上NPC之后不能再伤害NPC,否则会导致弹幕的 penetrate(穿透数)减少,导致弹幕死亡(这玩意 = 0 就会寄)。所以现在要用这个:

public override bool? CanDamage()
{
    // 不在粘滞状态才能造成伤害
    // 这和AI里那个SimpleStrikeNPC并不会冲突
    return !Sticking;
}

最后是弹幕的绘制,这个在这里不做介绍,想了解详细请查阅绘制教程。

public override bool PreDraw(ref Color lightColor)
{
    Texture2D tex = TextureAssets.Projectile[Type].Value;
    Main.spriteBatch.Draw(tex, Projectile.Center - Main.screenPosition, null, lightColor * Projectile.Opacity,
        Projectile.rotation, tex.Size() / 2f, Projectile.scale,
        (Math.Abs(Projectile.rotation) < Math.PI / 2f) ? 0 : SpriteEffects.FlipVertically, 0f);
    return false;
}
再次有请受害者

可以看到狙击步枪扎在石头人身上了(笑死)

至于只打了一滴血自然是因为伤害太低被防御抵了。不过它的图层似乎在石巨人之上,倒回文章开头看看破晓之光,你应该能发现它的图层在石巨人下边。

这个问题也是很好解决的,首先在命中NPC那里添加 Projectile.hide = true; ,隐藏原本的绘制,随后在 SetStaticDefaults 重写函数里写上 ProjectileID.Sets.DontAttachHideToAlpha[Type] = true; ,让弹幕使用自身中心的光照而不是玩家中心的光照作为弹幕亮度的渲染(如果 hidetrue 的话会默认使用玩家中心光照作为弹幕亮度的渲染),接着把绘制添加到NPC图层的后面:

public override void DrawBehind(int index, List<int> behindNPCsAndTiles, List<int> behindNPCs, List<int> behindProjectiles, List<int> overPlayers, List<int> overWiresUI)
{
    behindNPCs.Add(index);
}
受害者三顾茅庐

哈,这下对了。粘滞弹幕的基本点都在这啦,(其实这篇是个阉割版,细节并不全)

其它的花里胡哨玩意儿就要你们自己去探索咯。这里附上Exmod的粘滞弹幕

怎么发射这个弹幕不需要再教了吧?

啥?要?自己看武器篇去(恼

发表回复