I'm quite new to Mirror. Let me try to explain my setup and problem:
On client-side, player spawn multi-gameobject and select the only one which he would use in a race. For example, he has 4 cars in his garage, when the scene Garage is loaded, there are 4 car-object has been spawned in the scene. And when he select the desired car to use in a race, the Player property in PlayerManager instance will update with the selected car value.
public class PlayerManager : MonoBehaviour {
public static PlayManager Instance;
private void Awake() {
if (Instance != null) Destroy(gameObject);
Instance = this;
}
public Player Player {get; private set;}
public static void UpdateSelectedCar() {
Car car = CarSpawner.SelectedCar;
Player.Car = car;
}
}
public class Player () {
public Car Car { get; set; }
public string Name;
....
public Player(string playerName){
Name = playerName;
}
}
public class Car() {
public GameObject;
public CarName;
.....
public void Instantiate() {
GameObject = Object.Instantiate(carPrefab, carPosition);
}
}
In the NetworkManagerCar, I have setup as below:
public class NetworkManagerCar : NetworkManager {
// There are Host and Join Button in the UI for create host or join as client
private void OnEnable() {
hostBtn.onClick.AddListener(CreateHost);
joinBtn.onClick.AddListener(JoinGame);
}
private void OnDisable() {
hostBtn.onClick.RemoveListener(CreateHost);
joinBtn.onClick.RemoveListener(JoinGame);
}
private void CreateHost() {
StartHost();
networkStatus.text = "Waiting for other to join...";
}
private void JoinGame() {
StartClient();
networkStatus.text = "Finding Match...";
}
public override void OnClientConnect() {
PlayerManager.UpdateSelectedCar();
playerPrefab = PlayerManager.Instance.Player.Car.GameObject;
Debug.Log($"Prefab name: {playerPrefab.name}");
base.OnClientConnect();
Debug.Log($"{PlayerManager.Instance.Player.Name} connected");
}
public override void OnServerAddPlayer(NetworkConnectionToClient conn) {
GameObject player = playerPrefab;
player.name = $"{playerPrefab.name} [connId={conn.connectionId}]";
NetworkServer.AddPlayerForConnection(conn, player);
}
}
It is no problem when I host the game. However, when 1 client joined, I got the bug below. I use ParrelSync to clone client, I have already tried to delete the clone and create a new one but the bug is still:
On host console:
Object Car10(Clone) [connId=0] [connId=250806295] [connId=701038615] (Mirror.NetworkIdentity) netId=2 already has an owner. Use RemoveClientAuthority() first
UnityEngine.Debug:LogError (object,UnityEngine.Object)
On client console:
Spawn scene object not found for CE359B9F. Make sure that client and server use exactly the same project. This only happens if the hierarchy gets out of sync.
Could not spawn assetId=00000000-0000-0000-0000-000000000000 scene=CE359B9F netId=1
OnSpawn message with netId '2' has no AssetId or sceneId
Could anyone tell me where I was wrong :(
You need instantiate "playerPrefab" before passing to AddPlayerForConnection
GameObject player = Instantiate(playerPrefab);
player.name = $"{playerPrefab.name} [connId={conn.connectionId}]";
NetworkServer.AddPlayerForConnection(conn, player);
Related
I'm currently making a simple game for my project, which is spawning food and damaging it until its vanishes. I have to write a script that instantiates a random object from an array, attaches the script to it, and damages the instantiated food in another script. The current problem now is Unity telling me that I have a Null Reference Exception on both scripts, I tried to fix it by attaching the script to the instantiated object, but still, the problem remains.
Here the code on the script that attaches to the instantiated object, and also spawning the object too:
`
public class Food : MonoBehaviour
{
public GameObject[] food;
public Vector3Int spawnPosition;
public int health = 200;
public int currentHealth;
private GameObject clone;
public void Start()
{
currentHealth = health;
SpawnFood();
}
//Spawning food
public void SpawnFood()
{
int random = Random.Range(0, food.Length); //Null Reference Exception happen in this line.
clone = Instantiate(food[random], this.spawnPosition, Quaternion.identity) as GameObject;
clone.AddComponent<Food>();
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
//play hurt effect
if(currentHealth < 0)
{
Vanish();
}
}
void Vanish()
{
Debug.Log("Vanished");
}
}
`
Here is the other script:
`
public class Board : MonoBehaviour
{
public Tilemap tilemap { get; private set; }
public Piece activePiece { get; private set; }
public TetrominoData[] tetrominoes;
public Vector3Int spawnPosition;
public Vector2Int boardSize = new Vector2Int(10, 20);
public int damage;
public Food clone;
public TextMeshProUGUI hud_score;
public static int currentScore = 0;
public int scoreOneLine = 40;
public int scoreTwoLine = 100;
public int scoreThreeLine = 300;
public int scoreFourLine = 1200;
private int numberOfRowsThisTurn = 0;
public RectInt Bounds
{
get
{
Vector2Int position = new Vector2Int(-this.boardSize.x / 2, -this.boardSize.y / 2);
return new RectInt(position, this.boardSize);
}
}
private void Awake()
{
this.tilemap = GetComponentInChildren<Tilemap>();
this.activePiece = GetComponentInChildren<Piece>();
//call Tetromino.Initialize() to spawn pieces
for (int i = 0; i < this.tetrominoes.Length; i++)
{
this.tetrominoes[i].Initialize();
}
}
private void Start()
{
SpawnPiece();
}
private void Update()
{
UpdateScore();
UpdateUI();
}
public void UpdateUI()
{
hud_score.text = currentScore.ToString();
}
public void UpdateScore()
{
if(numberOfRowsThisTurn > 0)
{
if(numberOfRowsThisTurn == 1)
{
ClearedOneLine();
}
else if (numberOfRowsThisTurn == 2)
{
ClearedTwoLine();
}
else if (numberOfRowsThisTurn == 3)
{
ClearedThreeLine();
}
else if (numberOfRowsThisTurn == 4)
{
ClearedFourLine();
}
numberOfRowsThisTurn = 0;
}
}
public void ClearedOneLine()
{
currentScore += scoreOneLine;
clone.GetComponent<Food>().TakeDamage(10); //Null Reference Exception happen in this line.
}
public void ClearedTwoLine()
{
currentScore += scoreTwoLine;
clone.GetComponent<Food>().TakeDamage(20); //Null Reference Exception happen in this line.
}
public void ClearedThreeLine()
{
currentScore += scoreThreeLine;
clone.GetComponent<Food>().TakeDamage(40); //Null Reference Exception happen in this line.
}
public void ClearedFourLine()
{
currentScore += scoreFourLine;
clone.GetComponent<Food>().TakeDamage(80); //Null Reference Exception happen in this line.
}
`
Please help and thank you for the help.
Here is the Inspector image with 64 elements in an array:
I have tried to attach the script to the instantiated object when that object is being spawned.
I don't see food set anywhere. You need to populate the array inside Food from the Inspector or at least, populate programmatically in Food.Start() before using it.
Can you also show us a printscr of the Inspector view where Food is attached as component? The main focus here is the food array.
the food array is empty it should be filled by you,
instantiate doesn't fill it, it takes an object from the array and spawn it.
fill it with prefabs of foods that already has the food component no need to add it in code
At first my model was humanoid and the game worked fine. I had to change it to generic though and after that I got this error:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Pokemon.Fight
{
public class PokemonTrainer : MonoBehaviour
{
//public Animator Prefab
public Animator animator { get; set; }
private void Awake()
{
animator = GetComponentInChildren<Animator>();
}
public void SpawnTrainer(/*TrainerSO trainer*/)
{
//animator = Instantiate(trainer.trainerPrefab, transform);
//animator.transform.localPosition = Vector3.zero;
}
public void Throw(bool isPlayer, int id, int level)
{
StartCoroutine(ie_throw(isPlayer, id, level));
}
private IEnumerator ie_throw(bool isPlayer, int id, int level)
{
animator.SetTrigger("throw");
yield return new WaitForSeconds(0.11f);
PokemonBall ball = PokemonGameplay.Instance.pokemonEffectPool.GetPokemonBall();
ball.Throw(animator.GetBoneTransform(HumanBodyBones.RightHand).position, isPlayer, id, level);
}
//public void SetEndgame(bool isWin)
//{
// if (isWin) animator.CrossFadeInFixedTime("Victory", 0.1f);
// else animator.CrossFadeInFixedTime("Defeat", 0.1f);
//}
public void OnRebind()
{
animator.Rebind();
}
}
}
Here is the code that had the error and "PokemonBall ball = PokemonGameplay.Instance.pokemonEffectPool.GetPokemonBall();" This line in iethrow was the reason for it. Idk how to solve this, I changed nothing in my code when I change the model. And for this my Pokeball can't run in a right way.
Tried to figure out what is null but noothing is. The ball can still spawn but it headed to a wrong position(I set the position for it).
when I load a new scene I get this error: NullReferenceException: Object reference not set to an instance of an object GameController+d__16.MoveNext () (at Assets/Scrips/GameController.cs:76) UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at :0)
this is my script to save:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Scene_Manager : MonoBehaviour
{
int Saved_scene;
int Scene_index;
public void Load_Saved_Scene()
{
Saved_scene = PlayerPrefs.GetInt("Saved");
if (Saved_scene != 2)
SceneManager.LoadSceneAsync(Saved_scene);
else
return;
}
public void Save_and_Exit()
{
Scene_index = SceneManager.GetActiveScene().buildIndex;
PlayerPrefs.SetInt("Saved", Scene_index);
PlayerPrefs.Save();
SceneManager.LoadSceneAsync(0);
}
public void Next_Scene()
{
Scene_index = SceneManager.GetActiveScene().buildIndex + 1;
SceneManager.LoadSceneAsync(Scene_index);
}
}
and this is my script for the whole game where I call save:
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
[RequireComponent(typeof(GameUI))]
public class GameController : MonoBehaviour
{
public static GameController Instance { get; private set; }
[SerializeField]
private int knifeCount;
[Header("Knife Spawning")]
[SerializeField]
private Vector2 knifeSpawnPosition;
[SerializeField]
private GameObject knifeObject;
public GameUI GameUI { get; private set; }
private void Awake()
{
Instance = this;
GameUI = GetComponent<GameUI>();
}
private void Start()
{
GameUI.SetInitialDisplayedKnifeCount(knifeCount);
SpawnKnife();
}
public void OnSuccessfulKnifeHit()
{
if (knifeCount > 0)
{
SpawnKnife();
}
else
{
StartGameOverSequence(true);
}
}
private void SpawnKnife()
{
knifeCount--;
Instantiate(knifeObject, knifeSpawnPosition, Quaternion.identity);
}
public void StartGameOverSequence(bool win)
{
StartCoroutine("GameOverSequenceCoroutine", win);
}
private IEnumerator GameOverSequenceCoroutine(bool win)
{
if (win)
{
yield return new WaitForSecondsRealtime(0.3f);
FindObjectOfType<LevelLoader>().LoadNextLevel();
FindObjectOfType<Scene_Manager>().Save_and_Exit();
}
else
{
GameUI.ShowRestartButton();
}
}
public void RestartGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex, LoadSceneMode.Single);
}
}
Now first of all take a look at this post to understand NullReferenceExceptions.
After that take a closer look at your error message:
NullReferenceException: Object reference not set to an instance of an object GameController+d__16.MoveNext () (at Assets/Scrips/GameController.cs:76)
The (at Assets/Scrips/GameController.cs:76) part tells you exactly where you error is thrown (which basically means where it occured). It's in your Assets/Scrips/GameController.cs script at line 76.
private IEnumerator GameOverSequenceCoroutine(bool win)
{
if (win)
{
yield return new WaitForSecondsRealtime(0.3f);
FindObjectOfType<LevelLoader>().LoadNextLevel();
FindObjectOfType<Scene_Manager>().Save_and_Exit(); // <--- HERE
}
...
}
In your specific implementation my best guess would be that you shouldn't load your new scene before you try to save & quit. You're starting to load your new scene asynchronously and then call another function which loads another scene (?) while not actually preventing the first scene from activating until your other functions actually executed properly. There's a plethora of cases where this can and will break.
I think you'll need to rethink what it is your actually trying to accomplish because these function calls don't make sense like that.
public class GameControl : NetworkBehaviour {
public GameObject localPlayer;
[TargetRpc]
public void TargetGetLocalPlayer(NetworkConnection conn)
{
localPlayer = GameObject.Find("Local");
}
public override void OnStartServer()
{
base.OnStartServer();
TargetGetLocalPlayer(connectionToClient);
}
}
i have a script attached to a server object which should be able to grab the local player GameObject (which i denoted by changing it's name to 'Local' once it's spawned in another script) from the client but when i try to call TargetGetLocalPlayer , i get the following error :
Exception in OnStartServer:Object reference not set to an instance of an object at
UnityEngine.Networking.NetworkBehaviour.SendTargetRPCInternal
(UnityEngine.Networking.NetworkConnection conn, UnityEngine.Networking.NetworkWriter writer,
System.Int32 channelId, System.String rpcName) [0x0002e]
i am totally new to networking in unity and i feel like i should have gone with photon instead of unet , it seems like no one is interested in unet anymore and the docs suck at explaining anything and i will be very grateful if anyone could answer me , thanks in advance
I think a better solution would be to attach a script to each player. and make it so that when a "new" player joins it runs a method in your GameControl to add the player to a player list. like this:
//this is on your gameControl.
public List<Player> players;
public void AddPlayer(Player player)
{
players.Add(player);
}
this is working if you have your GameControl as a singleton. if you do not know how to do that check the last piece of code.
//this is on your Player script called player(if you have another name change all
//Player scripts in here to that name
public void Start()
{
GameControl.AddPlayer(this);
}
Or
Instead of making the players list a List you can make it a dictionary and make a key for each player who joins to make it more accesible.
How to make a script a singleton and why.
why:
if you have a manager class/script you always want there to only be ONE instance of it so you can call its methods easily and without problems.
How:
Basically you make it so that Only THIS script can change values and variables in the manager, and other scripts can get/call methods and functions. this makes it easily accesible and you will have less problems.
private static GameControl _GameControl;
private Player player;
public static GameControl gameControl
{
get
{
if(_GameControl == null)
{
Debug.LogError("Game Control is null");
}
return _GameControl;
}
}
void Awake()
{
_GameControl = this;
player = GameObject.Find("Player").GetComponent<Player>();
}
Well i see what you mean, well create a method that can be run by any script like a singleton. then you pass in the gameobject that you want to add like this:
public class GameManager
{
public GameObject _player;
//this is a singleton.
private static GameManager _gm;
public static GameManager gameManager
{
get
{
if(_gm == null)
{
Debug.LogError("Game manager is null");
}
return _gm;
}
}
void awake()
{
_gm = this;
}
void GetPlayer(GameObject player)
{
_player = player;
}
void AddPlayer(GameObject player)
{
//add it to whatever you want to.
}
}
call the method this way:
public class Player : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
GameManager.gameManager.GetPlayer(this.gameObject);
}
}
I am doing a multiplayer game and I want to know how I can add a minimum number of players for a user to enter a room . Like to not be alone till one player is connected at least. What I should add to my script? I have this Room Options function but It doesn't work too add like Min players or something to it. (I expected if is MaxPlayers to exist MinPlayers too)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon;
using Photon.Pun;
using UnityEngine.UI;
using Photon.Realtime;
public class MPManager : MonoBehaviourPunCallbacks, IPunObservable
{
public PlayFabAuth auth;
public string GameVersion;
public Text connectState;
public GameObject[] DisableOnConnected;
public GameObject[] DisableOnJoinRoom;
public GameObject[] EnableOnConnected;
public GameObject[] EnableOnJoinRoom;
public LoadedPlayer loadPlayer;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void FixedUpdate()
{
connectState.text = "Connection: " + PhotonNetwork.NetworkClientState;
}
public void ConnectToMaster()
{
// PhotonNetwork.connectionStateDetailed
PhotonNetwork.ConnectUsingSettings();
}
public override void OnConnectedToMaster()
{
PhotonNetwork.JoinLobby();
}
public override void OnJoinedLobby(){
foreach(GameObject disable in DisableOnConnected){
disable.SetActive(false);
}
foreach (GameObject enable in EnableOnConnected){
enable.SetActive(true);
}
}
public void CreateOrJoin()
{
PhotonNetwork.LeaveLobby();
PhotonNetwork.JoinRandomRoom();
}
public override void OnJoinRandomFailed(short returnCode, string message)
{
RoomOptions rm = new RoomOptions
{
MaxPlayers = 3,
IsVisible = true
};
int rndID = Random.Range(0, 3000);
PhotonNetwork.CreateRoom("Default: " + rndID, rm, TypedLobby.Default);
}
public override void OnJoinedRoom()
{
foreach (GameObject disable in DisableOnJoinRoom)
{
disable.SetActive(false);
}
foreach (GameObject enable in EnableOnJoinRoom)
{
enable.SetActive(true);
}
Debug.Log(loadPlayer.currentPlayer.name);
GameObject player = PhotonNetwork.Instantiate(loadPlayer.currentPlayer.name , Vector3.zero, Quaternion.identity, 0);
}
public override void OnLeftRoom()
{
PhotonNetwork.LeaveRoom();
}
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
throw new System.NotImplementedException();
}
}
Thank you for choosing Photon!
You need to get the count of actors (PhotonNetwork.CurrentRoom.PlayerCount) when you join the room (OnJoinedRoom) or when another player joins the room (OnPlayerEnteredRoom). When the number of actors joined is enough for you, start the game logic, e.g. load scene, send a custom event etc.