Multiplayer UI using Mirror unity - unity3d

im pretty new to the mirror package and im trying to build a simple multiplayer game thats basically played using UI elements only (buttons). A big part of the game is announcements that show up like - "Player X's Turn" for example.
At the moment the announcement only shows up in the host game, if the announcement came from the Networkbehaviour class that belongs to the player it would be easy to solve with a simple ClientRPC function but i want the UI functions to run from a different class that handles the UI elements.
what is the correct way to implement this? does the UIHandler need to be inhereting from any network class? would love some tips regarding this subject.
thanks in advance,
Amit Wolf.

A common strategy is to build a singleton networked event manager that triggers the RPC as an event.
public class EventManager: NetworkBehaviour
{
public static EventManager Instance;
void Awake()
{
if(Instance == null)
Instance = this;
else
Destroy(this);
}
public event Action<int> OnPlayerTurnChanged;
[ClientRpc]
public void ChangeTurn(int playerId)
{
OnPlayerTurnChanged?.Invoke(playerId);
}
}
Then you can subscribe to the event in any other script and perform logic:
public class UIScript: NetworkBehaviour
{
void Awake()
{
EventManager.Instance.OnPlayerTurnChanged+= UpdateUI;
timer = 0f;
}
void UpdateUI(int playerId)
{
//UI Logic to set the UI for the proper player
}
}

Related

How to Create a Custom Unity Function

Im trying to create a custom function in unity that performs setActive false but with Delay. Without Using Coroutine or Invoke Methods.
Im trying to create something like the Function Destroy in Unity:
Destroy(Object obj, float time)
But with SetActive.
What I am aiming for is a function like this SetActiveWithDelay(GameObject obj, bool stateToChange, float delayTime) with which we can write a line like SetActiveWithDelay(GameObjectToPass,true,2f) this which setActive the Gameobject to true by 2sec.
This can be easily done with Invoke or the use of Coroutine. But I just am curious about how to make a custom function this way. Makes things a lot simpler, instead of creating an IEnumerator and Using StartCoroutine or Invoke with string.
I looked into the object class on how Destroy was written and tried to replicate it. But it was confusing to me how it works. So, Let me know if anyone has any ideas on how to do this. Thanks!
Here below are some of the things I tried but again the custom function had a use of coroutine. But I feel this is all the same and I can directly use coroutine. So I'm interested to know if there is any other method or if going with coroutine and invoke is a better option.
With Coroutine:
Gameobject GOToMakeFalse;
void Start()
{
//Aiming for a simplified Custom Function to write this way. With Does As it says make gameobject false after two seconds
SetActiveWithDelay(GOToMakeFalse,false, 2f);
}
SetActiveWithDelay(GameObject obj ,bool inState,float delayTime)
{
//But without using a coroutire like this here
StartCoroutine(DelayedSetActive(obj,inState, delayTime));
}
public IEnumerator DelayedSetActive(GameObject GO,bool inBoolState, float dT)
{
yield return new WaitforSeconds(dT);
GO.SetActive(false);
}
And im also curious if we can extend the GameObject Class something like this.
GameObject.SetActiveWithDelay(true,2f);
Even this would be a cool addition and make development easy.
About extension, something like that should work:
public static class GameObjectExtensions
{
public static void SetActiveWithDelay(this GameObject obj, bool inState, float delayTime)
{
// ...
}
}
myGameObject.SetActiveWithDelay(true, 2f);
The important parts are the "static" class and the "this" in the first parameter of the static method.
You can use Async await or a simple timer script if you don't want to use coroutines. For Async you need to add the namespace
using System.Threading.Tasks;
You can read more on how to delay using Async here.
With the Help of #Vionix and #Kandohar. I have written an extension class of SetActiveWithDelay.
I have written 2 versions of it. Version 1 is with using "GameManager" as my GameManager class is a singleton instance & Version 2 is Having a MonoBehaviour script attached to the Gameobject you want to SetActive false and accessing that MonoBehaviour class. Hope this method will be helpful. Any alterations to this code by anyone are happily taken. Here is the Answer I came Up With.
Version-1: If you have a GameManager class that is a singleton instance
public static class GameObjectExtensions
{
public static void SetActiveWithDelay(this GameObject inObject, bool inState, float delayTime)
{
GameManager.Instance?.StartCoroutine(CallWithDelay(inObject, inState, delayTime));
}
static IEnumerator CallWithDelay(this GameObject inObject, bool inState, float delayTime)
{
yield return new WaitForSeconds(delayTime);
inObject.SetActive(inState);
}
}
Version-2: By accessing Monobehaviour class that is attached with the Gameobject we access.
public static class GameObjectExtensions
{
public static void SetActiveWithDelay(this GameObject inObject, bool inState, float delayTime)
{
MonoBehaviour mono = inObject.GetComponent<MonoBehaviour>();
mono.StartCoroutine(CallWithDelay(inObject, inState, delayTime));
}
static IEnumerator CallWithDelay(this GameObject inObject, bool inState, float delayTime)
{
yield return new WaitForSeconds(delayTime);
inObject.SetActive(inState);
}
}
Disclaimer: In Version Two, you need to make sure the Gameobject you're about to Setactive should definitely have a MonoBehaviour Script Attached to it.
With this now we can write GameObjectThatWeWantToChange.SetActiveWithDelay(true,2f);
I Thank #Vionix and #Kandohar for your answer and support. As said earlier if you guys have any idea on making my above snippet / answer better. Please do, and post it here. Thanks!

Reference same object with different tags

I'm currently developing a game where I have 3 types of enemies (Common, Elite and Boss which I'm using tags to define the type).
Since I want to give different characteristics and spawning rate to every enemy type, I've been struggling finding a way better to call All enemies instead of calling with tags.
Like:
bool enemyIsAlive()
{
if (GameObject.FindGameObjectWithTag("Common") == null)
{
return false;
}
return true;
}
Here I'm trying to check if there's any enemy in the scene but I can't find a way to reference all enemies. I tried with "Find" but picks the name and the enemies gonna have different names since I can't use the same name for all.
Anyone can help me?
Avoiding using Unity's GameObject Find functions is usually a good thing in the long run for game performance. When handling multiple enemies in a game, I tend to sway towards using a manager class that will be responsible for tracking the spawning and despawning of the enemies and then be used as the source of checking if an enemy is still alive.
For example, lets say you have a simple enemy class that can be either a Common, Elite or Boss. By putting this class onto an object (your enemy) in your hierarchy you can use the type field to say what type this enemy is, and then create a prefab of that enemy to spawn in at runtime.
using UnityEngine;
public class Enemy : MonoBehaviour
{
public Type type;
public enum Type
{
COMMON,
ELITE,
BOSS
}
private void OnEnable()
{
EnemySpawned();
}
private void OnDisable()
{
EnemyDied();
}
public void EnemySpawned()
{
Debug.Log("Enemy Spawned: " + type);
EnemyManager.RegisterEnemy(this);
}
public void EnemyDied()
{
Debug.Log("Enemy Died: " + type);
EnemyManager.UnregisterEnemy(this);
}
}
You can see that when an enemy spawns EnemySpawned() is called which uses an EnemyManager to register itself so that it can keep track of it. The same happens when the enemy dies EnemyDied() is called which then tells the EnemyManager to unregister that enemy.
Here is the EnemyManager class:
using System.Collections.Generic;
public class EnemyManager
{
private static Dictionary<Enemy.Type, int> enemies;
public static void RegisterEnemy(Enemy enemy)
{
if (enemies == null)
{
enemies = new Dictionary<Enemy.Type, int>();
}
if (enemies.ContainsKey(enemy.type))
{
enemies[enemy.type] += 1;
}
else
{
enemies.Add(enemy.type, 1);
}
}
public static void UnregisterEnemy(Enemy enemy)
{
if (enemies == null || !enemies.ContainsKey(enemy.type))
{
return;
}
enemies[enemy.type] -= 1;
}
public static bool IsEnemyTypeAlive(Enemy.Type enemyType)
{
return enemies != null && enemies.ContainsKey(enemyType) && enemies[enemyType] > 0;
}
}
This does not need to be added to an object in the scene hierarchy as it uses static fields and functions.
As you can see the EnemyManager will keep a Dictionary of the enemy types and how many of those enemies are registered. (You could also keep a reference to the Enemy class itself instead of just a count so you could later access those enemies through the EnemyManager too, but for simplicities sake, we will just keep a count)
When an Enemy is registered the count is incremented and when an Enemy is unregistered the count is decremented.
We can then use this to see if enemies are still alive by their type, by checking if the dictionary has firstly had anything registered, then if the dictionary has had that specific enemy type registered, and finally if there are still any registered enemies of that type as they may have all been unregistered.
Here is a demo class then of how that check could be used:
using UnityEngine;
public class Demo : MonoBehaviour
{
private void Update()
{
if (Input.GetKeyDown(KeyCode.Q))
{
Debug.Log("Common Enemy Alive: " + IsCommonEnemyAlive());
}
if (Input.GetKeyDown(KeyCode.W))
{
Debug.Log("Elite Enemy Alive: " + IsEliteEnemyAlive());
}
if (Input.GetKeyDown(KeyCode.E))
{
Debug.Log("Boss Enemy Alive: " + IsBossEnemyAlive());
}
}
public bool IsCommonEnemyAlive()
{
return EnemyManager.IsEnemyTypeAlive(Enemy.Type.COMMON);
}
public bool IsEliteEnemyAlive()
{
return EnemyManager.IsEnemyTypeAlive(Enemy.Type.ELITE);
}
public bool IsBossEnemyAlive()
{
return EnemyManager.IsEnemyTypeAlive(Enemy.Type.BOSS);
}
}
It may seem more complicated, but this solution will scale a lot better than using the GameObject find functions especially if you need to check if the enemies exist often.
To take it even further you could look into System.Action delegates and events to decouple the enemies and the EnemyManager even further and simply raise events when enemies are spawned or die.
you should not use Find that often, especially not in an update function since it is performance heavy.
Better:
Create a List where you register each enemy that spawns and also implement a function that removes them when they die. Also think about inheritance, i presume each enemy has common parameters so it would be a good approach to use it.

How to change Vuforia AnchorInputListenerBehaviour?

How to change Vuforia the AnchorInputListenerBehaviour, the original setting is clicked on the screen, I want to change it to judge the Micro Switch high or low signal?
You don't need the Vuforia code at all!
You seem to rather just want to have your own custom UnityEvent in the Inspector you can invoke whenever you want to ;)
If you need it you can even extend this and add some parameters as described here using UnityEvent<T> e.g. something like
[Serializable]
public class CustomEvent : UnityEvent<bool> { }
public class Example : MonoBehaviour
{
public CustomEvent _onButtonStateChanged;
private bool lastState;
// Now however you check your button states and get your pin data
// on whatever condition you can simply invoke your event
private void Update()
{
//TODO
var currentState = GetYourButtonStateByMagic();
if(currentState != lastState)
{
_onButtonStateChanged.Invoke(currentState);
lastState = currentState;
}
}
}

How to network a VR button with PUN in Unity?

I've been working on a solution for weeks now and I think it's time to reach out. I'm trying to make a button that plays a sound when pressed by a controller, everyone will hear that sound. Using VRTK and PlayoVR, I'm able to make a non-networked version where the player can put their hand through a cube, click the trigger from the controller, and it makes a sound.
This is the code for that cube:
namespace VRTK.Examples {
using UnityEngine;
public class Whirlygig : VRTK_InteractableObject
{
public GameObject AudioSource;
public AudioSource LeftSpeaker;
public override void StartUsing(VRTK_InteractUse currentUsingObject =
null)
{
AudioSource.GetComponent<AudioSource>().Play();
}
}
}
Where I get lost is how to network it with Photon Unity Networking. This is what I have:
namespace PlayoVR
{
using UnityEngine;
using VRTK;
using UnityEngine.Video;
using NetBase;
public class PlaySync : Photon.MonoBehaviour
{
public AudioSource LeftSpeaker;
public GameObject Whirlgig;
private bool StartUsing;
// Use this for initialization
void Awake()
{
GetComponent<VRTK_InteractableObject>().InteractableObjectUsed +=
new InteractableObjectEventHandler(DoPlay);
}
void DoPlay(object sender, InteractableObjectEventArgs e)
{
StartUsing = true;
}
// Update is called once per frame
void Update()
{
// Handle firing
if (StartUsing)
{
CmdPlay();
StartUsing = false;
}
}
void CmdPlay()
{
photonView.RPC("NetPlay", PhotonTargets.All);
}
[PunRPC]
void NetPlay()
{
LeftSpeaker.Play();
}
}
}
As you can probably see, I'm a beginner. With this code, when I put my hand in the cube and press the trigger, nothing happens. If anyone can provide any help or even an alternative, I'd be very grateful.
Kind regards,
TheMusiken

Unity3D Programmatically Assign EventTrigger Handlers

In the new Unity3D UI (Unity > 4.6), I'm trying to create a simple script I can attach to a UI component (Image, Text, etc) that will allow me to wedge in a custom tooltip handler. So what I need is to capture a PointerEnter and PointerExit on my component. So far I'm doing the following with no success. I'm seeing the EVentTrigger component show up but can't get my delegates to fire to save my life.
Any ideas?
public class TooltipTrigger : MonoBehaviour {
public string value;
void Start() {
EventTrigger et = this.gameObject.GetComponent<EventTrigger>();
if (et == null)
et = this.gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry entry;
UnityAction<BaseEventData> call;
entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerEnter;
call = new UnityAction<BaseEventData>(pointerEnter);
entry.callback = new EventTrigger.TriggerEvent();
entry.callback.AddListener(call);
et.delegates.Add(entry);
entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerExit;
call = new UnityAction<BaseEventData>(pointerExit);
entry.callback = new EventTrigger.TriggerEvent();
entry.callback.AddListener(call);
et.delegates.Add(entry);
}
private void pointerEnter(BaseEventData eventData) {
print("pointer enter");
}
private void pointerExit(BaseEventData eventData) {
print("pointer exit");
}
}
Also... the other method I can find when poking around the forums and documentations is to add event handlers via interface implementations such as:
public class TooltipTrigger : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler {
public string value;
public void OnPointerEnter(PointerEventData data) {
Debug.Log("Enter!");
}
public void OnPointerExit(PointerEventData data) {
Debug.Log("Exit!");
}
}
Neither of these methods seems to be working for me.
Second method (implementation of IPointerEnterHandler and IPointerExitHandler interfaces) is what you're looking for. But to trigger OnPointerEnter and OnPointerExit methods your scene must contain GameObject named "EventSystem" with EventSystem-component (this GameObject created automatically when you add any UI-element to the scene, and if its not here - create it by yourself) and components for different input methods (such as StandaloneInputModule and TouchInputModule).
Also Canvas (your button's root object with Canvas component) must have GraphicRaycaster component to be able to detect UI-elements by raycasting into them.
I just tested code from your post and its works just fine.