主题
投射物模式指南(Projectile Patterns)
GameDataEffectLaunchMissile 支持三种投射物飞行模式。选择正确的模式是投射物伤害正常触发的关键。
模式速查表
| 模式 | 目标 | DoImpactEntity | 命中效果属性 | FixedRange | 典型游戏参考 |
|---|---|---|---|---|---|
| 追踪型 | 单位 | false | CompleteEffect | 不设 | 普攻弹道、追踪导弹 |
| Skillshot 穿透型 | 点 / FixedRange | true | ImpactEffect | 推荐设置 | Vampire Survivors 火球、穿透箭矢 |
| 落点 AOE 型 | 点 | false | CompleteEffect → 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 而非 ImpactEffect | Skillshot 沿途碰撞用 ImpactEffect |
| 投射物撞地面消失 | DoStaticCollision + 高度差 | 设置 TargetHeight = LaunchHeight 使水平飞行,或关闭 DoStaticCollision |
| Search → LaunchMissile 变成追踪 | Search 找到的是单位目标 | 使用 FixedRange 或 PolarOffset 转为点目标 |
| 投射物不飞行 | 未设置 UpdateFlags.AllowMover = true | 投射物单位必须开启 AllowMover |