using System;
using System.Collections.Generic;
using Network.Defines;
using UnityEngine;
using Vector3 = UnityEngine.Vector3;
///
/// 输入源接口,用于解耦输入捕获
///
public interface IInputSource
{
Vector3 GetPlanarInput();
bool ConsumeShootInput();
Vector3 GetAimDirection();
}
///
/// 真实的 Unity 输入源
///
public class UnityInputSource : IInputSource
{
private readonly Transform _cameraTransform;
public UnityInputSource(Transform cameraTransform)
{
_cameraTransform = cameraTransform;
}
public Vector3 GetPlanarInput()
{
return new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical"));
}
public bool ConsumeShootInput()
{
return Input.GetMouseButtonDown(0);
}
public Vector3 GetAimDirection()
{
if (_cameraTransform != null)
{
return _cameraTransform.forward;
}
return Vector3.forward;
}
}
///
/// 模拟输入源(测试用),提供预设的输入序列
///
public class SimulatedInputSource : IInputSource
{
private readonly (float turn, float throttle)[] _inputSequence;
private int _index;
private Vector3 _lastAimDirection = Vector3.forward;
private bool _shootTriggered;
public SimulatedInputSource((float turn, float throttle)[] sequence)
{
_inputSequence = sequence;
_index = 0;
}
public Vector3 GetPlanarInput()
{
if (_index >= _inputSequence.Length)
{
return Vector3.zero;
}
var (turn, throttle) = _inputSequence[_index];
return new Vector3(turn, 0f, throttle);
}
public bool ConsumeShootInput()
{
if (_shootTriggered)
{
_shootTriggered = false;
return true;
}
return false;
}
public Vector3 GetAimDirection()
{
return _lastAimDirection;
}
///
/// 推进到下一个输入
///
public void Advance()
{
if (_index < _inputSequence.Length)
{
_index++;
}
}
///
/// 是否还有更多输入
///
public bool HasMore => _index < _inputSequence.Length;
///
/// 设置射击触发(下次 ConsumeShootInput 返回 true)
///
public void SetShootTriggered()
{
_shootTriggered = true;
}
///
/// 设置瞄准方向
///
public void SetAimDirection(Vector3 direction)
{
_lastAimDirection = direction;
}
///
/// 获取当前输入索引
///
public int CurrentIndex => _index;
}
///
/// 输入组件,负责从 IInputSource 获取输入、发送 MoveInput 到服务器、管理 tick
/// Update() 是唯一调用 SendMoveInput / SendShootInput 的地方
///
public class InputComponent : MonoBehaviour
{
[SerializeField] private string _playerId = "player";
[SerializeField] private float _sendInterval = 0.05f; // 50ms 发送间隔
private IInputSource _inputSource;
private Vector3 _currentInput;
private Vector3 _lastAimDirection = Vector3.forward;
private float _lastSendTime;
private bool _stopMessagePending;
private bool _wasMovingLastFrame;
private long _tick;
public event System.Action OnMoveInputCreated;
public event System.Action OnShootInputCreated;
public long CurrentTick => _tick;
///
/// 设置玩家 ID(由 MovementComponent.Init 调用)
///
public void InjectPlayerId(string playerId)
{
_playerId = playerId;
}
private void Awake()
{
var camera = Camera.main;
_inputSource = new UnityInputSource(camera?.transform);
}
///
/// 设置自定义输入源(用于替换默认的 UnityInputSource)
///
public void SetInputSource(IInputSource source)
{
_inputSource = source ?? throw new ArgumentNullException(nameof(source));
}
///
/// 获取当前输入源(用于测试)
///
public IInputSource GetInputSource()
{
return _inputSource;
}
///
/// 重置 tick(用于测试)
///
public void ResetTick(long tick = 0)
{
_tick = tick;
}
private void Update()
{
// 从输入源获取输入
_currentInput = _inputSource?.GetPlanarInput() ?? Vector3.zero;
// 检测移动状态变化
var hasMovement = ClientGameplayInputFlow.HasPlanarInput(_currentInput);
if (hasMovement)
{
_stopMessagePending = false;
}
else if (_wasMovingLastFrame)
{
_stopMessagePending = true;
}
_wasMovingLastFrame = hasMovement;
// 处理射击输入
var shootInput = GetShootInput();
if (shootInput != null && NetworkManager.Instance != null)
{
NetworkManager.Instance.SendShootInput(shootInput);
OnShootInputCreated?.Invoke(shootInput);
}
// 定期发送移动输入
if (Time.time - _lastSendTime > _sendInterval)
{
SendMoveInput();
}
}
private void SendMoveInput()
{
if (!ClientGameplayInputFlow.TryCreateMoveInput(_playerId, _tick, _currentInput,
_stopMessagePending, out var moveInput))
{
return;
}
if (NetworkManager.Instance != null)
{
NetworkManager.Instance.SendMoveInput(moveInput);
}
OnMoveInputCreated?.Invoke(moveInput);
_stopMessagePending = false;
_lastSendTime = Time.time;
_tick++;
}
private ShootInput GetShootInput()
{
var shootTriggered = _inputSource?.ConsumeShootInput() ?? false;
var aimDirection = _inputSource?.GetAimDirection() ?? Vector3.forward;
if (!shootTriggered)
{
return null;
}
var planarForward = Vector3.ProjectOnPlane(aimDirection, Vector3.up);
if (ClientGameplayInputFlow.HasPlanarInput(planarForward))
{
_lastAimDirection = planarForward;
}
return ClientGameplayInputFlow.CreateShootInput(_playerId, _tick, _lastAimDirection);
}
}