过去几个小时一直在解决这个问题。我制作了一扇像 Phasmofobia 中的门,你可以按住 MB1 并拖动来打开或关闭它。问题是我正在制作一款多人游戏,我需要在服务器上同步该门,当有人打开它时,它就会为那里的所有玩家打开。现在每个人都可以开门,但不同步。
我创建了下面的脚本,有什么建议吗?我知道我必须使用
[Command]
和 [ClientRpc]
但不知道如何在这里使用它们。我以前在动画中使用过它,它很简单,但在这种情况下它打败了我。我还将网络身份和网络转换附加到门上,没有更改任何设置,但无法使其工作。
public class DoorOpenScript : NetworkBehaviour
{
private Transform playerCamera;
[SerializeField] Transform distCheck;
[SerializeField] Transform hinge;
[SerializeField] private float maxDistance = 3f;
[SerializeField] float moveSpeed;
[SerializeField] Vector2 rotationConstraints;
bool movingDoor;
float rotation;
Vector3 targetPosition;
void Start()
{
targetPosition = distCheck.position;
// Find the player's camera at runtime
playerCamera = Camera.main.transform; // Assuming your camera is tagged as "MainCamera" or is the main camera in the scene
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = playerCamera.GetComponent<Camera>().ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
int layerMask = ~LayerMask.GetMask("IgnoreRaycast");
if (Physics.Raycast(ray, out hit, maxDistance, layerMask))
{
if (hit.collider.gameObject.CompareTag("Door") && hit.collider.gameObject == this.gameObject)
{
movingDoor = true;
}
}
}
if (movingDoor)
{
if (Input.GetMouseButtonUp(0))
{
movingDoor = false;
}
targetPosition = playerCamera.position + playerCamera.forward * 2f;
}
rotation += Mathf.Clamp(-GetRotation() * 5000 * Time.deltaTime, -moveSpeed, moveSpeed);
rotation = Mathf.Clamp(rotation, rotationConstraints.x, rotationConstraints.y);
hinge.localRotation = Quaternion.Euler(0, rotation, 0);
}
float GetRotation()
{
float firstDistance = (distCheck.position - targetPosition).sqrMagnitude;
hinge.Rotate(Vector3.up);
float secondDistance = (distCheck.position - targetPosition).sqrMagnitude;
hinge.Rotate(-Vector3.up);
return secondDistance - firstDistance;
}
}
编辑:我已经让它与
Command
和 ClientRpc
一起工作,门同步良好,但主机以外的其他玩家在尝试拉开门时,有时门只是移动了一点,有时是在主机再次移动它之前它根本不会移动。我还在 Command
中使用 RequireAuthority = false。大家有什么想法吗? :(
我在下面添加了一些框架代码,可能会帮助您解决问题。
我将解释我的代码是如何工作的,以及为什么这样写。
首先要注意的是CMMDoveDoor函数中门是如何旋转的。这是因为 NetworkTransform 组件的工作方式。如果syncDirection变量(在检查器中找到)设置为服务器到客户端,这意味着对客户端门旋转的任何更改只会影响客户端设备上的门(同一门不会影响客户端设备上的门)。在服务器或其他客户端设备上轮换)。通过使用命令,客户端基本上会询问服务器:“嘿,你能为我移动那扇门吗?”,但实际上是服务器移动了门。然后,网络转换将看到服务器移动了门,并告诉所有客户端“嘿,门移动了”,客户端应自动更新其门位置以匹配服务器。
第二个需要注意的事情是我们如何将 (requiresAuthority = false) 放入 [Command] 代码中。这告诉服务器,“让任何客户端调用此函数,即使他们不拥有门”。通常,您必须拥有门才能调用此函数,但这允许任何连接的客户端调用此函数。出于安全原因,这可能很重要(例如,如果每个玩家都有一个脚本,使用 [SyncVar](该变量只能在服务器上更改,但可以被所有玩家读取)存储他们在服务器上收到的积分数量客户),您不希望其他客户能够更改其他玩家拥有的积分)
虽然这可能不是最好的解释,但我想我会尽力给你一条生命线,因为在回答问题方面更有经验的人不太可能过来并发布更好的东西。如果您需要更多信息,请告诉我,如果您希望我尝试为您修复它,请随时发布您的新的、更新的代码。
using System.Collections.Generic;
using UnityEngine;
using Mirror;
public class DoorController : NetworkBehaviour
{
public void Update()
{
if (isClient)
{
//Check If The Player Wants To Move The Door
//If So, Call CMDMoveDoor(), And Input The Degrees That Should Be Moved
//Do NOT Move The Door Here. The Door Should Only Be Moved On The Server
}
}
//This Can Be Called By Any Client, But Ran On The Server
[Command (requiresAuthority = false)]
void CMDMoveDoor(float degrees)
{
//Optionally, You Can First Check The Degrees Amount To Make Sure That It Is Reasonable.
//For Example, If It Would Rotate Too Far Or Too Fast, You Can Clamp It Here
//Now, Update The Position Of The Door Here By The Degrees Specifed.
//Make Sure Your Network Transform Is Added To The Object Or It's Parent, And That The Sync Rotation Bool Is Set To True In The Inspector
//If The Server Owns The Door, The Door's Rotation Will Sync Across All Clients Automatically
}
}