I am getting very strange behavior, if my player instantiate as host (Server+Player) then my command function runs perfectly and certain object become instantiate but if I join the host then my Command doesn't execute on my local player but runs on all other clients.
void Start()
{
if (isLocalPlayer)
{
Debug.Log("It is VR Player spawn-Start");
SetVRLocalPlayerSetting();
//InstantiateMimicVRHeadAndController();
CmdInstantiateMimicVRHeadAndController();
}
else
{
Debug.Log("It is not vr Player its not isLocalPlayer");
DisableSelectedMonobevaiourScripts();
}
}
[Command]//This function have a problem
public void CmdInstantiateMimicVRHeadAndController() {
Debug.Log("instantiateing the controller and head object");
vrHeadCopyObj = (GameObject) Instantiate(vrHeadObjPrefab);
vrRightCtrlCopyObj = (GameObject) Instantiate(vrRightCtrlPrefab);
vrLeftCtrlCopyObj = (GameObject) Instantiate(vrLeftCtrlPrefab);
// spawn the bullet on the clients
NetworkServer.Spawn(vrHeadCopyObj);
NetworkServer.Spawn(vrRightCtrlCopyObj);
NetworkServer.Spawn(vrLeftCtrlCopyObj);
}
Related
I am about to try my hand at developing a small fps multiplayer game. I have encountered a problem with networking. Namely, I have a player that can fire a gun.
When I use the player in single player everything works fine and I can shoot and the target registers the hits. But when I shoot at another player it depends if the host or the player is shooting. If the host shoots, everything works and the bullet comes out of the player, so to speak. However, when I shoot from another client, the bullet spawns at 0,0,0. Also, when I shoot at the host, only the life indicator of the client goes down, although that of the host (the other player) should go down.
// This is the OnCollisionEnter for the bullet
[ServerCallback]
void OnCollisionEnter(Collider other)
{
Debug.Log($"...collider name: {other.gameObject.name}");
CustomStats stats = other.gameObject.GetComponent<CustomStats>();
Debug.Log($"stats exist: {stats != null}, collider name: {other.gameObject.name}");
if (stats != null )
{
if (stats is PlayerStats)
{
Debug.Log("Impact on player!");
if (gameObject.GetComponentInParent<InputMananger>()?.gameObject.name != other.gameObject.name)
{
((PlayerStats)stats).TakeDamage(10);
}
} else if (stats is EnemyStats)
{
Debug.Log("Impact on enemy!");
((EnemyStats)stats).TakeDamage(10);
}
}
}
// This is the calss which is called if the player shoots
public void Shoot()
{
if (isLocalPlayer)
{
gun = GetComponentInChildren<Gun>();
Debug.Log("PewPew!");
CmdFire();
}
/*
if (hitInfo.collider == null) return;
if (hitEnemy)
{
hitInfo.collider.GetComponent<EnemyStats>()?.TakeDamage(gun.damage);
hitEnemy = false;
}
if (hitPlayer)
{
CmdFire();
/*
Debug.Log("player");
NetworkIdentity hitPlayerNet = hitInfo.collider.GetComponent<NetworkIdentity>();
if (hitPlayerNet != null)
{
PlayerStats playersStats = hitPlayerNet.gameObject.GetComponent<PlayerStats>();
Debug.Log($"player script: {playersStats == null}");
playersStats?.TakeDamge(gun.damage);
}
hitPlayer = false;
}
*/
hitPlayer = false;
}
// this is called on the server
[Command]
void CmdFire()
{
Debug.Log(" Caller spawn projectile!");
GameObject projectile = Instantiate(projectilePrefab, projectileMount.position, cameraMount.rotation);
projectile.GetComponent<Projectile>()?.SetDamage(10);
projectile.transform.SetParent(projectileMount);
NetworkServer.Spawn(projectile);
RpcOnFire();
}
// this is called on the tank that fired for all observers
[ClientRpc]
void RpcOnFire()
{
Debug.Log(" start animation on caller!");
// shoot animation
}
I have tried many different ways to access the other player. Currently I have a script on the bullet that has an "OnCollisionEnter" function that I want to use to access my collider. In the single player it works as mentioned above but when I shoot from the host to the client it doesn't work.
I'm working on a multiplayer game and I'm encountering a issue when 2 players are loading the same level using PhotonNetwork.LoadLevel().
When I start the game alone, I can control my player and everything is fine. But when we are 2 players, Player A is controlling Player B and vice-versa.
I check a lot of links on the internet these past few days, and I learned the concept of PhotonNetwork.IsMine which I thought would solve all of my problem but it seems to not working with me. Also, I'm using the new input system of Unity but I don't think the issue come from here.
Basically, what I'm doing is:
Instantiate a player (this happened twice since I have 2 players) which have a PlayerManager
Player Manager Get Instance of the local player and synchronize camera with the local player only if isMine = true
CameraManager creates input manager if the script is linked to the local player by using isMine
Link the main camera to this script when the gamemanager request it
Update camera rotation only when isMine is true (second protection)
Here is a piece of my code:
GameManager.cs (Holding by a Scene Object, so it is initially instantiated for everyone with the scene)
void Start()
{
if(PlayerManager.LocalPlayerInstance == null)
{
//Get player's team
string team = (string)PhotonNetwork.LocalPlayer.CustomProperties["Team"];
int indexPlayer = GetSpawnPosition();
//Spawn player depending on its team and its index in the players pool
if (team.Equals("Spy"))
{
PhotonNetwork.Instantiate(this.spyPrefab.name, SpySpawns[indexPlayer].position, SpySpawns[indexPlayer].rotation);
}
else if (team.Equals("Defender"))
{
PhotonNetwork.Instantiate(this.defenderPrefab.name, SpySpawns[indexPlayer].position, SpySpawns[indexPlayer].rotation);
}
}
}
PlayerManager.cs (Holding by the player, so not initially instantiated with the scene)
void Awake()
{
//Keep track of the localPlayer to prevent instanciation when levels are synchronized
if (photonView.IsMine)
{
LocalPlayerInstance = gameObject;
}
//Don't destroy this gameobject so it can survives level synchronization
DontDestroyOnLoad(gameObject);
}
private void Start()
{
//Get Manager of the camera of the player and attach to the local player
CameraLookFPS cameraFPSManager = gameObject.GetComponent<CameraLookFPS>();
if (cameraFPSManager != null)
{
//Ensure that we the local player is controlling its own camera
if(photonView.IsMine == true)
{
cameraFPSManager.SynchronizeWithLocalPlayer();
}
}
else
Debug.Log("This player is missing the CameraLookFPS component");
}
CameraManager.cs (Holding by the player, so not initially instantiated with the scene)
private void Start()
{
//Synchronize camera with local player on start for debug
if(SynchronizeOnStart)
{
SynchronizeWithLocalPlayer();
}
//Bind input for controlling the camera
BindingInput();
}
private void BindingInput()
{
// Prevent control is connected to Photon and represent the localPlayer
if (photonView.IsMine == false && PhotonNetwork.IsConnected == true)
{
return;
}
else
{
//Get Components
Input_Master = new InputMaster();
//Enable
Input_Master.Enable();
//Input binding
Input_Master.Player.Look.performed += context => MoveCamera(context);
Input_Master.Player.Look.Enable();
}
}
public void SynchronizeWithLocalPlayer()
{
if (photonView.IsMine == false && PhotonNetwork.IsConnected == true)
{
return;
}
Player_Camera = Camera.main.transform;
isSynchronized = true;
}
I tried to be clear enough, tell me if something is bad explained. I'll continue my research on my side and I'll keep you in touch if I find something.
Thanks in advance for your help!
Adrien
I finally found my solution. It appears that I already read about it but I have failed when trying to solve it.
So, like a lot of people, it is a Camera issue.
What I did is:
Create a game object holding the camera and put it as a child of the player gameobject
Deactivate the game object holding the camera
When instantiating the player, check if the player is the local player, using PhotonNetwork.IsMine. If yes, activate the gameobject holding the camera through script
If you have questions, send me a message!
Adrien
I am working on a multiplayer game on Unity using Unet, when i host a match by playing the game on the Unity editor, everything works as intended, but when i host a match on a built game (Running the game by clicking "build and run"), the networking functions don't work. I can't understand very well what is happening but i think i understood that the network server is someway inactive. This is weird because, as i said before the network server works when i host a match by playing the game in the Unity editor.
//this function spawns an enemy prefab
void Spawn()
{
GameObject enemy = Instantiate(enemyPrefab, spawnPoint.position,
spawnPoint.rotation);
NetworkServer.Spawn(enemy);
}
/* this function is in the player script, when the enemy or the enemy bullet touches the player, the player gets damaged */
private void OnTriggerEnter2D(Collider2D hitInfo)
{
//Enemy enemy = hitInfo.GetComponent<Enemy>();
if (hitInfo.tag == "Enemy" || hitInfo.tag == "EnemyBullet")
{
RpcTakeDamage(10, "hit by enemy");
}
//Instantiate(impactEffect, transform.position, transform.rotation);
}
[ClientRpc]
public void RpcTakeDamage(int _amount, string _sourceID)
{
if (!isHit)
{
StartCoroutine("HurtColor");
currentHealth -= _amount;
Debug.Log(transform.name + " now has " + currentHealth + " health ");
if (currentHealth <= 0)
{
Die(_sourceID);
}
}
}
/* this function is in the bullet behaviour script, when a bullet touches an enemy, the enemy gets destroyed. */
private void OnTriggerEnter2D(Collider2D hitInfo)
{
playerMovement player = hitInfo.GetComponent<playerMovement>();
if (hitInfo.tag == "Enemy")
{
player = FindObjectOfType<playerMovement>();
player.bombPower = player.bombPower + UnityEngine.Random.Range(0.1f, 0.5f);
if(hitInfo.tag == "Enemy")
{
//Enemy enemy = FindObjectOfType<Enemy>();
//enemy.currentHealth -= damage;
player.CmdEnemyShot(hitInfo.transform.name, this.transform.name); //change this.transform.name with the player's name
}
Destroy(gameObject);
}
Here are some error messages:
/* when a player joins the match, i get this warning, i can not understand what command can't be sent, probably is from the NetworkBehaviour class */
"Trying to send command for object without authority."
/* this error shows when an enemy gets spawned, the enemy is still visible but apparently only on client side */
"SpawnObject for EnemyUpRight(Clone) (UnityEngine.GameObject),
NetworkServer is not active. Cannot spawn objects without an active server."
/* when an enemy bullet hit a plyer, this error shows up and the player can't take damage. */
"RPC Function RpcTakeDamage called on client."
//also this warning shows up
"Did not find target for sync message for 9"
//when an enemy is shot, the enemy doesn't die and this error shows up
"SpawnObject for Marisa_bullet(Clone) (UnityEngine.GameObject), NetworkServer is not active. Cannot spawn objects without an active server."
If you need my project to understand, here is the link.
The code can be a bit messy.
I am making a game using UNET and making it for the HoloLens. Basically, my game is pretty simple, players join the session and then they can spawn a ship to control with an Xbox controller. I have the move mechanics and even a basic shoot function working. What I really want to add next is something like a homing missile. I have the code for the homing missile and it works fine on the host but, I can't figure out how to get my missile object information back to the player if that makes sense. I will show you what I mean.
My Command:
[Command]
public void CmdFireMissle()
{
Vector3 bulletDir = planeToSpawn.transform.forward;
Vector3 bulletPos = planeToSpawn.transform.position + (bulletDir * (0.01f + 3 * planeToSpawn.transform.localScale.x));
// The bullet needs to be transformed relative to the shared anchor.
missleToSpawn = Instantiate(missle, sharedWorldAnchorTransform.InverseTransformPoint(bulletPos), Quaternion.LookRotation(bulletDir));
missleToSpawn.transform.localScale = planeToSpawn.transform.localScale * 0.1f;
missleToSpawn.GetComponentInChildren<Rigidbody>().velocity = bulletDir * 1.0f;
NetworkServer.Spawn(missleToSpawn);
RpcPlayBulletAudio(planeToSpawn);
// Clean up the bullet in 15 seconds.
Destroy(missleToSpawn, 15.0f);
}
Update Method:
void Update()
{
if (controllerInput.GetButtonDown(ControllerButton.A) && planeSpawned )
{
if (isLocalPlayer)
{
bool raycastHit = Physics.Raycast(transform.position, direction: transform.forward, hitInfo: out hit, maxDistance: range);
if (raycastHit && hit.transform.gameObject.CompareTag("Plane"))
{
enemyShip = hit.transform.gameObject;
CmdFireMissle();
// I need to get a reference to my missleToSpawn object on my client
// So I can use it here in this coroutine
StartCoroutine(MoveTo(missleToSpawn, misslePos, enemyShip, 1));
}
}
}
}
It works fine on the host since it has the reference when it calls the Command I just can't figure out how to get the reference for the client.
#Dtb49 you can attach a script to the "missle" object, while this script is used to get the spawn object's NetworkInstanceId once it's spawned.
public class YourScript: NetworkBehaviour {
public static NetworkInstanceId nid;
void Start(){
nid = this.netId;
}
}
In other script, you can get the object reference by using NetworkServer.objects[NetworkInstanceId].gameObject like:
if (NetworkServer.objects.ContainsKey (YourScript.nid)) {
Debug.Log (NetworkServer.objects[YourScript.nid].gameObject);
}
So I spawn players and than guns that they have, on a host it works perfectly but on a client guns do not become instance of an object. Here's the code
[Command]
void CmdSpawn() {
gun = (GameObject)Instantiate(gunToEquip, weaponPosition.position, weaponPosition.rotation);
NetworkServer.SpawnWithClientAuthority(gun, connectionToClient);
gun.transform.parent = weaponPosition;
}
First of all, you aren't setting the parent on client. Only on the server. The host is server and client at the same time so it works. Second - have you dragged a gun prefab into network manager's spawnable prefabs slot?
Also, I believe you shouldn't be doing it like so:
gun.transform.parent = weaponPosition;
Use that instead:
gun.transform.SetParent(weaponPosition);
Try that:
[Command]
void CmdSpawn()
{
gun=(GameObject)Instantiate(gunToEquip,weaponPosition.position,weaponPosition.rotation);
gun.transform.SetParent(weaponPosition);
RpcSpawn();
}
[ClientRpc]
void RpcSpawn()
{
if(NetworkServer.active) return;
gun = (GameObject)Instantiate(gunToEquip, weaponPosition.position, weaponPosition.rotation);
gun.transform.SetParent(weaponPosition);
}
It seems to be a duplicate of Unity 5.1 Networking - Spawn an object as a child for the host and all clients
The idea discussed there is to synchronize network ID of the parent object, e.g. on the Server side while spawning:
var gun = Instantiate (...);
gun.parentNetId = this.netId;
NetworkServer.Spawn (gun);
And later on the clients:
// GunController.cs
[SyncVar]
public NetworkInstanceId parentNetId;
public override void OnStartClient()
{
GameObject parentObject = ClientScene.FindLocalObject(parentNetId);
transform.SetParent(parentObject.transform);
}