Что Я делаю:
Я использую Netcode для GameObjects для синхронизации взаимодействия предметов между клиентами и сервером.
Каждый предмет в инвентаре имеет уникальный идентификатор, который я генерирую, когда предмет сначала создается, чтобы я мог его отслеживать.
Когда игрок берет предмет, он добавляется в его инвентарь, а когда он его бросает, экземпляр этого предмета снова создается в мире.
проблема возникает во время выпадения: вновь созданный элемент получает новый NetworkObjectId, хотя мне нужно, чтобы он сохранил тот же идентификатор, который у него был, пока он находился в инвентаре.
PlayerInteract.cs
Код: Выделить всё
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();
}
playerInventory = GetComponent();
}
private void Update()
{
if (!IsOwner) return;
HandleItemInteraction();
if (Input.GetKeyDown(KeyCode.G))
{
playerInventory.DropCurrentItemServerRpc();
}
if (Input.mouseScrollDelta.y > 0)
{
playerInventory.SwitchItem(1);
}
else if (Input.mouseScrollDelta.y < 0)
{
playerInventory.SwitchItem(-1);
}
}
private void HandleItemInteraction()
{
Ray ray = playerCamera.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2));
if (Physics.Raycast(ray, out RaycastHit hit, interactionRange))
{
Item item = hit.collider.GetComponent();
if (item != null)
{
itemInFocus = item;
ShowItemInfo(item);
if (Input.GetKeyDown(KeyCode.E))
{
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(RequireOwnership = false)]
void PickupItemServerRpc(ulong itemId, ServerRpcParams rpcParams = default)
{
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(itemId, out NetworkObject networkObject))
{
Item item = networkObject.GetComponent();
if (item != null)
{
playerInventory.AddItemToInventoryServerRpc(item.itemName, itemId);
}
}
}
}
Код: Выделить всё
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
public class PlayerInventory : NetworkBehaviour
{
public List allItems;
public List collectedItems = new List();
private int currentItemIndex = -1;
private const int maxInventorySlots = 3;
public Transform dropPosition;
public override void OnNetworkSpawn()
{
if (!IsServer) return;
foreach (GameObject item in allItems)
{
item.SetActive(false);
HideAllItemsForAllClientsClientRpc();
}
}
[ServerRpc(RequireOwnership = false)]
public void AddItemToInventoryServerRpc(string itemName, ulong networkObjectId)
{
if (collectedItems.Count < maxInventorySlots)
{
GameObject itemToActivate = allItems.Find(item => item.name == itemName);
if (itemToActivate != null)
{
collectedItems.Add(itemToActivate);
RemoveItemFromWorldServerRpc(networkObjectId);
currentItemIndex = collectedItems.Count - 1;
ActivateItemForAllClientsClientRpc(currentItemIndex);
SyncItemsForAllClientsClientRpc();
}
}
else
{
Debug.Log("Inventory is full!");
}
}
[ServerRpc(RequireOwnership = false)]
public void RemoveItemFromWorldServerRpc(ulong networkObjectId)
{
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject))
{
networkObject.Despawn(true);
}
}
[ClientRpc]
void HideAllItemsForAllClientsClientRpc()
{
foreach (var item in allItems)
{
item.SetActive(false);
}
}
[ClientRpc]
void ActivateItemForAllClientsClientRpc(int index)
{
HideAllItemsForAllClientsClientRpc();
if (index >= 0 && index < collectedItems.Count)
{
collectedItems[index].SetActive(true);
}
}
[ServerRpc(RequireOwnership = false)]
public void DropCurrentItemServerRpc()
{
if (currentItemIndex >= 0 && currentItemIndex < collectedItems.Count)
{
GameObject currentItem = collectedItems[currentItemIndex];
if (currentItem == null) return;
// Получаем компонент Item, чтобы использовать его префаб
Item itemComponent = currentItem.GetComponent();
if (itemComponent == null || itemComponent.itemPrefab == null)
{
Debug.LogError("Компонент Item или префаб не найден на предмете!");
return;
}
// Спавним предмет на сервере используя префаб из компонента Item
GameObject droppedItem = Instantiate(itemComponent.itemPrefab, dropPosition.position, Quaternion.identity);
NetworkObject networkObject = droppedItem.GetComponent() ?? droppedItem.AddComponent();
// Спавн с синхронизацией по сети для всех клиентов
networkObject.Spawn(true);
// Добавляем физику для выбрасывания предмета
Rigidbody rb = droppedItem.GetComponent() ?? droppedItem.AddComponent();
rb.AddForce(dropPosition.forward * 1.5f, ForceMode.Impulse);
// Деактивируем текущий предмет в инвентаре
currentItem.SetActive(false);
collectedItems.RemoveAt(currentItemIndex);
// Обновляем индекс текущего предмета
UpdateCurrentItemIndex();
// Уведомляем клиентов о выбрасывании предмета
NotifyItemDroppedForAllClientsClientRpc(droppedItem.name, droppedItem.GetComponent().NetworkObjectId, droppedItem.transform.position, droppedItem.transform.rotation);
}
}
void UpdateCurrentItemIndex()
{
if (collectedItems.Count > 0)
{
currentItemIndex = Mathf.Clamp(currentItemIndex - 1, 0, collectedItems.Count - 1);
ActivateItemForAllClientsClientRpc(currentItemIndex);
}
else
{
currentItemIndex = -1;
HideAllItemsForAllClientsClientRpc();
}
}
[ClientRpc]
void NotifyItemDroppedForAllClientsClientRpc(string itemName, ulong networkObjectId, Vector3 position, Quaternion rotation)
{
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject))
{
GameObject droppedItem = networkObject.gameObject;
if (droppedItem != null)
{
droppedItem.transform.position = position;
droppedItem.transform.rotation = rotation;
droppedItem.SetActive(true);
}
}
}
[ClientRpc]
void SyncItemsForAllClientsClientRpc()
{
Debug.Log("Syncing items...");
foreach (var item in allItems)
{
item.SetActive(false);
}
for (int i = 0; i < collectedItems.Count; i++)
{
SyncSingleItemForAllClientsClientRpc(collectedItems[i].name, true);
}
for (int i = 0; i < allItems.Count; i++)
{
if (!collectedItems.Contains(allItems[i]))
{
SyncSingleItemForAllClientsClientRpc(allItems[i].name, false);
}
}
}
[ClientRpc]
void SyncSingleItemForAllClientsClientRpc(string itemName, bool isActive)
{
GameObject itemInHand = allItems.Find(item => item.name == itemName);
if (itemInHand != null)
{
itemInHand.SetActive(isActive);
}
}
public void SwitchItem(int direction)
{
if (collectedItems.Count == 0) return;
currentItemIndex = (currentItemIndex + direction + collectedItems.Count) % collectedItems.Count;
HideAllItemsForAllClientsClientRpc();
ActivateItemForAllClientsClientRpc(currentItemIndex);
}
public bool HasItemInHand(string itemName)
{
return currentItemIndex >= 0 && currentItemIndex < collectedItems.Count && collectedItems[currentItemIndex].name == itemName;
}
[ServerRpc(RequireOwnership = false)]
public void RemoveItemFromHandServerRpc(string itemName)
{
if (HasItemInHand(itemName))
{
collectedItems[currentItemIndex].SetActive(false);
collectedItems.RemoveAt(currentItemIndex);
UpdateCurrentItemIndex();
}
}
public bool HasItemInInventory(string itemName)
{
return collectedItems.Exists(item => item.name == itemName);
}
}
Код: Выделить всё
using UnityEngine;
using Unity.Netcode;
public class Item : NetworkBehaviour
{
public string itemName;
public GameObject itemPrefab;
}
list_of_prefabs
original_prefab_network_object
Мой префаб после того, как я взял его в инвентарь и бросил:
[Netcode] Не удалось создать объект локально. [globalObjectIdHash=blablabla]. NetworkPrefab не найден. Зарегистрирован ли префаб в NetworkManager?
Я ожидал, что при удалении элемента он сохранит свой исходный уникальный идентификатор (или NetworkObjectId), что позволит мне отслеживать его как один и тот же объект как в инвентарь и когда он выпадает в игровом мире.
Подробнее здесь: https://stackoverflow.com/questions/790 ... item-spawn
Мобильная версия