I have a Game class and a Multiplayer class which handle all network players stuff for defined game:
export interface Multiplayer{
game: Game
start()
}
export class WebSocketMultiplayer implements Multiplayer{
constructor(public game: Game){}
start(){}
}
inversify configuration:
container.bind<Game>('Game').to(BasicGame)
container.bind<Multiplayer>('Multiplayer').to(WebSocketMultiplayer)
now I want to create, configure and run game and after that - run multiplayer.
const game = kernel.get<Game>('Game')
game.run()
const multiplayer = kernel.get<Multiplayer>('Multiplayer')
multiplayer.start()
but how should I pass game instance into Multiplayer constructor?
If I will use #inject in WebSocketMultiplayer constructor than it will create another game instance.
Temporary solution I'm using for now is to pass game instance in multiplayer start function
start(game: Game){
this.game = game
}
but how it suppose to be done with Inversify?
You ca try a few things.
The first options is to use the inSingletonScope method:
container.bind<Game>('Game').to(BasicGame).inSingletonScope();
You can learn more about scope here.
The second option is to use a factory:
container.bind<Game>('Game').to(BasicGame);
container.bind<Multiplayer>('Multiplayer').to(WebSocketMultiplayer);
container.bind<() => Game>("GameFactory").toFactory<Game>((context: interfaces.Context) => {
const game = context.container.get<Game>("Game");
game.run();
return () => game;
});
class WebSocketMultiplayer implements Multiplayer{
public game: Game;
constructor(#inject("GameFactory") gameFactory: () => Game){
this.game = gameFactory();
}
start(){}
}
If game.run() is asynchronous then you will need an asynchronous factory (AKA provider).
Related
A simple task. I have a ScoreController which will set the score to UI. I have a UI label that has ScoreView in it
The question is how do I get this ScoreView in my controller
I have found out the way to get View from prefab that's being instantiated:
public class CellViewFactory : ICellViewFactory
{
public ICellView GetView(Vector3 position)
{
var prefab = Resources.Load<GameObject>("Cell");
var instance = UnityEngine.Object.Instantiate(prefab);
var cellView = instance.GetComponent<ICellView>();
cellView.SetPosition(position);
return cellView;
}
}
But since UI isn't instantiated by the code, what's the way to get the ScoreView from it?
Now, I have found an option to call a static method of a Factory and pass self to the method. Smth like this:
public class ScoreView : MonoBehaviour
{
void Start()
{
ScoreViewFactory.NotifyScoreViewCreated(this);
}
}
But it doesn't seem to be flexible using static methods in decoupled code with Factories and interfaces. Hope there's another way
I would also be glad if you post some complete tutorioal on MVC in Unity. Seems that there's no complete working tutorials.
It would also be great if you would share an example of MVC pattern in Unity using DependencyInjection.
I am making a game now, it's almost done. now I am trying to control the audio on and off by button or toggle button.
The problem is, I put my audio source gameobject in the splashscreen that is in the 1st scene. and I put the audio or music button in the Setting scene which is inside the 3rd scene. I already make the c# script to control the audio but when I've tried to insert the AudioSource, but it can't since it's from a different scene. I've tried to put the AudioSource in the same scene but the audio didn't start except I go to settings scene first.
Here is the script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Music : MonoBehaviour
{
static Music instance = null;
public AudioSource Backsound;
private void Awake()
{
if (instance != null)
{
Destroy(gameObject);
}
else
{
instance = this;
GameObject.DontDestroyOnLoad(gameObject);
}
}
public void backsoundOnOff()
{
AudioSource bgsound = Backsound.GetComponent<AudioSource>();
if (bgsound.mute = true){
bgsound.mute = false;
}
else {
bgsound.mute = true;
}
}
}
You have already solved half the problem by using GameObject.DontDestroyOnLoad
The object does indeed exist in both scenes. Now you just need to fetch it.
In the first scene where the created the object, Change the tag of the object. Instead of using one of the exiting tags, create a new tag for it called something such as "MenuMusic". Make sure you assign it after creating it, unity does not assign it automatically
Now, in the 3rd scene, in the game object that needs to access it, create a private field "_music"
in your Start function, add
void Start() {
_music = GameObject.FindGameObjectsWithTag("MenuMusic");
}
You will now have the same instance of Music from scene 1
I would highly recommend referencing the sound script that you have into some sort of game manager. Usually how i work is i have one generic script that controls a multitude of options that i usually call the GameManager. This sets player controls, visual options and sound. From here you can simply set bool whether the player wants the music on and off. If this option wants to change you can reference the GameManager at any point in any script.
//Game Manager code
public void SoundControl(bool soundOff)
{
If(soundOff == true)
{
//Sound Off Control
}else
{
//Sound on Control
}
}
//Reference to game Manager
GameManager manager;
public void TurnOffSound()
{
//Turn sound off through manager
manager =
GameObject.FindGameObjectWithTag("Manager").GetComponent<GameManager>
().SoundControl(true);
}
I find this to be the easiest way to control any options through one script that you can reference anywhere.
Is there a way in Unity3d framework to register an object that would be accessible to all entities in all scenes in the game without resorting to singleton pattern (like a game state object)?
You could take the following approach:
In the scene where the GameObject is created, in a MonoBehavior script attached to the GameObject:
Name the GameObject with "name = ..." in Awake() (or in the Editor)
Example: myObject.name = "FindMeInEveryScene";
Ref: https://docs.unity3d.com/ScriptReference/Object-name.html
Call "DontDestroyOnLoad" on the GameObject in Awake()
Example: DontDestroyOnLoad( myObject );
Ref: https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
In the same scene and in subsequent scenes, in MonoBehavior scripts attached to other GameObjects.
Find the object with GameObject.Find(...) in Start()
Example: globalGameObject = GameObject.Find( "FindMeInEveryScene" );
Ref: https://docs.unity3d.com/ScriptReference/GameObject.Find.html
This will allow you to access the GameObject named "FindMeInEveryScene" without a singleton.
To pull it all together...
GlobalGameObject.cs
// Attached to the GameObject that needs to be accessd by any other
// GameObjects and needs to survive between loading scenes
public class GlobalGameObject : MonoBehaviour
{
static bool gameObjectInitialized = false;
void Awake()
{
if ( !gameObjectInitialized )
{
gameObject.name = "FindMeInEveryScene";
DontDestroyOnLoad( gameObject );
gameObjectInitialized = true;
}
}
}
UsingGameObject.cs
// Attached to GameObjects that need access to the GlobalGameObject
public class UsingGameObject : MonoBehaviour
{
private GameObject globalGameObject = null;
void Start()
{
globalGameObject = GameObject.Find( "FindMeInEveryScene" );
}
}
You could possibly do it using events but it isn't really the optimal way to use events. I'm intrigued to know why a singleton pattern doesn't work for you as this is pretty much their main purpose.
If you aren't going to do this with a static or a singleton, I don't see any point in you using any kind of game state object in your system design. It would do the same thing, but slower (avoid GameObject.Find at all costs). It would lack the guaaranteed uniqueness of the singleton and the easy reference provided by both. I don't understand what tradeoffs you mean but I'll assume they're sound to try and answer your question.
Doing this without a central game state object is going to require a more decentralized solution. Tasks that would be performed by a static game manager are now controlled by the individual objects themselves and notified by other objects.
https://unity3d.com/learn/tutorials/topics/scripting/delegates
The beauty of this method is that objects don't need to know about eachother at all. They just need to listen for events and notify the system that an event has occured. I really can't think of another alternative. Hope this helps. Best of Luck!
I wanted to implement a spawn method of my own in NetworkManager. The code for spawning players is shown below. Can someone tell me how to implement this in NetworkManager by overriding the normal spawn function.
public void Spawn()
{
int i = 1;
foreach (Transform child in PlayerFormation)
{
var player = ObjectPooler.GetPooledObject(PLAYER_PREFAB_PATH);
player.name = "Player ("+i+")";
player.transform.SetParent(child);
player.SetActive(true);
i++;
}
i=0;
}
The position to be spawned are shown in screenshot.
The function "OnServerAddPlayer()" is virtual and can therefore be overridden. Just make a class derive from NetworkManager, override the method, and put your custom NetworkManager in the script slot in the NetworkManager component in unity.
To see the base method see the following unity documentation.
I am trying to create a simple 2D Turn based multiplayer game using Photon Unity Networking.
It is just a simple turn based game where a player 1 (host) presses his button and it adds his score and changes its turn to player 2 (client) who presses his button to add score and change turn to player 1. It continues to infinite.
I was able to connect two players in the game using the basic Photon documentation. Now I need to add the networking logic of taking turns and changing them.
I searched the internet but I can't understand the RPC and SerializeView of Photon. I am really confused with that. Please Help me. Thank you in future. Here is my GameManager Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GameManager : Photon.PunBehaviour
{
public Text roomName;
public Text player1Name,player2Name;
public List<string> playersConnected = new List<string>();
int scoreP1 = 0, scoreP2 = 0;
public Text scoreTextP1, scoreTextP2;
public Button p1Btn, p2Btn;
int playerTurn = 1;
void Start()
{
roomName.text = SceneManager.GetActiveScene().name;
p2Btn.gameObject.SetActive(false);
p1Btn.gameObject.SetActive(true);
}
public void AddScoreP1()
{
scoreP1++;
scoreTextP1.text = scoreP1.ToString();
ChangePlayerTurn();
}
public void AddScoreP2()
{
scoreP2++;
scoreTextP2.text = scoreP2.ToString();
ChangePlayerTurn();
}
void ChangePlayerTurn()
{
if (playerTurn == 1)
{
playerTurn = 2;
p2Btn.gameObject.SetActive(true);
p1Btn.gameObject.SetActive(false);
}
else
{
playerTurn = 1;
p1Btn.gameObject.SetActive(true);
p2Btn.gameObject.SetActive(false);
}
print("Player Turn: P" + playerTurn);
}
void LoadArena()
{
if (!PhotonNetwork.isMasterClient)
{
Debug.LogError("PhotonNetwork : Trying to Load a level but we are not the master Client");
}
Debug.Log("PhotonNetwork : Loading Level : " + PhotonNetwork.room.PlayerCount);
PhotonNetwork.LoadLevel("Room for " + PhotonNetwork.room.PlayerCount);
}
public override void OnLeftRoom()
{
SceneManager.LoadScene(0);
}
public void LeaveRoom()
{
PhotonNetwork.LeaveRoom();
}
public override void OnPhotonPlayerConnected(PhotonPlayer other)
{
Debug.Log("OnPhotonPlayerConnected() " + other.NickName); // not seen if you're the player connecting
foreach (PhotonPlayer _player in PhotonNetwork.playerList)
{
playersConnected.Add(other.NickName);
}
if (PhotonNetwork.isMasterClient)
{
Debug.Log("OnPhotonPlayerConnected isMasterClient " + PhotonNetwork.isMasterClient); // called before OnPhotonPlayerDisconnected
LoadArena();
}
}
public override void OnPhotonPlayerDisconnected(PhotonPlayer other)
{
Debug.Log("OnPhotonPlayerDisconnected() " + other.NickName); // seen when other disconnects
foreach (PhotonPlayer _player in PhotonNetwork.playerList)
{
playersConnected.Remove(other.NickName);
}
if (PhotonNetwork.isMasterClient)
{
Debug.Log("OnPhotonPlayerDisonnected isMasterClient " + PhotonNetwork.isMasterClient); // called before OnPhotonPlayerDisconnected
LoadArena();
}
}
}
RPC is basically a way of invoking a function on a remote client.
Usually in a multiplayer setup you'd want your MasterClient to control the flow of the game. So, when two players join a room, what you need to do is from your MasterClient, decide which player goes first and then call a RPC function from MasterClient telling both client whose the first turn is. Then whichever player's turn it is, just activate its button and let them add score (Send RPC to MasterClient as well for that, so that everyone can stay in sync.) and the update turn and tell everyone via another RPC and so on.
though you can also use Events for such cases, they need less preparation to be used.
Both in a nutshell:
RPC, Remote Procedural calls, is used to call a certain method to all or certain clients/users in the same room/level. Example like this, you might want to use RCP to update the teams score if a team scored a goal.
SerializeView is used by PUN to synchronize and read data a few times per second, depending on the serialization rate. Example like this, you will use this to get and read to see each other's data like how much points another player have in realtime.
both of these functions are practically similar, but their use is entirely different.
There is more information about RCP and SerializeView on the Photon Website Link