ClientRpc Function not being carried out on all clients in Unity3d c# - c#

Currently I am working on a networked 2d platformer game. I have a script that is supposed to instantiate the players jetpack called JetpackManager. However when the player is spawned into the scene the code only spawns a jetpack into the hosts scene and not into the clients' scenes. This results in only the hosts scene working properly while all the players in the clients' scenes have no jetpacks. This is my code for the JetpackManager:
using UnityEngine;
using UnityEngine.Networking;
public class JetpackManager : NetworkBehaviour {
[SerializeField]
private Jetpack[] jetpacks;
[SerializeField]
private Transform jetPackHold;
private PlayerController playerController;
private Jetpack currentJetpack;
void Start(){
playerController = GetComponent<PlayerController> ();
if (isLocalPlayer) {
CmdEquipJetpack (0);
}
}
[Command]
void CmdEquipJetpack(int jetpackNumber){
RpcEquipJetpack (jetpackNumber);
}
[ClientRpc]
void RpcEquipJetpack(int jetpackNumber){
if (currentJetpack != null) {
Destroy (currentJetpack.gameObject);
}
currentJetpack = Instantiate (jetpacks[jetpackNumber], jetPackHold.position, jetPackHold.rotation);
currentJetpack.transform.SetParent (jetPackHold);
playerController.InitialiseJetpackVariables (currentJetpack);
}
}
So essentially my problem is that the code within the RpcEquipJetpack Function is for some reason only being called on the host and not on any of the clients.

You should instantiate Network object with network class
Network.Instantiate
Network instantiate a prefab.
The given prefab will be instanted on all clients in the game.
Synchronization is automatically set up so there is no extra work
involved. The position, rotation and network group number are given as
parameters. Note that in the example below there must be something set
to the playerPrefab in the Editor. You can read more about
instantiations in the object reference Object.Instantiate.

Related

Multiplayer in Unity

Below is the code for movement of player object and it works perfectly fine. But in the TellClientsToMoveClientRpc why are we doing transform.position as it refers to the the current gameobject but not every object moves, only the one which called the above Rpc moves which is desirable. But why all other gameobject do not move ?
using UnityEngine;
namespace HelloWorld
{
public class HelloWorldPlayer : NetworkBehaviour
{
private const float speed = 20f;
private void Update()
{
if (!IsOwner)
return;
PlayerMove();
}
void PlayerMove()
{
Vector3 velocity = new(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
if(velocity.sqrMagnitude >= 1)
{
TellServerToMoveClientServerRpc(velocity, NetworkManager.Singleton.LocalClientId);
}
}
[ServerRpc]
void TellServerToMoveClientServerRpc(Vector3 velocity, ulong clientId)
{
TellClientsToMoveClientRpc(velocity, clientId);
}
[ClientRpc]
void TellClientsToMoveClientRpc(Vector3 velocity, ulong clientId)
{
transform.position += speed * Time.deltaTime * velocity;
}
}
}```
A multiplayer game like this means that there are multiple clients running the same program (the game), meaning that there is a copy of all of the same game objects on each client's instance of the game.
This means that the game object you attached your HelloWorldPlayer script on is present on each client.
When you call TellServerToMoveClientServerRpc from a client, it will execute that function on the same game object the client called the function from, but on the server. This is because you added a [ServerRPC].
Now that the server has control over what's going to happen next, it calls TellClientsToMoveClientRpc. Now this time, since you added a [ClientRpc], that function will be called on the same game object the original client called the function from, but now on every single client that's connected to your server.
Since it's always the same game object calling the function (just on different instances of the game, running on different computers), not all game objects move: only the one that initially called the TellServerToMoveClientServerRpc function.

Unity c# - unable to Spawn Prefabs on a NavMesh

I am trying to make a zombie wave game and current have a Prefab for my enemies. If I have the prefab be in the scene when I hit run, they are attached to the NavMesh and track the player perfectly. I want to achieve this but with the enemy being spawned from an empty GameObject so I can get the waves spawning in. I have achieved them Spawning but they have the error,
"SetDestination" can only be called on an active agent that has been placed on a NavMesh.
UnityEngine.AI.NavMeshAgent:SetDestination(Vector3)
EnemyAI:Update() (at Assets/Scripts/EnemyAI.cs:25)
Here is my EnemyAI Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class EnemyAI : MonoBehaviour
{
public float lookRadius = 10f;
Transform target;
NavMeshAgent agent;
public GameObject Player;
void Start()
{
agent = GetComponent<NavMeshAgent>();
}
// Update is called once per frame
void Update()
{
float distance = Vector3.Distance(Player.transform.position, transform.position);
if (distance <= lookRadius)
{
agent.SetDestination(Player.transform.position);
}
}
}
And my spawning script, which is attached to an empty game object,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawning : MonoBehaviour
{
public GameObject prefab;
public int CountofCubes;
private IEnumerator coroutine;
public float spawnRate;
IEnumerator Start()
{
while (true)
{
for (int i = 0; i < CountofCubes; i++)
{
Instantiate(prefab, new Vector3(Random.Range(-25.0f, 25.0f), 0.5f, Random.Range(-25.0f, 25.0f)), Quaternion.identity);
}
yield return new WaitForSeconds(spawnRate);
}
}
}
Any help would be great thanks!
I had the same issue and I don't have an explanation but only a workaround:
In the Start fucntion, I added:
navAgent = GetComponent<NavMeshAgent>();
navAgent.enabled = false;
// Invoke is used as a workaround for enabling NavMeshAgent on NavMeshSurface
Invoke("EnableNavMeshAgent", 0.025f);
And the EnableNavMeshAgent function is just :
private void EnableNavMeshAgent ()
{
navAgent.enabled = true;
}
If I set the invoke delay to a value less than 0.025 second the error keep going but for 0.025 I only have it twice and the behaviour is the one I wanted after that.
Some reasons this might happen:
The agent is not on level with any navmesh i.e. can be too far above or below. You want it to be "close to the NavMesh surface". Use raycasting on the floor to position the agent or add a rigidbody and drop it from above the floor. After you do this you might need to disable and enable the agent.
Moving the transform yourself rather than using Wrap function. There's property where you can check if the simulated and actual position are in sync.
Corrupted navmesh so you might need to re-bake it.
It is essentially trying to tell you your agent is not on the mesh so it cannot find a path. Try playing with where you're placing the agent.
I kind of remember running into a similar problem a while back and problem was I forgot to bake the NavMesh in Window > AI > Navigation. Just in case.

Unity, C#, Network, 3D. Instantiating Prefab(Weapon) wont show on all Clients

I have 3 Gun Prefabs in an FPS Multiplayer.
When I spawn different prefabs I only see it on the local client.
Other clients still only see the Base Weapon GFX.
Is there a simple way for all players to see the weapon graphics which I Instantiate on the local client?
Animations, movement, rotations seem to work perfectly.
Only the currentGFX of the local weapon wont change on other clients
Here is my code for the Instantiate weapon part:
public void SpawnWeapon(PlayerWeapon _weapon)
{
currentWeapon = _weapon;
//Instantiate the prefab
GameObject _weaponIns = (GameObject)Instantiate(_weapon.graphics, weaponHolder, false);
//Assign the weaponGFX BUT works only on the local client
currentGraphics = _weaponIns.GetComponent<WeaponGraphics>();
}
You need to spawn weapon on the server.
you need add:
using UnityEngine.Networking;
in your script, then type
[Command] above your method and spawn object on the server like:
[Command]
public void SpawnWeapon(PlayerWeapon _weapon)
{
currentWeapon = _weapon;
GameObject _weaponIns = (GameObject)Instantiate(_weapon.graphics, weaponHolder,
false);
currentGraphics = _weaponIns.GetComponent<WeaponGraphics>();
NetworkServer.SpawnWithClientAuthority(_weaponIns,
PLAYERS_NETWORK_IDENTITY.connectionToClient);
}
It will cause server to update it's data and then other clients will be updated.

How to attach the camera to a player object instantiated by HLAPI Network Manager?

In short, I have a very simple multiplayer game. It's the Roll A Ball game (Unity3D tutorial). So right now I have the players etc spawning perfectly and everyone is able to control their own balls perfectly fine.
But here's the problem: I've got a default Main Camera. Since it's only the local player itself that needs to see it, I figured there's no point in trying to spawn a seperate camera for each player on the server.
However, to make the camera follow the player, I need to attach it the player gameobject. Obviously I can't attach it to the player prefab as it's a clone the camera needs to follow. But since the player is being spawned by the Network Manager component, I have no idea on how to refer to this clone.
What I've tried myself:
public class CameraController : NetworkManager
{
public GameObject playerPrefab;
public Transform target;
private Vector3 offset;
public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
GameObject player = (GameObject)Instantiate(playerPrefab, new Vector3(0, 0.5f, 0), Quaternion.identity);
target = player.transform;
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}
void Start()
{
offset = transform.position - target.position;
}
void LateUpdate()
{
transform.position = transform.position + offset;
}
}
But this resulted in:
Which I find extremely odd since as you can clearly see, there's no NetworkIdentity component on the NetworkManager object. I've been trying A LOT of things for the past 4 hours now and I just can't do it. So now I'm hoping you guys can help me out.
Edit: This is how the Network Manager normally spawns a player. As you can see, there's no code for it:
I had the same issue and figured out the followig solution. Seems like you already got a solution, but maybe it is interesting to share some possible ways for other people in the same situation.
This is a way to do it without a camera attached to the prefabs.
I'm using a NetworkManager to instantiate Player-prefabs. (Same as you)
I solved the problem of finding references to the clone objects by letting the clones tell the camera, who they are (or which transform belongs to them).
The Player has the following script:
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class PlayerController : NetworkBehaviour {
public override void OnStartLocalPlayer()
{
GetComponent<MeshRenderer>().material.color = Color.blue;
Camera.main.GetComponent<CameraFollow>().target=transform; //Fix camera on "me"
}
void Update ()
{
if (!isLocalPlayer)
{
return;
}
var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
var z = Input.GetAxis ("Vertical") * Time.deltaTime * 3.0f;
transform.Rotate (0, x, 0);
transform.Translate (0,0,z);
}
}
On my default main Camera (there is no camera attached to the player prefab, just the default camera) I put the following script on. It takes a target which I initialised with the prefab using the inspector.
using UnityEngine;
using System.Collections;
public class CameraFollow : MonoBehaviour {
public Transform target; //what to follow
public float smoothing = 5f; //camera speed
Vector3 offset;
void Start()
{
offset = transform.position - target.position;
}
void FixedUpdate()
{
Vector3 targetCamPos = target.position + offset;
transform.position = Vector3.Lerp (transform.position, targetCamPos,smoothing * Time.deltaTime);
}
}
After starting the game, each clone tells the camera who he is, so the target changes to the individual clients clone with this line from the Player's Script:
Camera.main.GetComponent<CameraFollow>().target=transform; //Fix camera on "me"
This way you don't need to create one camera per instance of player-prefabs (I'm not sure if this makes big differences in performance) and you don't have to deactivate all cameras which don't belong to your client.
If you host the game in the editor you can see that there is just 1 camera instead of one camera per connected client (like when you attach it to the prefab).
I think this is a good use of this method, you can use it to put things in it, which should be applied to the Local Player only.
public override void OnStartLocalPlayer()
{
}
I tried by starting the game in the editor and in a build and it seems to work well.
I would add a camera to the prefab and then write a player script like this:
using UnityEngine.Networking;
public class Player : NetworkBehaviour
{
public Camera camera;
void Awake()
{
if(!isLocalPlayer)
{
camera.enabled = false;
}
}
}
I've not really worked with networking but what if you do this after you spawn the local player
Camera.main.transfor.SetParent(the transform of the local player here);
As I understand the problem each separate instance of the game has a main camera.
Thanks to Rafiwui's point into the right direction, I've finally managed to get it working. All I had to do was adept his code a bit. The end result was:
public Camera camera;
void Awake()
{
camera.enabled = false;
}
public override void OnStartLocalPlayer()
{
camera.enabled = true;
}
Thanks A LOT to you all for helping me out! This has been quite a day for me.

Code not allowing letting me disable a script from another script

I'm having an issue where I can't disable a script from the other script - they are both public and within the same package (I think).
Here is my code for the script I'm trying to disable from:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif
using RTS;
public class PauseMenu : MonoBehaviour {
Canvas canvas;
private Player player;
public Button Button2;
void Start()
{
Debug.Log ("asdf");
player = transform.root.GetComponent< Player >();
canvas = GetComponent<Canvas>();
canvas.enabled = false;
ResourceManager.MenuOpen = false;
Button2.GetComponent<Button>().onClick.AddListener(() => { Resume();});
if(player) player.GetComponent< UserInput >().enabled = false;
}
And the code for the other script:
//sets up what resources we are using
using UnityEngine;
using System.Collections;
using RTS;
public class UserInput : MonoBehaviour {
//sets up a private variable for only this class - our player
private Player player;
// Use this for initialization
void Start () {
//this goes to the root of the player ie the object player and allows us to
player = transform.root.GetComponent< Player > ();
}//end Start()
So the part that is not working is:
if(player) player.GetComponent< UserInput >().enabled = false;
And the code runs and then causes the runtime error:
NullReferenceException: Object reference not set to an instance of an object
PauseMenu.Pause () (at Assets/Menu/PauseMenu.cs:40)
PauseMenu.Update () (at Assets/Menu/PauseMenu.cs:29)
Here is a picture showing my scene hierarchy and components:
The issue here is that you try to execute transform.root.GetComponent< Player >(); from within PauseMenu, which is on the "Canvas" object.
The problem with that is that the topmost transform in the hierarchy of your "Canvas" object (which is what transform.root returns) is, well, the transform of the "Canvas" object - which is in no way related to the UserInput script you are trying to access. For this script to actually work, you would need the transform of your "Player" object, which is the object that actually has the UserInput script.
My suggestion is to eliminate the need to run GetComponent() at all - create a public UserInput variable in your PauseMenu class, then (while selecting your "Canvas") in the editor, drag the "Player" object into that new field. This will cause the UserInput script of your "Player" object to be accessible within the PauseMenu.
So your PauseMenu script might look like:
public class PauseMenu : MonoBehaviour {
Canvas canvas;
public UserInput playerInput; // Drag the Player object into this field in the editor
public Button Button2;
void Start()
{
Debug.Log ("asdf");
canvas = GetComponent<Canvas>();
canvas.enabled = false;
ResourceManager.MenuOpen = false;
Button2.GetComponent<Button>().onClick.AddListener(() => { Resume();});
playerInput.enabled = false;
}
}
Hope this helps! Let me know if you have any questions.
(An alternative is to use GameObject.Find("Player") to get GameObject of "Player". This needs a bit more code but doesn't use the editor.)
I would say your player = transform.root.GetComponent< Player >(); arrives null.
So you are trying to disable something that doesnt exist.
Enter debug mode and see if your player is null or not.

Categories

Resources