I have a problem where I can't spawn the selected object from clients. It works perfectly when the action is performed by the host, but not when a client attempts it. When a client attempts it, I get the following error: "SpawnWithClientAuthority player object is not a player". This is quite confusing as it works perfectly when it's performed by the host.
The code for this particular part is the following:
private void updateAppearance(GameObject newObject)
{
Destroy(appearance);
hiderModel.SetActive(false);
int newObjectNum = propNames.IndexOf(newObject.name);
activePropIndex = newObjectNum;
Debug.Log(newObjectNum);
newObject = (GameObject)Instantiate(props[newObjectNum], playerCam.gameObject.transform);
newObject.transform.localPosition = new Vector3(0, getObjectHeight(newObjectNum), 0);
NetworkServer.SpawnWithClientAuthority(newObject, gameObject); <--- This part gives the error
appearance = newObject;
appearance.transform.localPosition = new Vector3(0, appearance.transform.localPosition.y, 0);
}
The object to spawn has localAuthority set and has a network transform on it.
The object is registered as a spawnable object and it is the instantiated prefab that I am passing to the SpawnWithClientAuthority method. As far as I have read, this should allow the function to work, but unfortunately it doesn't.
Any ideas on how to fix this?
Thanks in advance
It works perfectly when the action is performed by the host, but not when a client attempts it
Because ONLY SERVER can spawn objects across the network.
Wrap your code to [Command] to execute it on server.
Related
I copy and pasted Unity Netcode's example code from https://docs-multiplayer.unity3d.com/docs/advanced-topics/message-system/clientrpc to test it out. This is my exact code:
public class Test : MonoBehaviour
{
public TMP_Text text;
void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
PongClientRpc(Time.frameCount, "hello, world"); // Server -> Client
}
}
[ClientRpc]
void PongClientRpc(int somenumber, string sometext)
{
text.text = $"{somenumber} : {sometext}";
Debug.Log(text.text);
}
}
What I've found is that the function just acts as a normal function whether it's called on the host application or client application, and it is never transmitted across the network. My expectation is that when it is called on the host application, the RPC would be transmitted to the client application and executed there as well, thus reflecting a change in the TMP_Text object, but clearly this is not the case.
Just to check all the boxes, this script is attached to a GameObject that also has a NetworkObject component. For good measure, I tested the TMP_Text object both with and without a NetworkObject component attached. In all cases, any object with a NetworkObject component was properly added to the NetworkManager's network prefabs list.
I'm starting to question if I even understand what an RPC is or how it's supposed to work. Any insights would be greatly appreciated!
This class needs to be derived from NetworkBehavior not MonoBehaviour else it won't be networked.
Make sure to add using Unity.Netcode; at the top of the class.
I'm having an issue with ClientRpc never being called any client objects. I've trolled the internet for hours, but I can't find any clues as to why my implementation isn't working.
I'm following an order of events like so:
Add players in a lobby
Switch scene to the gameplay scene
Generate random tiles on the server
Send tile data to the clients using Rpc for rendering
However, the rendering function never gets called.
NetworkManager.cs
public override void OnServerReady(NetworkConnection conn)
{
base.OnServerReady(conn);
//Make sure they're all ready
for (int i = RoomPlayers.Count - 1; i >= 0; i--)
{
if (!RoomPlayers[i].IsReady)
{
return;
}
}
//Previously add SpawnTiles to OnServerReadied
OnServerReadied?.Invoke(conn);
}
GameManager.cs
private void SpawnTiles(NetworkConnection conn)
{
//Generate rawTiles beforehand
Debug.Log(conn.isReady);
Debug.Log("Entered spawn tiles");
RpcSpawnTiles(rawTiles);
}
[ClientRpc]
public void RpcSpawnTiles(short[] rawTiles)
{
Debug.Log("Client spawning tiles");
}
And this is my output when run on a host:
True
Entered spawn tiles
It appears to never enter the Rpc function :(
Is there something super obvious that I'm missing? My GameManager does have a NetworkID attached to it
There seemed to be a combination of things that I had to do to get it to work- I'm not entirely sure which one worked, but if you are having these problems, these are some of the things I had to check-
Just basic code. Make sure that the appropriate things are being created on the client, host, or server
Make sure that there aren't any race conditions, especially when working with changing scenes. You can call stuff from OnClientSceneChanged and OnServerSceneChanged
As derHugo pointed out, I was not checking to see if my package could even be sent. It turns out that the maximum size that can be sent is 1200 bytes, while I was trying to send 100x100x4 bytes. This error didn't occur when I was testing on the host, only when there was an external client
I have created a code file in Unity and assigned it to an empty GameObject I have placed in the scene:
var obj = new GameObject("Sample");
obj.transform.position = new Vector3(0, 0, 0);
var text = obj.AddComponent<TextMesh>();
text.text = "Hello world";
When I run the scene, I can see the text. And that is my problem: I did not specify anywhere in code to add obj to the scene, but it gets placed automatically apparently.
This can be a problem if I want to introduce an object later than instantiation time.
What am I doing wrong? How can this be achieved? What are the patterns/best-practices here?
Immediate fix:
Use obj.SetActive(false) to temporarily disable the object and then use obj.SetActive(true) when you need the object to be active.
Other solutions / best practices:
Create the object you desire in the scene, save it as a prefab (prefabricated object) and then only instantiate it when you need it. Here's a link for further reading into the prefab system. https://docs.unity3d.com/Manual/Prefabs.html
Object pooling is typically used when you will have a bunch of the same objects (like lasers, bullets, etc). Watching this video may be of help: https://www.youtube.com/watch?v=tdSmKaJvCoA
I'm making a multiplayer bomberman game using Unet. I'm trying to create a ready check to start the game when everyone else is ready.
I tried using a sync var,Command and Rpc calls, but nothing seems to work properly.
When using a Cmd call this way (called when a player pushes the ready button)
[Command]
private void CmdIncreaseReady()
{
IncreaseReady();
RpcIncreaseReady();
}
[ClientRpc]
private void RpcIncreaseReady()
{
IncreaseReady();
}
private void IncreaseReady() {
playersReady++;
//ChechAllReady();
}
When pressed on the client, everyone's counter is 0 (it should be one), and when pressed on the server, the server's counter is 2 and the client 1.
I've tried to call Cmd when !isServer and Rpc when it is, and the result is that stills doesn't update when pressed on the client, but it is updated correctly (the counter is 1 on both), when pressed on the server.
if (!isServer)
{
CmdIncreaseReady();
}
else
{
RpcIncreaseReady();
}
If I delete the IncreaseReady() call on the Cmd, the result is the same than above.
I've tried too to use a [SyncVar] to the counter, with and without a hook, passing 1 on the hook (to increment the counter that amount) and passing the already counter incremented and set variable to that number, nothing seems to work.
I really don't know how to make it work anymore. Could someone help me? I'm really desperate with this. I've searched everywhere.
The script is the game manager, when every client has his version, it has a Netework Identity and has Local player Authority.
I've tried another approach, passing the increased number on the Cmd and Rpc, and inside the functions, just set the playersReady = i. Even that doesn't work.
[UPDATE]:
So, I've found the specific problem that I want to solve, and it's to Sync non-player objects properties.
I've tried to spawn the Game Manager with Client Authority, and now it seems to sync correctly only on the server, the client stills doesn't call the Cmd propety.
Looking at the Debug mode in the inspector, I've been able to see that on the server, hasAutority is true, but is false on the client.
This is a fragment of the code on the player script where I spawn the game manager:
void Update()
{
if (!sceneLoaded)
{
if(isServer & SceneManager.GetActiveScene().name == "Main Scene")
//if (SceneManager.GetActiveScene().name == "Main Scene")
{
if (connectionToClient.isReady)
{
var go = (GameObject)Instantiate(gameManager);
NetworkServer.SpawnWithClientAuthority(go, connectionToClient);
go.transform.SetParent(null);
//go.GetComponent<NetworkIdentity>().AssignClientAuthority(connectionToClient);
globalManager = go.GetComponent<FSM>();
//SetFSM();
sceneLoaded = true;
}
}
}
UNET has a Lobby system which does exactly what I think you want. It has a per-player ready state, so that the game starts when all players are ready.
I would recommend using that, as it saves you having to reimplement things like dropping out, rejoining etc...
In my project, I have two prefabs - One with a first person camera, and another one stripped of all it's first person components such as the mouselook, input controller, camera, and so on. My ultimate goal is to spawn my prefab with the camera on it and sending an rpc to spawn the dummy prefab on all other clients to sync up with my first person prefab. The problem I'm facing is when I try to send the rpc from my first person prefab's PhotonView, the rpc doesn't get called on other clients because the PhotonView id doesn't exist on other clients yet.
The following code is located on my NetworkManager script on an empty game object in the hierarchy.
void OnJoinedRoom(){
GameObject MyPlayer = (GameObject) Instantiate (FPSPlayer,spawnPoint.transform.position, spawnPoint.transform.rotation);
FPSPlayerPhotonView = MyPlayer.transform.root.gameObject.GetPhotonView();
int id = PhotonNetwork.AllocateViewID ();
FPSPlayerPhotonView.viewID = id;
FPSPlayerPhotonView.RPC("SpawnMyPlayerAsRemote", PhotonTargets.OthersBuffered,id, spawnPoint.transform.position, spawnPoint.transform.rotation);
}
[PunRPC]
void SpawnMyPlayerAsRemote(int id, Vector3 pos, Quaternion rot){
GameObject MyRemotePlayer = (GameObject)Instantiate (RemotePlayer, pos,rot);
RemotePlayerPhotonView = MyRemotePlayer.transform.root.gameObject.GetPhotonView();
RemotePlayerPhotonView.viewID = id;
}
This method is tested and will work.
What I suggest you do is create an empty player with NetworkView.
Create this player via PhotonNetwork.Instantiate so it will be instantiated inside all clients.
Check you are the owner of the networkview.
If you are, create all the client sided essentials (FPS Camera etc...).
If you aren't, create the dummy.
EDIT: I was wrong on the RPC part.