Предметы работают только для хоста, но не для клиента
В моей многопользовательской игре система предметов работает идеально для хоста. Однако у клиента возникают проблемы. В частности: Предметы, которые подбирает клиент, добавляются в инвентарь, но не отображаются у него в руке. Клиент не может выбрасывать предметы. Клиент не видит предметы, которые выбросил хост. Я реализовал систему инвентаря с использованием Unity Netcode, но кажется, что синхронизация для клиента работает некорректно. Что может быть причиной этих проблем, и как я могу обеспечить корректную синхронизацию предметов при их подборе и выбрасывании между хостом и клиентами? Буду признателен за любые советы или предложения!
PlayerInventory script
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
using TMPro;
public class PlayerInventory : NetworkBehaviour
{
public List<GameObject> allItems;
public List<GameObject> collectedItems = new List<GameObject>();
private int currentItemIndex = -1;
private const int maxInventorySlots = 3;
public Transform dropPosition;
public override void OnNetworkSpawn()
{
foreach (GameObject item in allItems)
{
item.SetActive(false);
}
}
[ServerRpc(RequireOwnership = false)]
public void AddItemToInventoryServerRpc(string itemName, ulong networkObjectId, ServerRpcParams rpcParams = default)
{
Debug.Log($"Попытка добавить предмет {itemName} в инвентарь на сервере.");
if (collectedItems.Count < maxInventorySlots)
{
GameObject itemToActivate = allItems.Find(item => item.name == itemName);
if (itemToActivate != null)
{
collectedItems.Add(itemToActivate);
HideItemForAllClientsClientRpc(itemName);
ActivateItemClientRpc(collectedItems.Count - 1);
SyncItemInHandClientRpc(itemName, true);
RemoveItemFromWorldClientRpc(networkObjectId);
Debug.Log($"Предмет {itemName} успешно добавлен в инвентарь.");
}
else
{
Debug.LogError($"Предмет {itemName} не найден среди скрытых предметов.");
}
}
else
{
Debug.Log("Инвентарь полон!");
}
}
[ClientRpc]
void RemoveItemFromWorldClientRpc(ulong networkObjectId)
{
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject))
{
GameObject itemObject = networkObject.gameObject;
itemObject.SetActive(false);
Destroy(itemObject);
}
}
[ClientRpc]
void HideItemForAllClientsClientRpc(string itemName)
{
GameObject itemToDeactivate = allItems.Find(item => item.name == itemName);
if (itemToDeactivate != null)
{
itemToDeactivate.SetActive(false);
}
}
[ClientRpc]
void ActivateItemClientRpc(int index)
{
if (index >= 0 && index < collectedItems.Count)
{
if (currentItemIndex != -1)
{
collectedItems[currentItemIndex].SetActive(false);
}
currentItemIndex = index;
collectedItems[currentItemIndex].SetActive(true);
Debug.Log($"Активирован предмет: {collectedItems[currentItemIndex].name}");
}
}
[ServerRpc(RequireOwnership = false)]
public void DropCurrentItemServerRpc(ServerRpcParams rpcParams = default)
{
if (currentItemIndex >= 0 && currentItemIndex < collectedItems.Count)
{
GameObject currentItem = collectedItems[currentItemIndex];
Debug.Log($"Выбрасывание предмета: {currentItem.name}");
GameObject droppedItem = Instantiate(currentItem, dropPosition.position, Quaternion.identity);
droppedItem.SetActive(true);
var networkObject = droppedItem.GetComponent<NetworkObject>();
if (networkObject != null && !networkObject.IsSpawned)
{
networkObject.Spawn(true);
}
Rigidbody rb = droppedItem.GetComponent<Rigidbody>();
if (rb == null)
{
rb = droppedItem.AddComponent<Rigidbody>();
rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
rb.mass = 1f;
}
rb.AddForce(dropPosition.forward * 1.5f, ForceMode.Impulse);
currentItem.SetActive(false);
collectedItems.RemoveAt(currentItemIndex);
if (collectedItems.Count > 0)
{
currentItemIndex = Mathf.Clamp(currentItemIndex - 1, 0, collectedItems.Count - 1);
ActivateItemClientRpc(currentItemIndex);
}
else
{
currentItemIndex = -1;
Debug.Log("Инвентарь пуст.");
}
SpawnDroppedItemClientRpc(droppedItem.name, droppedItem.transform.position, droppedItem.transform.rotation);
}
else
{
Debug.LogWarning("Не удалось выбросить предмет: текущий индекс не валиден.");
}
}
[ClientRpc]
void SpawnDroppedItemClientRpc(string itemName, Vector3 position, Quaternion rotation)
{
GameObject itemPrefab = allItems.Find(item => item.name == itemName);
if (itemPrefab != null)
{
GameObject droppedItem = Instantiate(itemPrefab, position, rotation);
droppedItem.SetActive(true);
var networkObject = droppedItem.AddComponent<NetworkObject>();
networkObject.Spawn(true);
}
}
void Update()
{
if (!IsOwner || collectedItems.Count == 0) return;
if (Input.GetAxis("Mouse ScrollWheel") != 0f)
{
int direction = Input.GetAxis("Mouse ScrollWheel") > 0f ? 1 : -1;
SwitchItem(direction);
}
if (Input.GetKeyDown(KeyCode.G))
{
DropCurrentItemServerRpc();
}
}
private void SwitchItem(int direction)
{
if (collectedItems.Count == 0) return;
if (currentItemIndex != -1)
{
collectedItems[currentItemIndex].SetActive(false);
}
currentItemIndex += direction;
if (currentItemIndex >= collectedItems.Count) currentItemIndex = 0;
if (currentItemIndex < 0) currentItemIndex = collectedItems.Count - 1;
ActivateItemClientRpc(currentItemIndex);
}
public bool HasItemInHand(string itemName)
{
return currentItemIndex != -1 && collectedItems[currentItemIndex].name == itemName;
}
[ServerRpc(RequireOwnership = false)]
public void RemoveItemFromHandServerRpc(string itemName, ServerRpcParams rpcParams = default)
{
if (HasItemInHand(itemName))
{
Debug.Log($"Удаление предмета {itemName} из рук.");
collectedItems[currentItemIndex].SetActive(false);
collectedItems.RemoveAt(currentItemIndex);
currentItemIndex = -1;
}
}
[ClientRpc]
void SyncItemInHandClientRpc(string itemName, bool isActive)
{
GameObject itemInHand = collectedItems.Find(item => item.name == itemName);
if (itemInHand != null)
{
itemInHand.SetActive(isActive);
}
}
}
PlayerInteract script
using UnityEngine;
using Unity.Netcode;
using TMPro;
public class PlayerInteract : NetworkBehaviour
{
public float interactionRange = 3f;
public Camera playerCamera;
public GameObject interactUI;
private TMP_Text interactText;
private Item itemInFocus;
private PlayerInventory playerInventory;
private void Start()
{
if (interactUI != null)
{
interactText = interactUI.GetComponent<TMP_Text>();
if (interactText == null)
{
Debug.LogError("TMP_Text компонент не найден на interactUI объекте!");
}
}
else
{
Debug.LogError("interactUI объект не присвоен в инспекторе!");
}
playerInventory = GetComponent<PlayerInventory>();
}
private void Update()
{
if (!IsOwner) return;
Ray ray = playerCamera.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2));
RaycastHit hit;
if (Physics.Raycast(ray, out hit, interactionRange))
{
Item item = hit.collider.GetComponent<Item>();
if (item != null)
{
itemInFocus = item;
ShowItemInfo(item);
if (Input.GetKeyDown(KeyCode.E))
{
Debug.Log("Нажата клавиша E для подбора предмета: " + item.itemName);
PickupItemServerRpc(item.NetworkObjectId);
}
}
else
{
HideItemInfo();
}
}
else
{
HideItemInfo();
}
}
void ShowItemInfo(Item item)
{
if (interactText != null)
{
interactUI.SetActive(true);
interactText.text = $"Нажмите E, чтобы подобрать {item.itemName}";
}
}
void HideItemInfo()
{
if (interactUI != null)
{
interactUI.SetActive(false);
}
}
[ServerRpc]
void PickupItemServerRpc(ulong itemId)
{
Debug.Log("ServerRpc вызван для предмета с ID: " + itemId);
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(itemId, out NetworkObject networkObject))
{
Item item = networkObject.GetComponent<Item>();
if (item != null)
{
playerInventory.AddItemToInventoryServerRpc(item.itemName, itemId);
Debug.Log("Предмет успешно добавлен в инвентарь и удален из мира.");
}
else
{
Debug.LogError("Item не найден на объекте NetworkObject!");
}
}
else
{
Debug.LogError("NetworkObject с данным itemId не найден!");
}
}
}
Item script
using UnityEngine;
using Unity.Netcode;
public class Item : NetworkBehaviour
{
public string itemName;
public override void OnNetworkSpawn()
{
if (IsServer)
{
//
}
}
}
Я потратил значительное количество времени, пытаясь устранить эту проблему, но так и не смог найти решение. Я неоднократно пересматривал свой код и сетевую настройку, но проблема по-прежнему сохраняется.