移动同步
This commit is contained in:
@@ -21,8 +21,8 @@ MonoBehaviour:
|
||||
- {fileID: 201277550, guid: 5b712878ecece354ba4ffb026c0a221c, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: b8017cef39731ba439c70fecc09488e3, type: 3}
|
||||
- {fileID: 201277550, guid: 26a567abbe21227428f5c3d309d1d73c, type: 3}
|
||||
- {fileID: -8731164649411638372, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: 44611030e61220d42ab7c37ba3c0ea92, type: 3}
|
||||
- {fileID: 8192566354860284824, guid: 6331b3542e64a564c81bc39cedf70c8d, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: f32d4c19de900e64cb73cedcb8ba6f70, type: 3}
|
||||
- {fileID: 611616139817875448, guid: bf5f023b4017a5e41a9815ec5745df3d, type: 3}
|
||||
- {fileID: -8731164649411638372, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 246f31a0e00fea74a93125fec6d80da8
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.7.3R
|
||||
assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln
|
||||
uploadId: 987062
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ec83424c5410b04aab06dddca60e7f6
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.7.3R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj
|
||||
uploadId: 987062
|
||||
@@ -18,7 +18,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
m_IsActive: 0
|
||||
--- !u!4 &1098286384477188469
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -160,6 +160,7 @@ GameObject:
|
||||
- component: {fileID: 3905363232486332009}
|
||||
- component: {fileID: -8731164649411638372}
|
||||
- component: {fileID: -1605824257395221361}
|
||||
- component: {fileID: -1778631990423174556}
|
||||
m_Layer: 0
|
||||
m_Name: Player
|
||||
m_TagString: Player
|
||||
@@ -277,10 +278,15 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: b6780c1c0b9d27c46a0bd43f0d2291f6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Agame.PlayerController
|
||||
_componentIndexCache: 0
|
||||
_addedNetworkObject: {fileID: -8731164649411638372}
|
||||
_networkObjectCache: {fileID: -8731164649411638372}
|
||||
moveSpeed: 5
|
||||
sprintSpeed: 8
|
||||
gravity: -20
|
||||
jumpHeight: 1.5
|
||||
ownerOnlyObjects:
|
||||
- {fileID: 6595615933852889331}
|
||||
--- !u!114 &-8731164649411638372
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -327,7 +333,9 @@ MonoBehaviour:
|
||||
<PredictedSpawn>k__BackingField: {fileID: 0}
|
||||
<PredictedOwner>k__BackingField: {fileID: 0}
|
||||
NetworkBehaviours:
|
||||
- {fileID: 3905363232486332009}
|
||||
- {fileID: -1605824257395221361}
|
||||
- {fileID: -1778631990423174556}
|
||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
||||
InitializedNestedNetworkObjects: []
|
||||
RuntimeParentNetworkBehaviour: {fileID: 0}
|
||||
@@ -338,7 +346,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584119494375252787
|
||||
_initializedTimestamp: -8584117169363086877
|
||||
--- !u!114 &-1605824257395221361
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -351,6 +359,52 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 8a6a39c46bf52104ba8efe3100bce3f7, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: FishNet.Runtime::FishNet.Object.EmptyNetworkBehaviour
|
||||
_componentIndexCache: 0
|
||||
_componentIndexCache: 1
|
||||
_addedNetworkObject: {fileID: -8731164649411638372}
|
||||
_networkObjectCache: {fileID: -8731164649411638372}
|
||||
--- !u!114 &-1778631990423174556
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7344258574652498121}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: FishNet.Runtime::FishNet.Component.Transforming.NetworkTransform
|
||||
_componentIndexCache: 2
|
||||
_addedNetworkObject: {fileID: -8731164649411638372}
|
||||
_networkObjectCache: {fileID: -8731164649411638372}
|
||||
_componentConfiguration: 0
|
||||
_synchronizeParent: 0
|
||||
_packing:
|
||||
Position: 1
|
||||
Rotation: 1
|
||||
Scale: 0
|
||||
_useScaledTime: 1
|
||||
_interpolation: 2
|
||||
_extrapolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
_clientAuthoritative: 1
|
||||
_sendToOwner: 1
|
||||
_interval: 1
|
||||
_synchronizePosition: 1
|
||||
_positionSensitivity: 0.001
|
||||
_positionSnapping:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
_synchronizeRotation: 1
|
||||
_rotationSnapping:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
_synchronizeScale: 1
|
||||
_scaleSensitivity: 0.001
|
||||
_scaleSnapping:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
|
||||
@@ -150,6 +150,71 @@ Transform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1001 &383713395
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: -8731164649411638372, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: SceneId
|
||||
value: 2952396223
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8731164649411638372, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: WasActiveDuringEdit
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 7.09624
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 2.94
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: -10.98281
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7344258574652498121, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Player
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
--- !u!1 &870123117
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -367,7 +432,7 @@ PrefabInstance:
|
||||
- target: {fileID: 7443408887813606060, guid: 0b650fca685f2eb41a86538aa883e4c1, type: 3}
|
||||
propertyPath: _playerPrefab
|
||||
value:
|
||||
objectReference: {fileID: 1929457866}
|
||||
objectReference: {fileID: -8731164649411638372, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
- target: {fileID: 7443408887813606060, guid: 0b650fca685f2eb41a86538aa883e4c1, type: 3}
|
||||
propertyPath: Spawns.Array.size
|
||||
value: 2
|
||||
@@ -497,82 +562,6 @@ Transform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1929457866 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: -8731164649411638372, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
m_PrefabInstance: {fileID: 5836281772481032079}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: FishNet.Runtime::FishNet.Object.NetworkObject
|
||||
--- !u!1001 &5836281772481032079
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: -8731164649411638372, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: SceneId
|
||||
value: 4160487019
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8731164649411638372, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: WasActiveDuringEdit
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 2
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4433491345327751435, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7344258574652498121, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Player
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 687b16b0a8065d74fa4380e93a8a3d81, type: 3}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -580,6 +569,6 @@ SceneRoots:
|
||||
- {fileID: 870123120}
|
||||
- {fileID: 1617942571}
|
||||
- {fileID: 1749431010}
|
||||
- {fileID: 5836281772481032079}
|
||||
- {fileID: 277106704}
|
||||
- {fileID: 1541499318}
|
||||
- {fileID: 383713395}
|
||||
|
||||
@@ -3,65 +3,82 @@ using UnityEngine.InputSystem;
|
||||
|
||||
namespace Agame
|
||||
{
|
||||
/// <summary>
|
||||
/// 第一人称视角控制:鼠标横移带动 body 偏航,纵移带动相机俯仰。
|
||||
///
|
||||
/// 网络侧前提(重要):本组件挂在 PlayerCamera 上,而 PlayerCamera 只会被
|
||||
/// <see cref="PlayerController"/> 为「本地拥有者」启用(见其 ApplyOwnership)。
|
||||
/// 非拥有者客户端的相机是 disabled 的,因此这里不会执行,天然不需要再判所有权。
|
||||
///
|
||||
/// 反过来说:如果日后把 PlayerCamera 改成常驻启用,必须补上所有权判断,
|
||||
/// 否则每个客户端都会用自己的鼠标去转动别人玩家的视角。
|
||||
/// </summary>
|
||||
public class MouseLook : MonoBehaviour
|
||||
{
|
||||
[Header("Sensitivity")]
|
||||
[Tooltip("Degrees of rotation per pixel of mouse movement.")]
|
||||
public float mouseSensitivity = 0.12f;
|
||||
[SerializeField] private float mouseSensitivity = 0.12f;
|
||||
|
||||
[Header("Vertical Clamp")]
|
||||
public float minLookAngle = -80f;
|
||||
public float maxLookAngle = 80f;
|
||||
[SerializeField] private float minLookAngle = -80f;
|
||||
[SerializeField] private float maxLookAngle = 80f;
|
||||
|
||||
[Header("References")]
|
||||
public Transform characterBody;
|
||||
[Tooltip("被偏航(Horizontal)的躯体 Transform,通常是玩家根节点。")]
|
||||
[SerializeField] private Transform characterBody;
|
||||
|
||||
private float xRotation = 0f;
|
||||
private float _xRotation;
|
||||
private bool _warnedMissingBody;
|
||||
|
||||
private void Start()
|
||||
private void OnEnable()
|
||||
{
|
||||
// 跟随本对象的启用状态锁定光标:只有本地拥有者的相机会被启用,
|
||||
// 所以光标锁定/释放自然就只在拥有者这一侧发生。
|
||||
LockCursor(true);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
// 对象被禁用(非拥有者,或玩家已下线)时务必释放光标,否则会锁死在别人的机器上。
|
||||
LockCursor(false);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var kb = Keyboard.current;
|
||||
var mouse = Mouse.current;
|
||||
Mouse mouse = Mouse.current;
|
||||
if (mouse == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Esc releases the cursor, left click grabs it again (editor-friendly)
|
||||
// Esc 释放光标,左键重新抓取(方便编辑器里切出去)。
|
||||
Keyboard kb = Keyboard.current;
|
||||
if (kb != null && kb.escapeKey.wasPressedThisFrame)
|
||||
{
|
||||
LockCursor(false);
|
||||
}
|
||||
|
||||
if (mouse.leftButton.wasPressedThisFrame && Cursor.lockState != CursorLockMode.Locked)
|
||||
{
|
||||
LockCursor(true);
|
||||
}
|
||||
|
||||
if (Cursor.lockState != CursorLockMode.Locked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Mouse delta is in pixels for the new Input System
|
||||
// 新输入系统下 delta 的单位是像素,不是轴值。
|
||||
Vector2 delta = mouse.delta.ReadValue();
|
||||
float mouseX = delta.x * mouseSensitivity;
|
||||
float mouseY = delta.y * mouseSensitivity;
|
||||
|
||||
// Yaw the body left/right
|
||||
// 横移:转动躯体
|
||||
if (characterBody != null)
|
||||
{
|
||||
characterBody.Rotate(Vector3.up * mouseX);
|
||||
characterBody.Rotate(Vector3.up * (delta.x * mouseSensitivity));
|
||||
}
|
||||
else if (!_warnedMissingBody)
|
||||
{
|
||||
_warnedMissingBody = true;
|
||||
Debug.LogWarning(
|
||||
$"[{nameof(MouseLook)}] 未指定 Character Body,水平视角不会生效。请指向玩家根节点的 Transform。",
|
||||
this);
|
||||
}
|
||||
|
||||
// Pitch the camera up/down
|
||||
xRotation -= mouseY;
|
||||
xRotation = Mathf.Clamp(xRotation, minLookAngle, maxLookAngle);
|
||||
transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
|
||||
// 纵移:俯仰相机,并夹在上下限之间防止翻转
|
||||
_xRotation = Mathf.Clamp(_xRotation - (delta.y * mouseSensitivity), minLookAngle, maxLookAngle);
|
||||
transform.localRotation = Quaternion.Euler(_xRotation, 0f, 0f);
|
||||
}
|
||||
|
||||
private void LockCursor(bool locked)
|
||||
|
||||
@@ -1,64 +1,154 @@
|
||||
using FishNet.Component.Transforming;
|
||||
using FishNet.Connection;
|
||||
using FishNet.Object;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Agame
|
||||
{
|
||||
/// <summary>
|
||||
/// 玩家移动控制 —— FishNet 客户端授权模式。
|
||||
/// 参考:Docs/联机方案选型.md §3.3,以及官方教程 "Moving Your Player Around"。
|
||||
///
|
||||
/// 职责边界:本组件只负责「读输入 + 驱动 CharacterController」。
|
||||
/// 位置/旋转的同步交给 Player 预制体上的 NetworkTransform(Client Authoritative),
|
||||
/// 这里不写任何 RPC / SyncVar。
|
||||
///
|
||||
/// FishNet 4.7.3 下的三条硬约束:
|
||||
/// 1. 必须继承 NetworkBehaviour 而非 MonoBehaviour,否则拿不到 IsOwner 与网络生命周期回调。
|
||||
/// 2. IsOwner 禁止在 Awake / Start / OnStartServer / OnStartNetwork 里访问 ——
|
||||
/// FishNet 代码生成器会直接抛编译错误,按拥有者初始化的逻辑要放在 OnStartClient。
|
||||
/// 3. 只有拥有者能驱动自己的对象,少了这道判断就会去移动别人的玩家。
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(CharacterController))]
|
||||
public class PlayerController : MonoBehaviour
|
||||
public class PlayerController : NetworkBehaviour
|
||||
{
|
||||
[Header("Movement")]
|
||||
public float moveSpeed = 5f;
|
||||
public float sprintSpeed = 8f;
|
||||
public float gravity = -20f;
|
||||
public float jumpHeight = 1.5f;
|
||||
[SerializeField, Min(0f)] private float moveSpeed = 5f;
|
||||
[SerializeField, Min(0f)] private float sprintSpeed = 8f;
|
||||
|
||||
private CharacterController controller;
|
||||
private Vector3 velocity;
|
||||
private bool isGrounded;
|
||||
[Header("Gravity & Jump")]
|
||||
[Tooltip("负值向下。")]
|
||||
[SerializeField] private float gravity = -20f;
|
||||
[SerializeField, Min(0f)] private float jumpHeight = 1.5f;
|
||||
|
||||
[Header("Ownership")]
|
||||
[Tooltip("仅本地拥有者需要启用的对象(玩家相机等)。非拥有者会被自动禁用,避免多相机抢占渲染。")]
|
||||
[SerializeField] private GameObject[] ownerOnlyObjects;
|
||||
|
||||
private CharacterController _controller;
|
||||
private Vector3 _verticalVelocity;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
controller = GetComponent<CharacterController>();
|
||||
// Awake 中不得访问 IsOwner,这里只做纯本地缓存。
|
||||
_controller = GetComponent<CharacterController>();
|
||||
}
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
base.OnStartClient();
|
||||
|
||||
// 用 OnStartClient 而不是 Awake/Start:走到这里说明对象已完成网络链接、
|
||||
// 所有权已分配,此时 IsOwner 才是可信的。
|
||||
ApplyOwnership();
|
||||
WarnIfTransformNotSynchronized();
|
||||
}
|
||||
|
||||
public override void OnOwnershipClient(NetworkConnection prevOwner)
|
||||
{
|
||||
base.OnOwnershipClient(prevOwner);
|
||||
|
||||
// 所有权可能中途变更(例如将来的主机迁移),相机等要跟着切换。
|
||||
ApplyOwnership();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var kb = Keyboard.current;
|
||||
if (kb == null)
|
||||
{
|
||||
// 只驱动本地拥有者,否则每个客户端都会去移动别人的玩家。
|
||||
if (!IsOwner)
|
||||
return;
|
||||
}
|
||||
|
||||
isGrounded = controller.isGrounded;
|
||||
if (isGrounded && velocity.y < 0f)
|
||||
{
|
||||
velocity.y = -2f;
|
||||
}
|
||||
Keyboard kb = Keyboard.current;
|
||||
if (kb == null)
|
||||
return;
|
||||
|
||||
// WASD input
|
||||
float speed = kb.leftShiftKey.isPressed ? sprintSpeed : moveSpeed;
|
||||
Vector3 moveDirection = ReadMoveDirection(kb);
|
||||
|
||||
_controller.Move(moveDirection * (speed * Time.deltaTime));
|
||||
|
||||
UpdateJumpAndGravity(kb);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取 WASD 并转换为相对于角色自身朝向的方向(body 的偏航由 MouseLook 负责)。
|
||||
/// 斜向输入做归一化,避免对角线移动比正向更快。
|
||||
/// </summary>
|
||||
private Vector3 ReadMoveDirection(Keyboard kb)
|
||||
{
|
||||
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;
|
||||
|
||||
// Move relative to the capsule's own facing (which MouseLook rotates)
|
||||
Vector3 move = transform.right * moveX + transform.forward * moveZ;
|
||||
move = Vector3.ClampMagnitude(move, 1f);
|
||||
Vector3 direction = transform.right * moveX + transform.forward * moveZ;
|
||||
return Vector3.ClampMagnitude(direction, 1f);
|
||||
}
|
||||
|
||||
float speed = kb.leftShiftKey.isPressed ? sprintSpeed : moveSpeed;
|
||||
controller.Move(move * speed * Time.deltaTime);
|
||||
private void UpdateJumpAndGravity(Keyboard kb)
|
||||
{
|
||||
bool isGrounded = _controller.isGrounded;
|
||||
|
||||
// Jump
|
||||
if (kb.spaceKey.wasPressedThisFrame && isGrounded)
|
||||
// 贴地时保留一点向下的力,防止 isGrounded 在斜坡/台阶上反复抖动。
|
||||
if (isGrounded && _verticalVelocity.y < 0f)
|
||||
_verticalVelocity.y = -2f;
|
||||
|
||||
if (isGrounded && kb.spaceKey.wasPressedThisFrame)
|
||||
{
|
||||
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
||||
// 由目标跳跃高度反推初速度:v = sqrt(2 * g * h)
|
||||
_verticalVelocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
||||
}
|
||||
|
||||
// Gravity
|
||||
velocity.y += gravity * Time.deltaTime;
|
||||
controller.Move(velocity * Time.deltaTime);
|
||||
_verticalVelocity.y += gravity * Time.deltaTime;
|
||||
_controller.Move(_verticalVelocity * Time.deltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按所有权开关"只属于本地玩家"的对象。最典型的是玩家相机:
|
||||
/// 若每个客户端的相机都常驻启用,N 个玩家就会互相抢渲染。
|
||||
/// </summary>
|
||||
private void ApplyOwnership()
|
||||
{
|
||||
if (ownerOnlyObjects == null)
|
||||
return;
|
||||
|
||||
foreach (GameObject target in ownerOnlyObjects)
|
||||
{
|
||||
if (target != null)
|
||||
target.SetActive(IsOwner);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缺少 NetworkTransform 时位置不会同步,但画面上"自己能走",极易误判成逻辑没问题。
|
||||
/// 这里主动报一次,省掉对着两个人不同步干瞪眼的时间。
|
||||
/// </summary>
|
||||
private void WarnIfTransformNotSynchronized()
|
||||
{
|
||||
if (!IsOwner)
|
||||
return;
|
||||
|
||||
if (GetComponent<NetworkTransform>() == null)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"[{nameof(PlayerController)}] {name} 上没有 NetworkTransform,位置不会同步到其他客户端。" +
|
||||
"请在 Player 预制体上加 NetworkTransform,并开启 Client Authoritative 与 Synchronize Position。",
|
||||
this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user