NPC 移动
NPC 的地面步态、导航缓存、越障飞行、降落与游泳控制约定。
NPC 将连续的地面速度求解器与分阶段的飞行、游泳控制器组合使用。这些类位于 internal.ai.travel,是扩展模组 NPC 实现时使用的内部运行时组件,不是公共 API 门面。
它做什么
NpcGaitController 分别决定导航速度与疾跑表现。NpcNavigation 管理路径及空中、水中航点缓存。NpcMoveControl 执行地面、飞行、降落和水中转向;NpcStepAssist 则为实体移动与寻路提供统一的跨步高度。
基础实体安装 NpcConfig.flightSettings 与默认游泳设置。canNpcFly() 返回 false,canNpcSwim() 和 canBreatheInWater() 返回 true。因此市民、简单 NPC 和冒险者默认不能飞行或悬停。需要飞行的子类须显式覆盖 canNpcFly() 并返回 true;伊芙琳已显式启用。飞行配置只调整已启用的能力。队形位置、梯子/跑酷控制权和移动命令属于独立的跟随系统。
没有飞行能力时,setNpcFlying 与 setNpcHovering 拒绝启用对应状态,从 NBT 恢复同步标志也经过此检查;setManualFlight 同样拒绝不支持的请求。共享动画套件不含 fly 或 hover,飞行 NPC 须在自身动画文件中提供这两个片段,见 NPC 资源。
运行时模型
地面步态
普通跟随、地面命令和战斗接近共享实体上的步态控制器。跟随传入玩家的疾跑状态,不请求紧急移动。地面命令与战斗只在 sprintRange > 0 且目标距离达到该值时请求紧急移动。跟随停留时放松求解器,专用越障接管时重置;跟随任务结束会清除移动与观察记忆、越障状态和疾跑标记。
当前需求公式包含随剩余距离变化的上限。steps 为经过的刻数,重新采样时使用 1。采样过期会刷新距离、位置和接近速度,但不会自动调用完整的 reset()。
gap = max(0, filteredDistance - max(0, holdDistance))
desiredClosing = min(sqrt(2 × 0.005 × gap), 0.24)
ceiling = clamp(0.25 + gap × 0.5, 0, 1)
target = clamp(demand + (desiredClosing - closingRate) × 0.35, 0, ceiling)
if urgent: target = max(target, min(0.75, ceiling))
demand = clamp(demand + clamp(target - demand, -0.08 × steps, 0.08 × steps), 0, 1)
walk = max(0.15, walkSpeed)
sprint = max(walk, sprintSpeed)
blended = lerp(demand, walk, sprint)
travelSpeed = sprinting ? blended / 1.3 : blended
疾跑标记通过 setNpcSprinting 发布;本实现以固定除数 1.3 补偿导航速度。apply() 通过 setTravelSpeed() 修改现有路径速度,不替换路径。relax() 保留历史并降低需求;reset() 清除历史、步态与缓存输出。
| 常量 | 值 | 作用 |
|---|---|---|
ARRIVAL_DECELERATION | 0.005 | 到达减速系数。 |
MAX_CLOSING_RATE | 0.24 | 期望接近速度上限,方块/刻。 |
TRACK_GAIN | 0.35 | 接近速度误差增益。 |
MAX_DEMAND_RATE | 0.08 | 每经过一刻的需求值最大变化。 |
DEMAND_CEILING_BASE / DEMAND_CEILING_PER_BLOCK | 0.25 / 0.5 | 按剩余距离限制需求上限的系数。 |
DISTANCE_LERP / CLOSING_LERP / GROUND_SPEED_LERP | 0.45 / 0.35 / 0.30 | 距离、接近速度和水平速度的滤波系数。 |
STALE_TICKS | 6 | 经过时间非正或间隔超过此值时重新采样,避免将位置跳变作为速度。 |
HOLD_DEMAND / HOLD_GROUND_SPEED | 0.05 / 0.015 | 需求与滤波后的地面速度均低于阈值才请求 HOLD。 |
GAIT_MIN_DWELL_TICKS | 12 | 步态变化的最短间隔刻数。 |
SPRINT_ENTER_DEMAND / SPRINT_EXIT_DEMAND | 0.60 / 0.42 | 普通疾跑需求的进入与维持阈值。 |
LEADER_SPRINT_ENTER_DEMAND / LEADER_SPRINT_EXIT_DEMAND | 0.32 / 0.18 | 跟随玩家疾跑时使用的阈值。 |
SPRINT_GAP_FACTOR / SPRINT_GAP_MIN / SPRINT_GAP_MAX | 0.08 / 0.7 / 1.6 | 进入距离为 clamp(sprintRange × 系数, 最小值, 最大值),单位为方块。 |
SPRINT_GAP_EXIT_FACTOR | 0.5 | 维持疾跑所需距离为进入距离的此比例。 |
SPRINT_ENTER_SUSTAIN_TICKS | 16 | 需求与距离条件须持续满足此刻数,才进入 SPRINT。 |
URGENT_DEMAND_FLOOR | 0.75 | 紧急状态提高目标需求,但仍受剩余距离上限约束。 |
MIN_TRAVEL_SPEED / SPRINT_ATTRIBUTE_BOOST | 0.15 / 1.3 | 实际行走倍率下限,以及疾跑时采用的除数。 |
有效步态为 HOLD、WALK 或 SPRINT。进入疾跑前,需求与距离必须同时满足条件;退出当前步态也受最短持续时间限制。HOLD 是表现结果,不是停止导航的命令:持有移动控制权的任务仍须在合适时清除行走目标或停止路径。
导航与缓存
导航使用两栖节点生成器,允许门路径、进入开门以及游泳;接受水路径类型,拒绝熔岩与开放路径类型。完整原版路径优先,否则尝试自定义后备路径;不完整的自定义结果只有在原版未返回路径时才会采用。
后备模式依据实际状态选择:飞行时使用空中路径;具备游泳能力且自身或目标在水中时尝试水中路径;其余使用地面路径。地面可达性直接调用原版与自定义地面搜索,避免可飞行的路线被误判为可以步行到达。
| 搜索 | 队列取出次数上限 | 起点/终点包围盒扩展 | 邻居 |
|---|---|---|---|
| 地面 | 2800 | 水平 36、垂直 8 方块 | 水平正向与对角方向,依次尝试垂直偏移 0, 1, -1, -2。 |
| 空中 | 4200 | 水平 48、垂直 24 方块 | 相邻的 26 个方向。 |
| 水中 | 3200 | 水平 36、垂直 12 方块 | 水平邻居的不同高度与纯垂直移动共同生成 26 个方向。 |
这些范围用于扩展起终点之间的包围盒,不是最大旅行距离。搜索耗尽时可能返回部分路径。地面候选要求脚下有支撑、脚部无水或熔岩、身体所跨区块已加载且有净空。空中候选要求已加载和净空;水中候选允许脚部或头部有水,也可采用能站立的节点。路径段以不超过 0.5 方块的间隔检查平移后的身体包围盒。
地面可达性在不足 20 刻且目标移动不超过 1.5 方块时复用结果;目标移动可提前绕过缓存。空中/水中路线在缺失、结束、模式改变、目标移动超过 1.5 方块、年龄超过 30 刻或下一段净空失效时重建。因此 30 刻是缓存年龄上限,不是搜索最短间隔。失败路线不做负结果缓存。
可直接通行时返回目标本身,无需路线。航点完成半径为空中 0.85 方块、水中 0.7 方块;选择时最多向前查看 7 个节点,寻找可直达的路径段。客户端航点查询直接返回请求目标。开始或停止普通导航会清除路线缓存。stopGroundNavigation() 只释放步行路径,在移动控制权交接时保留空中/水中路线。
POI 移动锁会阻止新导航。移动命令期间,不相关实体目标和距命令目标超过 1.5 方块的位置会被拒绝。专用跟随越障入口允许中间路径,但仍检查 POI 移动锁。
飞行阶段与起飞
阶段包括 GROUND、AIR_TAKING_OFF、AIR_FLYING、AIR_GLIDING、AIR_HOVERING 和 WATER_SWIMMING。空中阶段关闭重力并发布飞行/悬停标记;地面和游泳阶段恢复重力,控制器更新会清除坠落距离。死亡会清除手动飞行、路线、降落和障碍飞行状态。
飞行要求具备能力且没有战斗目标。独占地面越障会抑制普通飞行请求;远距离跟随救援可以释放该越障。可游泳的水体通常阻止飞行。请求可来自空中移动命令、手动开关、远距离玩家、越障恢复、已配置的脱离液体或特殊离水起飞。命令/锚点停留会抑制手动飞行及普通跟随飞行。
空中伴随识别正在飞行或滑翔的玩家。启用 preferFlight = true 或触发远距离跟随救援后,还会识别脚下超过 1.5 方块没有支撑面的移动;两种情况都要求启用 flyToOwner。玩家接触地面、攀爬或接触水时采用地面或游泳规则。NpcFollowerAirSpace 为队形与移动提供一致的目标规则:在玩家高度选择有净空的位置,到达后悬停,悬停时朝向玩家本人。飞行控制一直持续到下降结束:清除地面移动和跳跃输入,并阻止步行路径执行。BaseNpcEntity.turnHead 在空中保留控制器的身体朝向,不根据位移执行倒退行走的朝向修正。玩家回到地面后恢复降落判定;玩家起飞可以中断已开始的降落。
在配置的跟随范围内,preferFlight = false 会阻止普通跳跃和下落触发自动起飞。跟随任务在步行或确认路线阻塞期间保留地面控制,并在允许自动飞行前尝试梯子与跑酷;跟随范围内已开始的越障继续独占控制权。普通按距离触发的起飞也会拒绝仍可步行到达的目标。手动飞行、空中命令及离水行为继续使用各自的资格规则。
远距离跟随救援按 NPC 到玩家的实际三维距离判断,阈值为 NpcConfig.followRange,默认 32.0 方块。超出范围后,两种飞行偏好都允许起飞,不受普通等待、步行可达性否决和越障延迟限制。仍要求启用 flyToOwner、具备飞行能力、玩家存活且在同一世界、处于 FOLLOW、无命令停留及战斗目标,并且 NPC 清醒、未骑乘、未因对话暂停跟随。水中资格限制仍然生效;救援也可中断已开始的下降。传送恢复会先给飞行尝试 40 刻(2 秒),再使用现有的空闲条件判断。
普通跟随飞行还要求处于 FOLLOW、玩家存在、双方均未攀爬,并超出起飞距离。离开 GROUND 后使用停止距离。实现声明了 60 刻的液体等待,但更早的接触水且可游泳检查阻止该水中情况进入此分支;具备游泳能力但身处熔岩的 NPC 仍可能使用此值。
障碍飞行先解析地面命令目的地或队形位置;空中/水中命令及已到达的命令锚点不会请求此恢复。要求地面停滞、地面可达性失败,并且水平或垂直绝对距离超过阈值。候选落点还必须将水平距离加垂直绝对距离改善到指定幅度。锁存不会因接近目标而解除;着地、失去资格、持续停滞或达到上限都可清除它。
离水起飞要求 NPC 同时能游泳和飞行、处于水中,而跟随的玩家在上方且干燥、没有游泳;还须处于 FOLLOW、无目标且无命令停留。需要达到远距离跟随等待,或至少空闲 20 刻。起飞会提供向上和向前速度,并续期允许水中发布飞行状态的临时许可。若玩家正在水中游泳,通常会抑制飞行,除非满足命令或离水起飞例外。
| 常量 | 值 | 作用 |
|---|---|---|
FOLLOW_FLY_WARMUP_TICKS / FOLLOW_FLY_WARMUP_NO_SIGHT_TICKS | 30 / 45 | 远距离跟随起飞等待;看不见玩家时提高要求。 |
STUCK_ESCAPE_TICKS | 40 | 空闲计数达到此值后,远距离起飞等待降为 10 刻。 |
GROUND_STALL_TICKS / GROUND_BLOCKED_TAKEOFF_TICKS | 12 / 8 | 地面停滞阈值,以及之后连续无法地面到达的起飞判定刻数。 |
GROUND_BLOCKED_MIN_GAP / FLIGHT_MIN_GAIN | 1.75 / 1.5 | 障碍起飞的距离阈值,以及水平距离加垂直绝对距离所需的改善量。 |
OBSTACLE_FLIGHT_MAX_TICKS / OBSTACLE_FLIGHT_COOLDOWN_TICKS | 300 / 60 | 障碍飞行锁存上限,以及完成降落后的冷却刻数。 |
FLIGHT_ROUTE_REFRESH_TICKS / FLIGHT_ROUTE_RETRY_TICKS | 30 / 30 | 落点缓存时长与失败后的重试等待;锚点移动可提前使已有落点失效。 |
FLIGHT_ROUTE_ANCHOR_TOLERANCE_SQ / FLIGHT_PAD_RADIUS | 3.0 × 3.0 / 4 | 锚点容差平方,以及路线落点的环形搜索半径,单位为方块。 |
FLIGHT_APPROACH_HEIGHT | 0.6 | 落点上方的航点偏移,单位为方块。 |
POSITION_GAIN / RESPONSE | 0.16 / 0.5 | NpcArrivalMotion 的期望速度增益与速度插值系数,到达速度没有下限。 |
LANDING_HANDOFF_RANGE_SQ / LANDING_HANDOFF_MIN_HEIGHT / LANDING_HANDOFF_MAX_HEIGHT | 0.55 × 0.55 / 0.0 / 3.0 | 转入降落须满足水平距离及相对落点的有符号高度区间。 |
FACING_MIN_HORIZONTAL_SQ / FLIGHT_TURN_RATE | 0.5 × 0.5 / 12.0 | 忽略更短的水平朝向向量;否则按此最大度数/刻转向。 |
FLIGHT_PROGRESS_EPS_SQ / FLIGHT_STUCK_TICKS | 0.05 × 0.05 / 20 | 位移不足并持续此刻数时,使空中路线失效并进入停滞处理。 |
FLIGHT_STALL_HOVER_TICKS / HOVER_LANDING_TIMEOUT_TICKS | 10 / 40 | 连续停滞调用触发悬停;降落判定在持续悬停后扩大落点搜索。 |
LANDING_PAD_REFRESH_TICKS / LANDING_PAD_RADIUS / LANDING_PAD_WIDE_RADIUS | 20 / 3 / 6 | 落点缓存时长和普通/扩大半径;锚点变化或搜索扩大可绕过缓存时长。 |
LANDING_DESCENT_SPEED / LANDING_HORIZONTAL_SPEED | -0.20 / 0.30 | 无落点下降速度与最大水平接近速度,方块/刻。 |
LANDING_APPROACH_LERP / LANDING_ALIGNED_RANGE | 0.35 / 0.85 | 水平插值与对齐半径,单位为方块。 |
LANDING_ALIGN_TIMEOUT_TICKS | 60 | 降落达到此刻数后,即使未完成对齐也继续下降。 |
FLIGHT_ARRIVAL_RANGE / RELEASE_PADDING | 0.45 / 0.55 | 直达飞行在到达半径内悬停,恢复移动要求超出到达半径加释放余量。 |
LANDING_TURN_RATE / LANDING_FACING_HOLD_TICKS / LANDING_SETTLE_SPEED_SQ | 15.0 / 12 / 0.06 × 0.06 | 面向玩家的转向速率、着地后维持刻数,以及取消维持的水平速度阈值。 |
WATER_TAKEOFF_MIN_VERTICAL_GAP / WATER_TAKEOFF_WARMUP_TICKS / WATER_TAKEOFF_FLIGHT_ALLOW_TICKS | 3.0 / 12 / 24 | 玩家在上方的高度差、远距离跟随等待,以及可续期的水中飞行许可刻数。 |
WATER_TAKEOFF_UPWARD_VELOCITY / WATER_TAKEOFF_FORWARD_VELOCITY | 0.36 / 0.12 | 起飞初始最小向上速度与附加向前速度,方块/刻。 |
目的地飞行与降落
飞往地面目的地时保存落点和解析时使用的锚点。空中伴随、手动飞行、空中命令、有效的离水起飞及脱离液体直接使用航点转向。已有目的地只有在不足 30 刻且锚点移动不超过 3.0 方块时才复用;任一条件失效即可重新搜索。刷新失败会保留已有路线;首次解析失败等待 30 刻后重试。
对于静止目的地和路线航点,NpcArrivalMotion 根据剩余距离将期望速度逐渐降至零,并逐刻混合速度。每步位移同时受最大速度和剩余距离限制。悬停到达判定使用最终目的地,与路线航点完成判定分开;明确的跟随停留不会回退到旧步行目标。航点有足够水平偏移时,移动朝向跟随水平速度,并保留有界转向速率。
NpcFlightFollowMotion 每个世界刻只采样一次玩家位置,包含垂直移动。速度至少为 0.02 方块/刻时持续追随,连续静止 8 刻(0.4 秒)后才允许保持位置。玩家速度使用 0.35 滤波;采样间隔超过 5 刻或位移超过 8.0 方块时重置估计。移动追随会跳过凝聚及到达停留。
无障碍直达伴随将采样的玩家速度加入位置误差修正,转向加速度上限为 0.06 方块/刻²。伴随目的地按 0.35 混合,变化超过 8.0 方块时重置。滤波后的目的地须通过净空检查;路线航点不做插值,障碍绕行使用普通到达转向,不叠加玩家速度。
NpcLandingResolver 从近到远按环搜索,在首个有可用位置的环内,以水平距离平方加垂直绝对差 × 1.75 评分。每列从 floor(anchorY) 开始向下检查最多 10 个支撑方块,按碰撞形状确定表面高度,包括非整方块。要求支撑与脚部区块已加载、支撑和脚部无液体且身体有净空。下降通道每隔 0.5 方块检查,最多检查落点上方 24 方块,负高度裁剪为 0;这是有界净空检查,不代表完整飞行路线一定存在。
路线通过实时空中航点接近落点上方 0.6 方块。转入滑降须满足表中的水平范围与有符号高度区间。选定落点只要支撑和身体净空仍然有效就保持固定。滑降在水平对齐期间暂停垂直运动,对齐后下降;超过 60 刻,即使未对齐也下降。下降速度为 -min(0.20, max(0.035, heightAbovePad × 0.2)) 方块/刻。对齐不会请求向上冲量。
解析到落点后的下降具有持续性。空中伴随、远距离跟随救援、深水/自由游泳或水中命令可以中断它,普通的新飞行请求不能。实际接触地面才完成降落。着地后停止导航、恢复重力、清零水平速度并去除正的垂直速度。跟随者按有界的着地转向速率朝向玩家,执行命令的 NPC 保留移动朝向。水平运动超过阈值时,着地朝向维持会提前结束。
航点缺失会衰减速度并累计停滞。进度监测也会在位移不足时使路线失效;但成功转向的每刻会在监测前重置连续停滞计数,所以仅反复触发进度监测并不保证能累积到悬停阈值。降落判定在持续悬停后扩大落点搜索,即使没有落点也最终进入垂直下降;新的飞行请求仍可能改变阶段,因此这不是总滞空时间的无条件上限。
游泳与表现
游泳要求设置启用并具备能力,同时处于浸没/深水,或水中命令已接触水。未在地面的既有游泳阶段可在液体高度至少为 0.35 时继续。仅有脚部浅水接触不会强制进入自由游泳。
命令目标优先。已到达的水中停留没有移动目标。普通跟随游泳在 SwimSettings.followStopRange 内停止选择目标;玩家在干燥地面时,控制器每隔 20 刻探测附近陆地,并将其余跟随停止距离收紧到最多 1.0 方块。水中航点搜索失败会回退到原始请求目标。
主动游泳是独立的已跟踪动作状态:进入使用 NpcConfig.swimRange,维持使用 swimStopRange,判定改变须持续 3 刻。非正阈值采用 0.2 方块。目标清除时该状态也会清除;此状态本身不会停止转向计算。
基础实体的游泳视觉另有迟滞规则:
| 视觉规则 | 值 / 行为 |
|---|---|
| 水高进入 / 维持 | 0.5 / 0.12 方块,同时检查浸没、游泳与命令状态。 |
| 进入稳定期 | 2 刻;水中命令使用 1。 |
| 最短视觉维持 | 30 刻;持续接触会续期到至少 15 刻。 |
| 退出稳定期 | 最短维持到期后再经过 12 刻。 |
| 立即退出 | 失去能力、飞行、骑乘、睡眠、死亡,或已落地且未没入水中、水深低于0.12格。 |
水接触还会在实体所在列的 0.05、0.45、0.95 高度偏移采样,已处于游泳姿势时增加 -0.15。视觉状态决定采用 SWIMMING 还是 STANDING 姿势。水中移动输入会除以移动属性,分母下限为 1.0E-5。
上岸辅助要求水平距离不超过 1.65、垂直差在 -0.75–1.5 方块内且目标位置无液体。它触发跳跃,增加 0.035 水平和 0.08 垂直速度,并使向上输入至少为缩放后游泳速度的 0.7。
水中游荡可将方块目标高度改为 NPC 当前高度,前提是该水中位置有净空,且处于 WANDER/WANDER_BY_RADIUS、没有命令/锚点并且未飞行。到达判定至少使用 2.0 方块水平范围,并要求目标列有水。资格检查已要求接触水,因此当前成功分支不会执行辅助方法后面的 3.0 垂直后备限制。
约定
NPC 配置
下列为 NpcConfig 的 Java 记录/构造器字段,不是数据包 JSON。其他战斗、队伍与情绪字段由对应系统说明。记录构造器没有执行未列出的数值裁剪,不应假设任意值或非有限值会被校验。
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
walkSpeed | double | 1.0 | 地面导航倍率;求解器至少使用 0.15,构造器本身不裁剪。 |
sprintSpeed | double | 1.35 | 插值后的导航倍率上限;求解器会将其提高到不低于实际 walkSpeed。 |
sprintRange | double | 12.0 | 单位为方块;命令和战斗的紧急移动要求此值为正且距离达到该值,也用于步态距离阈值。 |
swimRange | double | 3.0 | 主动游泳动作的进入距离,单位为方块;构造时至少为 0。 |
swimStopRange | double | 1.6 | 主动游泳动作的退出距离,裁剪到 0–swimRange。 |
followRange | double | 32.0 | 跟随任务范围,单位为方块;队形与牵引范围策略可能进一步收窄。 |
wanderRange | double | 8.0 | 游荡半径配置,单位为方块。 |
stepHeight | double | 1.25 | 单位为方块;构造时裁剪到 0.5–2.0,实际跨步高度仍不会低于传入的原版高度。 |
骑乘或飞行时,跨步辅助返回传入的原版高度。能游泳且接触水的 NPC 至少使用 1.0;其他地面移动至少使用配置的 stepHeight。跳跃触发会将所需抬升与实际跨步高度加共享碰撞容差比较。
飞行设置
NpcConfig.flightSettings 为每名 NPC 提供不可变的飞行设置。构建器默认使用 NpcMoveControl.FlightSettings.defaultSettings(),null 也会归一化为此默认值。NpcMoveControl.FlightSettings.defaultSettings() 提供下表默认值。规范构造器与 createSettings 直接存储输入,不检查范围或是否为有限数;控制器将距离范围转换为平方值。
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
flyToOwner | boolean | true | 允许远距离跟随飞行和离水起飞判定。 |
preferFlight | boolean | false | 优先主动飞行而非地面路线与越障规划,并启用对无支撑玩家的空中伴随;超出 followRange 时,两种偏好都适用距离救援。 |
flyOutOfFluid | boolean | false | 身处水或熔岩且不能使用游泳时请求脱离液体。 |
followStartRange | double | 12.0 | 地面起飞距离,单位为方块,使用平方距离比较。 |
followStopRange | double | 4.0 | 不处于 GROUND 时继续跟随飞行的距离,使用平方值比较。 |
maxGlideDropRange | double | 6.0 | 向下搜索备用落点或地面的距离,单位为方块。 |
hoverRadius | double | 3.0 | 找不到落点后选择悬停时使用的近距离阈值。 |
flightSpeed | float | 2.0 | 在到达减速之前乘以移动属性。 |
具备飞行能力的角色可在其 Java NPC 配置中启用此偏好。单独设置 preferFlight 不会赋予飞行能力:
NpcConfig config = NpcConfig.builder()
.flightSettings(NpcMoveControl.FlightSettings.defaultSettings().withPreferFlight(true))
.build();
此开关属于实体的 Java 配置,不写入 NBT,也不是全局客户端选项。
游泳设置
NpcMoveControl.SwimSettings.defaultSettings() 提供下表默认值,构造器与工厂也不做输入校验。disabled() 按字段顺序使用 false, false, 0, 0, 1.0, 1.0, 0.0。
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
enabled | boolean | true | 还必须通过 canNpcSwim() 能力检查。 |
buoyant | boolean | true | 接触水时每刻为垂直速度增加 0.005 方块/刻。 |
pitchChange | int | 45 | 默认将俯仰目标裁剪到 ±45 度;实际转向速率为 5 度/刻。 |
yawChange | int | 45 | 每刻最大偏航变化,单位为度。 |
speedInWater | float | 0.1 / 0.3 | 水中移动速度倍率;默认值为声明的玩家与 NPC 基础倍率之比。 |
speedInAir | float | 1.0 | 未接触水时使用的备用移动速度倍率。 |
followStopRange | double | 4.0 | 停止选择游泳跟随目标的距离,与主动游泳动作的迟滞阈值独立。 |
控制权与持久化
控制器、需求历史、路线缓存、起飞计数和落点仅存在于运行时。飞行、悬停、主动游泳和游泳视觉使用实体跟踪标记,普通疾跑状态通过实体设置。不要通过保存控制器的临时阶段来替代实体生命周期。
移动修改应在服务端 AI 执行流程中进行。requestGroundTraversal() 声明本控制刻的控制权,在下次更新时消费,并按需恢复地面与重力标记;越障持续拥有控制权时需要持续声明。faceGroundDirection() 执行受限的水平转向,并在该控制刻内维持。setManualFlight(false) 仅取消手动请求,不会传送或立刻将 NPC 强制置于地面。
扩展方式
安装自定义 NpcMoveControl 时构造相应设置,并覆盖能力方法限制物种行为。新增地面移动控制者应在拥有控制权的每刻更新并应用共享步态,普通停留时放松,切换控制权时重置。路径目标刷新应与每刻速度发布分开处理。
使用 canReachOnGround 判断步行可达性,空中与水中移动使用各自航点方法。处理空值、部分路径和目标变化;有界搜索不能保证完整路线。通过实体取得跨步高度,不另设一套跳跃阈值。
验证
分别测试慢走、持续疾跑、短暂疾跑、到达与恢复采样,同时观察导航速度和疾跑标记。检查地面命令与战斗在配置距离附近的紧急移动,再检查梯子/跑酷接管时的控制权变化。
测试步行、骑乘、飞行和离水时的跨步变化。飞行场景应包括近距离不可达台阶、远距离玩家、天花板、移动目的地、无有效落点、略高/略低的落点,以及远离玩家的命令降落;除了成功到达,还要检查缓存失效。
水中测试浅水接触、深度变化、水中到达停留、水面上方的干燥玩家及上岸。分别观察阶段、主动游泳标记和视觉姿势。重新加载 NPC 后应重建运行时移动状态,而非恢复保存的路线。