Player/BallInput.cs
using System;
/// <summary>
/// 输入辅助:**仅键盘 + 手柄**(2026-09-05 用户决策,鼠标操控已移除)。
/// 数据源 Input.AnalogMove——引擎已把 WASD(模板 Input.config 预定义的
/// Forward/Backward/Left/Right)与手柄左摇杆叠加为 x=前(+)/后(-)、y=左(+)/右(-)。
/// 顶视相机下:前 → 相机 Up(屏幕上),左 → -相机 Right(屏幕左)。
/// M2 起真人客户端本地计算(owner 模拟),bot 由 BotBrain 直接给方向,不走这里。
/// </summary>
public static class BallInput
{
/// <summary> 摇杆死区(防模拟量中心漂移) </summary>
const float DeadZone = 0.2f;
/// <summary>
/// 是否正在文本输入(聊天框/输入框持有键盘焦点)——此时任何裸 Input 读取都必须让位。
/// 根因(已对引擎源码核实):`Input.Actions.OnButton` 把按键**无差别累积进所有 InputContext**
/// (sbox-public/engine/Sandbox.Engine/Systems/Input/Input.Actions.cs:229-232),
/// 聊天框也不设 `Input.Suppressed`,所以 `Input.Keyboard.Pressed("P")` 在打字时同样为真
/// → 聊天里打 "P" 会直接退局。
/// 判定用 `Sandbox.UI.InputFocus.Current`(引擎 UI 焦点面板;无输入框聚焦时为 null)。
/// 注意:更贴近的 `InputRouter.KeyboardFocusPanel` 是 engine internal,游戏程序集不可见。
/// </summary>
public static bool Typing
{
get
{
try
{
return Sandbox.UI.InputFocus.Current is not null;
}
catch
{
// UI 系统未就绪(启动早期/无面板)——按"没在打字"处理,绝不每帧抛异常
return false;
}
}
}
public static Vector2 GetMoveDir( CameraComponent camera )
{
if ( !camera.IsValid() ) return Vector2.Zero;
var analog = Input.AnalogMove;
if ( analog.Length < DeadZone ) return Vector2.Zero;
var rot = camera.GameObject.WorldRotation;
var world = rot * ( Vector3.Right * ( -analog.y ) + Vector3.Up * analog.x );
var v = new Vector2( world.x, world.y );
var len = v.Length;
return len > 0.001f ? new Vector2( v.x / len, v.y / len ) : Vector2.Zero;
}
/// <summary>
/// 瞄准方向(v0.7.0.0 用户需求):光标世界位置相对球的单位方向——分裂/吐孢子指向这里。
/// 正交俯视相机下 ScreenPixelToRay 的射线原点 XY 就是光标的世界坐标。
/// 光标正贴球心时返回 Zero(调用方回退移动方向)。
/// </summary>
public static Vector2 GetAimDir( CameraComponent camera, Vector3 ballPos )
{
if ( !camera.IsValid() ) return Vector2.Zero;
var ray = camera.ScreenPixelToRay( Mouse.Position );
var dx = ray.Position.x - ballPos.x;
var dy = ray.Position.y - ballPos.y;
var len = MathF.Sqrt( dx * dx + dy * dy );
if ( len < 0.001f ) return Vector2.Zero;
return new Vector2( dx / len, dy / len );
}
/// <summary>
/// 光标世界坐标(v0.7.8.7 手感):分裂/吐孢子传"点"不传"方向"——host 对主球和每个分身
/// 各自算指向(分身也朝光标吐,agar 标准),正贴某颗身体时该身体回退移动方向。
/// </summary>
public static Vector2 GetAimPoint( CameraComponent camera )
{
if ( !camera.IsValid() ) return Vector2.Zero;
var ray = camera.ScreenPixelToRay( Mouse.Position );
return new Vector2( ray.Position.x, ray.Position.y );
}
}