Skip to content

投射物模式指南(Projectile Patterns) ​

GameDataEffectLaunchMissile 支持三种投射物飞行模式。选择正确的模式是投射物伤害正常触发的关键。

模式速查表 ​

模式目标DoImpactEntity命中效果属性FixedRange典型游戏参考
追踪型单位falseCompleteEffect不设普攻弹道、追踪导弹
Skillshot 穿透型点 / FixedRangetrueImpactEffect推荐设置Vampire Survivors 火球、穿透箭矢
落点 AOE 型点falseCompleteEffect → Search可选迫击炮、手雷、陨石

模式一:追踪型(Homing) ​

投射物追踪目标单位,到达后执行 CompleteEffect。

效果链:Search → LaunchMissile(单位目标)→ 到达后 CompleteEffect

csharp
// 投射物单位
_ = new GameDataUnit(Unit.HomingMissile)
{
    Filter = [UnitFilter.Missile],
    State = [UnitState.Invulnerable],
    CollisionRadius = 16,
    AttackableRadius = 32,
    Particle = "effect/missile/particle.effect"u8,
    UpdateFlags = new() { AllowMover = true },
};

// 发射效果——单位目标,不开 DoImpactEntity
_ = new GameDataEffectLaunchMissile(Effect.HomingLaunch)
{
    Missile = Unit.HomingMissile,
    Speed = static (_) => 600,
    CompleteEffect = Effect.HomingDamage,  // 到达目标后造成伤害
};

// 搜索效果——找到的单位作为 LaunchMissile 的目标
_ = new GameDataEffectSearch(Effect.HomingSearch)
{
    Method = SearchMethod.Circle,
    Radius = static (_) => 500,
    MaxCount = static (_) => 1,
    SearchFilters = [new() {
        Required = [UnitRelationship.Enemy],
        Excluded = [UnitState.Dead, UnitState.Invulnerable]
    }],
    Effect = Effect.HomingLaunch,
};

关键点:

  • 不要开 DoImpactEntity,否则投射物在飞向目标途中碰到其他单位会提前触发 ImpactEffect 引爆
  • 伤害效果放在 CompleteEffect(继承自 GameDataEffectPersist)

模式二:Skillshot 穿透型(Penetrating) ​

投射物飞向固定距离的点,沿途碰撞敌人触发 ImpactEffect。

效果链:Search → LaunchMissile(FixedRange 点目标)→ 沿途 ImpactEffect

csharp
// 发射效果——FixedRange 自动将搜索到的单位目标转为固定距离点
_ = new GameDataEffectLaunchMissile(Effect.FireballLaunch)
{
    Missile = Unit.FireballMissile,
    FixedRange = static (_) => 800f,        // 固定射程 800
    DoImpactEntity = true,                  // 开启沿途碰撞
    ImpactEffect = Effect.FireballDamage,   // 碰到敌人时触发伤害
    ImpactMaxCount = static (_) => 1,       // 最多穿透 1 个敌人
    ImpactSearchRadius = static (_) => 48,  // 碰撞检测半径
    ImpactSearchFilter = [new() {
        Required = [UnitRelationship.Enemy],
        Excluded = [UnitState.Dead, UnitState.Invulnerable]
    }],
    Speed = static (_) => 800,
    LaunchHeight = (_) => 80,
    TargetHeight = (_) => 80,  // 同高度确保水平飞行
};

// 搜索敌人,直接作为 LaunchMissile 的效果
_ = new GameDataEffectSearch(Effect.FireballSearch)
{
    Method = SearchMethod.Circle,
    Radius = static (_) => 600,
    MaxCount = static (_) => 3,
    SearchFilters = [new() {
        Required = [UnitRelationship.Enemy],
        Excluded = [UnitState.Dead, UnitState.Invulnerable]
    }],
    Effect = Effect.FireballLaunch,
};

关键点:

  • FixedRange 会自动取"发射位置→目标"的方向,飞往发射位置前方固定距离的点
  • FixedRangeOffsetByUnitRadius(默认 true)会叠加发射位置的交互半径
  • 伤害效果放在 ImpactEffect,不是 CompleteEffect
  • LaunchHeight 和 TargetHeight 同时设置可确保投射物水平飞行,避免被 DoStaticCollision 提前拦截

不使用 FixedRange 的替代方案 ​

如果需要更灵活的控制(如非施法者发射、动态射程),可以手动用 GameDataEffectPolarOffset 做方向转换:

csharp
// Search → PolarOffset → LaunchMissile
_ = new GameDataEffectPolarOffset(Effect.FireballOffset)
{
    TargetLocation = new() { Value = BaseType.TargetLocation.Caster },
    Angle = new()
    {
        Method = EffectAngleMethod.AngleBetweenTwoPoints,
        Location = new() { Value = BaseType.TargetLocation.Caster },
        OtherLocation = new() { Value = BaseType.TargetLocation.Default },
    },
    Distance = static (_) => 800f,
    TargetEffect = Effect.FireballLaunch,
};

> 提示:效果树的 TargetLocation 支持从非施法者位置发射投射物。例如设置 LaunchLocation 为 Source 可以从召唤物位置发射。

模式三:落点 AOE 型(Point-Target AOE on Arrival) ​

投射物飞到指定点,到达后在落点执行 AOE 效果。不碰撞中途单位。

效果链:LaunchMissile(点目标)→ 到达后 CompleteEffect → Search(AOE)→ Damage

csharp
// AOE 搜索 + 伤害(在落点执行)
_ = new GameDataEffectSearch(Effect.MortarAOE)
{
    Method = SearchMethod.Circle,
    Radius = static (_) => 200,
    MaxCount = static (_) => 10,
    SearchFilters = [new() {
        Required = [UnitRelationship.Enemy],
        Excluded = [UnitState.Dead, UnitState.Invulnerable]
    }],
    Effect = Effect.MortarDamage,
};

// 发射效果——点目标,不碰撞中途单位
_ = new GameDataEffectLaunchMissile(Effect.MortarLaunch)
{
    Missile = Unit.MortarShell,
    Speed = static (_) => 400,
    ParabolaApex = static (_) => 300f,  // 抛物线飞行
    CompleteEffect = Effect.MortarAOE,  // 到达落点后执行 AOE
    TurnToVelocity = true,             // 跟随抛物线旋转
};

关键点:

  • 不开 DoImpactEntity
  • 伤害效果放在 CompleteEffect,配合 Search 在落点做范围搜索
  • ParabolaApex 可设置抛物线轨迹,适合迫击炮/手雷视觉效果

复用自身:冲锋遇静态障碍停止 ​

GameDataEffectLaunchMissile 也可以复用施法者作为移动单位。设置 Method=UseExistingMissile、WhichUnit.Value=Caster 和主角的 UpdateFlags.AllowMover=true;使用 FixedRange 与 Speed 控制冲锋,开启 DoStaticCollision,并按障碍类别设置 StaticMask。地面悬崖冲锋示例使用 [Unwalkable, Cliff],不要直接沿用默认的 [Unflyable]。

Surface2D 的静态遮挡来自保存时烘焙的地形/装饰物与手绘碰撞格。先保存并重开场景,再调试空地和障碍方向。碰撞停止保留原单位,属于效果中断;CompleteEffect 只用于正常到达,统一收尾放在 FinalEffect。完整字段、代码和验收流程见 冲锋静态碰撞 Skill。

投射物单位定义 ​

使用 CreateMissile 创建投射物时,需要一个 GameDataUnit 作为飞行体;UseExistingMissile 则复用已有单位:

csharp
_ = new GameDataUnit(Unit.MyMissile)
{
    Filter = [UnitFilter.Missile],           // 标记为投射物
    State = [UnitState.Invulnerable],        // 不可被攻击
    CollisionRadius = 16,                    // 碰撞半径
    AttackableRadius = 32,                   // 可被攻击半径(影响 ImpactSearchRadius)
    Particle = "effect/missile/particle.effect"u8,  // 投射物视觉
    UpdateFlags = new() { AllowMover = true },      // 允许移动器控制
};

常见陷阱 ​

问题原因解决方案
追踪型投射物提前引爆开了 DoImpactEntity追踪型不需要 DoImpactEntity
Skillshot 不造成伤害伤害放在 CompleteEffect 而非 ImpactEffectSkillshot 沿途碰撞用 ImpactEffect
投射物撞地面消失DoStaticCollision + 高度差设置 TargetHeight = LaunchHeight 使水平飞行,或关闭 DoStaticCollision
Search → LaunchMissile 变成追踪Search 找到的是单位目标使用 FixedRange 或 PolarOffset 转为点目标
投射物不飞行未设置 UpdateFlags.AllowMover = true投射物单位必须开启 AllowMover