统一使用新输入系统
This commit is contained in:
@@ -7,18 +7,20 @@ using UnityEngine.InputSystem;
|
||||
namespace Agame
|
||||
{
|
||||
/// <summary>
|
||||
/// 玩家移动控制 —— FishNet 客户端授权模式。
|
||||
/// 参考:Docs/联机方案选型.md §3.3,以及官方教程 "Moving Your Player Around"。
|
||||
/// 玩家移动控制 —— FishNet 客户端授权 + Input System 的 PlayerInput / Actions 方案。
|
||||
///
|
||||
/// 职责边界:本组件只负责「读输入 + 驱动 CharacterController」。
|
||||
/// 位置/旋转的同步交给 Player 预制体上的 NetworkTransform(Client Authoritative),
|
||||
/// 这里不写任何 RPC / SyncVar。
|
||||
/// 输入链路:
|
||||
/// Assets/_Game/Input/PlayerControls.inputactions
|
||||
/// → Player 根节点上的 PlayerInput 组件(仅本地拥有者启用)
|
||||
/// → 本脚本在 Update 里读取 Move / Sprint / Jump
|
||||
///
|
||||
/// FishNet 4.7.3 下的三条硬约束:
|
||||
/// 1. 必须继承 NetworkBehaviour 而非 MonoBehaviour,否则拿不到 IsOwner 与网络生命周期回调。
|
||||
/// 2. IsOwner 禁止在 Awake / Start / OnStartServer / OnStartNetwork 里访问 ——
|
||||
/// FishNet 代码生成器会直接抛编译错误,按拥有者初始化的逻辑要放在 OnStartClient。
|
||||
/// 3. 只有拥有者能驱动自己的对象,少了这道判断就会去移动别人的玩家。
|
||||
/// 本脚本同时兼任「输入与相机的所有权开关」,见 ApplyOwnership 的注释。
|
||||
///
|
||||
/// FishNet 4.7.3 的三条硬约束(与输入方案无关,继续沿用):
|
||||
/// 1. 必须继承 NetworkBehaviour,否则拿不到 IsOwner 与网络生命周期回调。
|
||||
/// 2. IsOwner 禁止在 Awake / Start / OnStartServer / OnStartNetwork 中访问 ——
|
||||
/// FishNet 的 ILPP 会直接报编译错误。
|
||||
/// 3. 只有拥有者能驱动自己的对象,少了 IsOwner 判断就会去移动别人的玩家。
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(CharacterController))]
|
||||
public class PlayerController : NetworkBehaviour
|
||||
@@ -33,16 +35,36 @@ namespace Agame
|
||||
[SerializeField, Min(0f)] private float jumpHeight = 1.5f;
|
||||
|
||||
[Header("Ownership")]
|
||||
[Tooltip("仅本地拥有者需要启用的对象(玩家相机等)。非拥有者会被自动禁用,避免多相机抢占渲染。")]
|
||||
[Tooltip("仅本地拥有者需要启用的对象(玩家相机等)。非拥有者会自动禁用,避免多相机抢占渲染。")]
|
||||
[SerializeField] private GameObject[] ownerOnlyObjects;
|
||||
|
||||
private CharacterController _controller;
|
||||
private PlayerInput _playerInput;
|
||||
|
||||
private InputAction _moveAction;
|
||||
private InputAction _jumpAction;
|
||||
private InputAction _sprintAction;
|
||||
|
||||
private Vector3 _verticalVelocity;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Awake 中不得访问 IsOwner,这里只做纯本地缓存。
|
||||
_controller = GetComponent<CharacterController>();
|
||||
_playerInput = GetComponent<PlayerInput>();
|
||||
|
||||
if (_playerInput == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"[{nameof(PlayerController)}] {name} 上找不到 PlayerInput 组件,输入将不可用。" +
|
||||
$"请在玩家预制体上加 PlayerInput,并把 Actions 指向 {PlayerInputNames.AssetFileName}。",
|
||||
this);
|
||||
return;
|
||||
}
|
||||
|
||||
_moveAction = PlayerInputNames.Resolve(_playerInput, PlayerInputNames.Move, this);
|
||||
_jumpAction = PlayerInputNames.Resolve(_playerInput, PlayerInputNames.Jump, this);
|
||||
_sprintAction = PlayerInputNames.Resolve(_playerInput, PlayerInputNames.Sprint, this);
|
||||
}
|
||||
|
||||
public override void OnStartClient()
|
||||
@@ -59,47 +81,39 @@ namespace Agame
|
||||
{
|
||||
base.OnOwnershipClient(prevOwner);
|
||||
|
||||
// 所有权可能中途变更(例如将来的主机迁移),相机等要跟着切换。
|
||||
// 所有权可能中途变更(例如将来的主机迁移),输入与相机要跟着切换。
|
||||
ApplyOwnership();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 只驱动本地拥有者,否则每个客户端都会去移动别人的玩家。
|
||||
// 只驱动本地拥有者。
|
||||
if (!IsOwner)
|
||||
return;
|
||||
|
||||
Keyboard kb = Keyboard.current;
|
||||
if (kb == null)
|
||||
return;
|
||||
Vector2 moveInput = _moveAction != null ? _moveAction.ReadValue<Vector2>() : Vector2.zero;
|
||||
Vector3 moveDirection = BuildMoveDirection(moveInput);
|
||||
|
||||
float speed = kb.leftShiftKey.isPressed ? sprintSpeed : moveSpeed;
|
||||
Vector3 moveDirection = ReadMoveDirection(kb);
|
||||
bool isSprinting = _sprintAction != null && _sprintAction.IsPressed();
|
||||
float speed = isSprinting ? sprintSpeed : moveSpeed;
|
||||
|
||||
_controller.Move(moveDirection * (speed * Time.deltaTime));
|
||||
|
||||
UpdateJumpAndGravity(kb);
|
||||
bool jumpPressed = _jumpAction != null && _jumpAction.WasPressedThisFrame();
|
||||
UpdateJumpAndGravity(jumpPressed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取 WASD 并转换为相对于角色自身朝向的方向(body 的偏航由 MouseLook 负责)。
|
||||
/// 斜向输入做归一化,避免对角线移动比正向更快。
|
||||
/// 把 Move 的二维输入转换为相对于角色自身朝向的三维方向
|
||||
/// (body 的偏航由 MouseLook 负责)。斜向输入做归一化,避免对角线移动比正向更快。
|
||||
/// </summary>
|
||||
private Vector3 ReadMoveDirection(Keyboard kb)
|
||||
private Vector3 BuildMoveDirection(Vector2 moveInput)
|
||||
{
|
||||
float moveX = 0f;
|
||||
float moveZ = 0f;
|
||||
|
||||
if (kb.aKey.isPressed) moveX -= 1f;
|
||||
if (kb.dKey.isPressed) moveX += 1f;
|
||||
if (kb.sKey.isPressed) moveZ -= 1f;
|
||||
if (kb.wKey.isPressed) moveZ += 1f;
|
||||
|
||||
Vector3 direction = transform.right * moveX + transform.forward * moveZ;
|
||||
Vector3 direction = transform.right * moveInput.x + transform.forward * moveInput.y;
|
||||
return Vector3.ClampMagnitude(direction, 1f);
|
||||
}
|
||||
|
||||
private void UpdateJumpAndGravity(Keyboard kb)
|
||||
private void UpdateJumpAndGravity(bool jumpPressed)
|
||||
{
|
||||
bool isGrounded = _controller.isGrounded;
|
||||
|
||||
@@ -107,7 +121,7 @@ namespace Agame
|
||||
if (isGrounded && _verticalVelocity.y < 0f)
|
||||
_verticalVelocity.y = -2f;
|
||||
|
||||
if (isGrounded && kb.spaceKey.wasPressedThisFrame)
|
||||
if (isGrounded && jumpPressed)
|
||||
{
|
||||
// 由目标跳跃高度反推初速度:v = sqrt(2 * g * h)
|
||||
_verticalVelocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
||||
@@ -118,11 +132,18 @@ namespace Agame
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按所有权开关"只属于本地玩家"的对象。最典型的是玩家相机:
|
||||
/// 若每个客户端的相机都常驻启用,N 个玩家就会互相抢渲染。
|
||||
/// 按所有权统一开关「只属于本地玩家」的东西:PlayerInput 与玩家相机。
|
||||
///
|
||||
/// 集中放在这里的原因:这两样都是「一个客户端只该有一份生效」。
|
||||
/// 散到各脚本里各自 enable 容易漏,而且会出现顺序问题 ——
|
||||
/// PlayerCamera 上的 MouseLook 在 OnEnable 里就要读 Look 动作,
|
||||
/// 所以必须先开 PlayerInput、再启用相机,否则第一帧读不到值。
|
||||
/// </summary>
|
||||
private void ApplyOwnership()
|
||||
{
|
||||
if (_playerInput != null)
|
||||
_playerInput.enabled = IsOwner;
|
||||
|
||||
if (ownerOnlyObjects == null)
|
||||
return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user