Unity Networked Ready check - c#

Trying to create a ready check using PUN2 so that all players will load into the game scene at the same time, but I do not understand how to check another players custom property and keep a count of how many players are currently ready and if all are ready then start the game. I think I have a custom property set up for every player that should be but I am unsure if it working at all.
public class HeroSelectController : MonoBehaviour
{
[HideInInspector]
public string selectedHero;
private PhotonView PV;
private bool PlayerReady = false;
private ExitGames.Client.Photon.Hashtable _playerCustomProperties = new ExitGames.Client.Photon.Hashtable();
private void Update()
{
Debug.Log("Player Ready = " + _playerCustomProperties["PlayerReady"]);
}
private void HeroSelect()
{
PlayerReady = true;
selectedHero = "PlayerTest";
PhotonNetwork.SetPlayerCustomProperties(_playerCustomProperties);
_playerCustomProperties["PlayerReady"] = PlayerReady;
}
public void OnClickHeroButton()
{
HeroSelect();
if (PhotonNetwork.IsMasterClient)
{
foreach (var photonPlayer in PhotonNetwork.PlayerList)
{
photonPlayer.CustomProperties["PlayerReady"] = true;
PhotonNetwork.LoadLevel(3);
}
}
}
}
What is currently happening is that the master client can start the game regardless of everyone else's state. Feel like I might be overthinking all of this and there is a much similar solution as I would expect a function like this to be common place as I would expect something similar to be used in many online games so if I am going about completely the wrong way please point me a more suitable direction

Don't know exactly how Photon works but I assume these properties are already synchronized and as far as I understand you simply want some kind of a check like e.g.
private bool AllPlayersReady
{
get
{
foreach (var photonPlayer in PhotonNetwork.PlayerList)
{
if(photonPlayer.CustomProperties["PlayerReady"] == false) return false;
}
return true;
}
}
Which you could probably also shorten using Linq like
using System.Linq;
...
private bool AllPlayersReady => PhotonNetwork.PlayerList.All(player => player.CustomProperties["PlayerReady"] == true);
And then use it like
public void OnClickHeroButton()
{
HeroSelect();
if (!PhotonNetwork.IsMasterClient) return;
if(!AllPlayersReady)
{
return;
}
PhotonNetwork.LoadLevel(3);
}
This can still be enhanced since now the master has to press repeatedly until maybe everyone is ready. I would additionally use a Coroutine like
public void OnClickHeroButton()
{
HeroSelect();
if (!PhotonNetwork.IsMasterClient) return;
StartCoroutine (WaitAllReady ());
}
private IEnumerator WaitAllReady()
{
yield return new WaitUntil (() => AllPlayersReady);
PhotonNetwork.LoadLevel(3);
}
Thanks to Iggy:
Instead of a routine which checks every frame you can use OnPlayerPropertiesUpdate in order to check for the ready state
void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
{
if (!PhotonNetwork.IsMasterClient) return;
// you can even limit the check to make it
// only if "PlayerReady" is among the changed properties
if(!changedProps.Contains("PlayerReady")) return;
if(!AllPlayersReady) return;
PhotonNetwork.LoadLevel(3);
}
Note: Typed on smartphone but I hope the idea gets clear

Related

How to repeatedly execute a method until said otherwise

I have an EventSystem for managing my turn-based game in Unity.
public class EventSystem : MonoBehaviour
{
private static List<Action> _commandsQueue = new List<Action>();
private bool _canExecuteCommand = true;
public void AddToQueue(Action command)
{
_commandsQueue.Add(command);
}
private void StartCommandExecution()
{
_commandsQueue[0]();
_canExecuteCommand = false;
}
public void CommandExecutionComplete()
{
_canExecuteCommand = true;
}
public void PlayFirstCommandFromQueue()
{
if (_commandsQueue.Any() && _canExecuteCommand)
{
StartCommandExecution();
}
else
{
Debug.LogError("No Command In Queue");
}
}
}
How do I put a method in Update() until _canExecuteCommand is true again but only for some methods?
It is quite broad what you are trying to do but in general you would use an endless loop within a Coroutine.
You can create a generic routine which invokes any Action you pass in as parameter once a frame like e.g.
private IEnumerator InvokeEveryFrame(Action action)
{
// This looks strange but is okey in a Coroutine as long as you yield somewhere within
while(true)
{
action?.Invoke();
// This tells Unity to "pause" this routine here
// render the current frame and continue from here in the next frame
yield return null;
}
}
So all that's left is starting the routine using MonoBehaviour.StartCoroutine like e.g.
Coroutine routine = StartCoroutine(SomeParameterlessMethod);
or if you need parameters
Coroutine routine = StartCoroutine(() => SomeMethod(x, y, z));
and then at some point later stop it using MonoBehaviour.StopCoroutine and the stored Coroutine reference like e.g.
StopCoroutine(routine);
how exactly you store that reference is up to you of course up to you.

Cannot assign "Death" because its a method group

I have recently started to learn c# but I have encountered an error which I hope some of you will be able to help me with. We are developing a game in Unity5 and when the players health hits 0 the rag-doll function will kick in but unfortunately I cannot call death as it is a method group... Any ideas? Cheers. Here is the code(I commented it out so I could enter play mode in Unity because the error wasn't letting me in line 47 and 53):
using UnityEngine;
using System.Collections;
public class EnemyAI_Basic : MonoBehaviour
{
private EnemyAI_Basic enemyAI_Basic;
Animator controller;
float health;
Animator anim;
bool Alive = true;
public bool Dead = false;
int pointValue = 5;
private Collider myCollider;
private Rigidbody myRigidbody;
CapsuleCollider capsuleCollider;
void Start()
{
controller = GetComponentInParent<Animator>();
health = 40;
capsuleCollider = GetComponent<CapsuleCollider>();
anim = GetComponent<Animator>();
}
void Update()
{
if (!Dead)
{
anim.SetTrigger("Alive");
}
}
void Death()
{
Dead = true;
Alive = false;
capsuleCollider.isTrigger = true;
anim.SetTrigger("Dead");
Destroy(gameObject, 4f);
}
void OnEnable()
{
SetInitialReferences();
enemyAI_Basic.Death += ActivateRagdoll;
}
void OnDisable()
{
enemyAI_Basic.Death -= ActivateRagdoll;
}
void SetInitialReferences()
{
enemyAI_Basic = transform.root.GetComponent<EnemyAI_Basic>();
if (GetComponent<Collider>() != null)
{
myCollider = GetComponent<Collider>();
}
if (GetComponent<Rigidbody>() != null)
{
myRigidbody = GetComponent<Rigidbody>();
}
}
void ActivateRagdoll()
{
if (myRigidbody != null)
{
myRigidbody.isKinematic = false;
myRigidbody.useGravity = true;
}
if (myCollider != null)
{
myCollider.isTrigger = false;
myCollider.enabled = true;
}
}
}
I see your problem.
You're treating the death method like a delegate, which it isn't, in this case. You can only register methods to either delegates or events, you can't register methods to method groups.
For your situation, there are a few ways to resolve this.
First solution, and by far the easiest, put in a call to ActivateRagdoll inside the Death function, like so:
void Death()
{
Dead = true;
Alive = false;
capsuleCollider.isTrigger = true;
anim.SetTrigger("Dead");
ActivateRagdoll();
Destroy(gameObject, 4f);
}
Then remove the:
enemyAI_Basic.Death += ActivateRagdoll;
and
enemyAI_Basic.Death -= ActivateRagdoll;
from the OnEnable and OnDisable methods, that should remove any compiler errors.
Now, in order to use this method, all you have to do is call it from somewhere in code. Depending on how you kill the Ai, you may consider making that method public, or have some death logic inside your update loop,
if(health <= 0)
{
Death();
enabled = false; //disable the component so the update loop doesn't repeat the death procedure multiple times, Unity might not be pleased.
}
The other way, and the way that you're trying to do it, is to create an event that classes can subscribe methods to. This event will call all of the subscribed methods, providing them with a means of being notified, whenever something interesting happens, like the Ai dying in this case. This can be useful for external classes that shouldn't have to constantly check the death status of the Ai, after all, they may not be concerned with the Ai if its on full health. They only need to know, when the Ai does die. Events are ideal for this situation.
To make an event you'll be doing something like this:
public class EnemyAI_Basic : MonoBehaviour
{
//your other variables
public delegate void EnemyDies(); //declare the method types that can be registered to the event
public event EnemyDies onEnemyDeath; //declare event, using the delegate for the method type
//other methods here
private void OnEnable()
{
//must be the same method type as the event, the compiler will let you know if it isn't
onEnemyDeath += Death;
onEnemyDeath += ActivateRagdoll;
}
private void OnDisable()
{
onEnemyDeath -= Death;
onEnemyDeath -= ActivateRagdoll;
}
//other things here
//doesn't have to be public, just in case you want a bullet to kill the enemy
public void KillAi()
{
//checking for null basically says: is anybody registered? If not, nothing to invoke.
//It will get upset if you try and invoke an event without any registered methods, hence the check.
if(onEnemyDeath != null)
{
onEnemyDeath();
}
}
void Death()
{
Dead = true;
Alive = false;
capsuleCollider.isTrigger = true;
anim.SetTrigger("Dead");
//ActivateRagdoll(); You don't have to manually call this now, invoking the event will call it for you.
//Registration order is important, me thinks. Experiment :)
Destroy(gameObject, 4f);
}
}
There are several other ways to rig up a system like this, but, in your case this may be what you're after.
Some reading:
Events: https://www.codeproject.com/Articles/11541/The-Simplest-C-Events-Example-Imaginable
https://www.tutorialspoint.com/csharp/csharp_events.htm
Delegates: https://msdn.microsoft.com/en-us/library/ms173171.aspx
Method groups: What is a method group in C#?
Hopefully this helps.

How to check a bool or call a function in script of Unity on Windows App Solution side

I need to call a function on Windows Store app side by checking a boolean from unity script. How to do that i used this code but it gives errors. I just need a simple solution in which i check a bool declared in unity script to call a function at ported windows store app end
using System;
using UnityEngine;
public class SphereScript : MonoBehaviour
{
private bool m_IsMoving = true;
private bool m_IsMovingLeft = false;
public Camera GameCamera;
public event Action<bool> SphereStateChanged;
public bool IsSphereMoving { get { return m_IsMoving; } }
void Start()
{
if (GameCamera == null)
{
throw new Exception("Camera is not attached to the sphere script!");
}
}
void FixedUpdate()
{
if (!m_IsMoving)
{
return;
}
if (m_IsMovingLeft)
{
transform.position -= new Vector3(0.2f, 0.0f);
if (GameCamera.WorldToScreenPoint(transform.position).x < 100.0f)
{
m_IsMovingLeft = false;
}
}
else
{
transform.position += new Vector3(0.2f, 0.0f);
if (GameCamera.WorldToScreenPoint(transform.position).x > Screen.width - 100.0f)
{
m_IsMovingLeft = true;
}
}
}
void OnGUI()
{
var buttonText = m_IsMoving ? "Stop sphere" : "Start sphere movement";
if (GUI.Button(new Rect(0, 0, Screen.width, 40), buttonText))
{
m_IsMoving = !m_IsMoving;
if (SphereStateChanged != null)
{
SphereStateChanged(m_IsMoving);
}
}
}
}
Complete code is here
The way I understand you question is that when something happens on the Unity side, you'd like to call a method on the Windows Store app side, right?
A nice way to do this is to have an event in your Unity code that your Win code can register for. For the sake of this example I'm going to call your event ButtonClicked.
First you need a static class that the event will be in. Create a new C# script in the Unity Assets folder and open it up. I called mine StaticInterop. Clear the code that was generated, and make it this:
public class StaticInterop
{
public static event EventHandler ButtonClicked;
public static void FireButtonClicked()
{
if (ButtonClicked != null)
{
ButtonClicked(null, null);
}
}
}
Now, in Unity whenever that thing happens (in this case when the button is clicked), do this line of code:
StaticInterop.FireButtonClicked();
That is everything on the Unity side done. So create a build, and open up the VS Windows Store app project it creates.
In the Win code you can now sign up for the event like this:
StaticInterop.ButtonClicked+= StaticInterop_ButtonClicked;
And declare this method for it to run:
void StaticInterop_ButtonClicked(object sender, EventArgs e)
{
// Do whatever you need here.
}
The important thing to note is that the StaticInterop static class is showing up in your Win code. This means that you can use it to transfer anything between the Unity and Win side. You could even have a method in the class called something like PauseGame(), and then run that from the Win side with StaticInterop.PauseGame().

Promises with Unity

I want a Character class, inheriting from MonoBehavior and exposing handfuls methods : Walk, Attack, ...
However, assuming that two components use these methods at the same time, I would like to queue the actions and have some way to notify the components that their actions have been executed.
In Javascript standard, I would have done something like this :
var Character = function ( ) {
this._deferred = new Deferred( );
};
Character.prototype.walk = function ( ) {
return this._deferred = this._deferred.then( function ( ) {
// Do the actual walk
} );
};
Character.prototype.attack = function ( ) {
return this._deferred = this._deferred.then( function ( ) {
// Do the actual attack
} );
};
var character = new Character( );
// Component A
character.walk( ).then( function ( ) {
// Executes when the walk is done
} );
// Component B
character.attack( ).then( function ( ) {
// Executes when the walk AND attack is done
} );
What is the right way to do so with Unity / C# ?
Preamble
For the answer I will use this "I would like to queue the actions" as a description of your problem.
There are lot of ways to solve this problem. And I don't pretend to be comprehensive.
Even in JS I would consider using Promises for queuing character commands as wrong choice.
I haven't ever compiled, run or tested the code that i present here :D
Promises in C#
There is some C# port of Promises on github. I haven't ever used it, but the code doesn't seem to contain anything that stops it from being used in Unity. Anyway, you can give it a try.
Using Queue<>
I would definitely use Queue<TCommand> of some TCommand for this problem. The only question is what to use as TCommand. I will give you 2 examples here. But, as usual, there are more options.
Some class
Something like this:
public enum CommandUpdateResult
{
Ongoing,
Finished
}
public interface ICommand
{
CommandUpdateResult Update();
}
public class RunCommand: ICommand
{
// Bla-bla-bla
}
public class AttackCommand: ICommand
{
// Bla-bla-bla
}
public class Character: MonoBehaviour
{
private Queue<ICommand> commandQueue;
public void Awake()
{
commandQueue = new Queue<ICommand>();
}
public void Update()
{
if (commandQueue.Count > 0 && commandQueue.Peek().Update() == CommandUpdateResult.Finished)
commandQueue.Dequeue();
}
public void EnqueueCommand(ICommand command)
{
commandQueue.Enqueue(command);
}
}
public class SomeClassThatUsesCharacter
{
private Character character;
public void SomeMethodThatUsesCharacter()
{
character.EnqueueCommand(new RunCommand(bla-bla-bla));
character.EnqueueCommand(new AttackCommand(bla-bla-bla));
}
}
IEnumerator
The simplest (but not very elegant) way to use IEnumerator is to use it with some infinite coroutine.
public class Character: MonoBehaviour
{
private Queue<IEnumerator> commandQueue;
private IEnumerator CommandQueueCoroutine()
{
while (true)
{
if (commandQueue.Count > 0)
yield return StartCoroutine(commandQueue.Peek());
else
yield return new WaitForFixedUpdate();
}
}
public void Awake()
{
commandQueue = new Queue<ICommand>();
StartCoroutine(CommandQueueCoroutine());
}
public void Update()
{
if (commandQueue.Count > 0 && commandQueue.Peek().Update() == CommandUpdateResult.Finished)
commandQueue.Dequeue();
}
public void Enqueue(IEnumerator command)
{
commandQueue.Enqueue(command);
}
IEnumerator RunCommand()
{
while (Jenny.Tells("Run"))
{
transform.position.x += 1;
yield return new WaitForFixedUpdate();
}
}
IEnumerator AttackCommand(BadGuy badGuy)
{
badGuy.Die();
yield break;
}
}
public class SomeClassThatUsesCharacter
{
private Character character;
public void SomeMethodThatUsesCharacter()
{
character.Enqueue(character.RunCommand());
character.Enqueue(character.AttackCommand(someBadGuy));
}
}
A very good Unity Promise library: https://github.com/Real-Serious-Games/C-Sharp-Promise and see related blog http://www.what-could-possibly-go-wrong.com/promises-for-game-development/
Alternatively I think you can also go down an RX approach with Observers that react to Walk and Attack events. See https://github.com/neuecc/UniRx
Well, at the moment Unity already allows you to use:
TaskCompletionSource as a promise;
Task as a future;
but if Promise itself is more suitable for your project give it a try by using: C-Sharp-Cancellable-Promise library
This is a fork of Real-Serious-Games Promise lib updated to the latest .NET and with Promises cancellation support.

Making Threads in Unity using frames

I have an assignment to make a thread safe logging class that writes to a file. Every ten frames I am supposed to push some information of my choice to the logging class from a separate script. I was wondering how to do that. Here is my code so far.
public class Threading
{
public bool Execute = true;
public Vector3 player;
public Vector3 WriteTime;
System.Collections.Generic.Queue<float> values;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
}
public void execute()
{
while (Execute)
{
System.Threading.Thread.Sleep(500);
values.Enqueue(player.x);
UnityEngine.Debug.Log("value");
}
System.IO.StreamWriter write = new System.IO.StreamWriter("values.txt"); // writes to file every 5 seconds
while (values.Count > 0)
{
WriteTime = write.WriteLine(values.Dequeue().ToString());
}
write.Close();
}
public void Lock() // applied Lock threading
{
while(true)
{
lock (this)
{
// dont have anything here yet. Trying to figure out locks
}
}
}
Thank you.

Categories

Resources