Problem with Networking in unity and mirror - c#

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.

Related

Unity New Input Manager and Photon issue

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

Unity UNET network server not active when hosting a match on built game

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.

What is a good way to use animation events to process melee attacks in Unity 3d?

Okay so I'm working on like a hack and slash kind of game in Unity and I have an enemy object who I'd like have attack the player and deal damage using colliders. I have added an event to the enemy's attack animation that calls the function OnTriggerEnter(Collider other) that should deal the damage but I get a error saying "Object reference not set to an instance of an object" whenever I try it out. Does anyone have any ideas on how I could make this work?
This code snippet can be very helpful for you to have an idea. I made this in an old project and works perfectly.
In this case is a right leg kick animation.
NOTE: code snippet not tested. “Health” script source is not included.
public class MeleeAttackController : MonoBehaviour
{
public SphereCollider kickAttackSphere; // in this case the sphere collider must be child of the right foot
public float meleeAttackDamage = 50;
public AudioClip kickAttackClip;
CapsuleCollider m_capsule;
void Awake()
{
kickAttackSphere.isTrigger = true;
m_capsule = GetComponent<CapsuleCollider>();
}
void Event_KickAttack_RightLeg() // invoked by the kick animation events (in case you have many kick animations)
{
if(CheckSphereAndApllyDamage(kickAttackSphere))
{
if (kickAttackClip)
{
AudioSource.PlayClipAtPoint(kickAttackClip, weaponAttackSphere.transform.position); // play the sound
}
}
}
/// <summary>
/// Returns true if the sphere collider overlap any collider with a health script. If overlap any collider also apply a the meleeAttackDamage.
/// </summary>
bool CheckSphereAndApllyDamage(SphereCollider sphere)
{
// check if we hit some object with a health script added
Collider[] colliders = Physics.OverlapSphere(sphere.transform.position, sphere.radius, Physics.AllLayers, QueryTriggerInteraction.Ignore);
foreach (Collider collider in colliders)
{
if (collider == m_capsule) continue; // ignore myself
Health health = collider.GetComponent<Health>();
if (health)
{
// if the collider we overlap has a health, then apply the damage
health.TakeDamage(meleeAttackDamage, transform);
return true;
}
}
return false;
}
}

Command is not executing when local player is not host

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);
}

unity OnTriggerStay2D() for two triggers

I am using unity 5 c# and I have a gameobject with 2 trigger colliders one of them is in a different location.
I need to be able to use OnTriggerStay2D and OnTriggerEnter2D for them but I need to find what trigger is being entered. Right now if I enter the 1st(polygon) trigger the OnTriggerEnter activates for the 2nd(box).
How can I Tell the two colliders apart???
public void OnTriggerEnter2D(Collider2D other) //2nd collider trigger
{
if (other.tag == "Player") {
Found = true; //if the player is in shooting range
Idle = false;
}
}
public void OnTriggerStay2D(Collider2D other) //1st collider trigger
{
if (Found != true) {
if (other.tag == "Player") {
Shield = true;
Idle = false;
}
}
}
public void OnTriggerExit2D(Collider2D other) //2nd collider trigger
{
if (other.tag == "Player") {
Found = false;
Shield = false;
Shooting = false;
Idle = true;
}
}
I have tried making the 1st trigger public void OnTriggerStay2D(PolygonCollider2D other) but it says "This message parameter has to be of type: Collider2D
The message will be ignored."
What I am trying to do is have a polygon trigger in front of the gameobject and a different box trigger closer to the gameobject so when you go near the gameobject you enter the 1st trigger and it puts its shield up but when you get close to it (within shooting range of it) it will put its shield down and start shooting you.
Well collider2d detects all types of 2d colliders. It doesn't matter if it's polygon or just a box. As the documentation suggestions it doesn't need to be public or private. It only takes a collider2d as it's argument however.
For debugging purposes why not use print?
Print("you've entered the trigger function");
Also I wouldn't use 2 different trigger colliders on the same GameObject. Why not just make 2 separate gameobjects so you can have more thorough detection. Each GameObject with its own trigger collider can have different tags.
If you have to use 2 trigger colliders on one object. Which isn't the best idea. You could use shapeCount to determine which one it's hitting. Although like I said I would warrant against doing 2 trigger colliders on the same object when whatever you're trying to do can be easier on two separate objects.
However links aren't usually prohibited I think. I would watch and study these videos. They're very useful for explaining the engine and they really aren't even that long.
https://unity3d.com/learn/tutorials/modules/beginner/2d
They even have a video explaining 2d colliders.
This is my fix. In one of my games I have a boulder, I have a trigger which will delete a block below it so it falls, I then have another trigger which tells the boulder to start moving left or right I then also have another trigger which will delete the boulder once the boulder comes in contact.
So what you can do is create 2 new game objects, create a new CS file and name them appropriately, then with those two new classes allow them to take in the gameobject you are referring to in your question.
Then when they are triggered you can use code from their class.
So your first class would become something like this
public void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player") {
Enemy.Found = true; //if the player is in shooting range
Enemy.Idle = false;
}
}
public void OnTriggerExit2D(Collider2D other)
{
if (other.tag == "Player") {
Enemy.Exit2DTrigger();
}
}
Then the other class would be something like this
public void OnTriggerStay2D(Collider2D other)
{
if (Enemy.Found != true) {
if (other.tag == "Player") {
Enemy.Shield = true;
IEnemy.dle = false;
}
}
}
public void OnTriggerExit2D(Collider2D other)
{
if (other.tag == "Player") {
Enemy.Exit2DTrigger();
}
}
Then in your Enemy class you would have
public void Exit2DTrigger()
{
Found = false;
Shield = false;
Shooting = false;
Idle = true;
}
P.S. also don't you need to use other.gameObject.tag == "Player" ?

Categories

Resources