Question about PhotonNetwork.CurrentRoom.CustomProperties - c#

When the game starts in multiplayer, the master client sends a PunRPC to have all clients run a function. This function tries to get the room properties to see if the game is active, if so it does something. For some reason a client gets a null reference error, but the master client does not. The strange thing is, a debug of the hash table for room properties is visible but I cannot get a specific item in it.
Tried Debugging the hash table to make sure that the key was set when the code was run. It was. "(System.String)ag=(System.Boolean)True ag=activeGame" This shows in Debug.Log(hash); But (bool)hash[rpk.activeGame] gets a null reference error. But only on the client side not the master client. So the key also works.
// Call all the clients to set up the room settings in the sub menu.
[PunRPC]
private void GameRoomSetup (string pOne, string pTwo, int pOneColor, int pTwoColor)
{
GameObject gameMenu = GameObject.Find ("GameMenu");
gameMenu.GetComponent<SubMenu> ().UpdatePlayers (pOne, pTwo, pOneColor, pTwoColor);
gameMenu.GetComponent<SubMenu> ().StartGameSetup ();
// If you are a player, change the active buttons that are visible.
if (PhotonNetwork.NickName == pOne || PhotonNetwork.NickName == pTwo)
{
gameMenu.GetComponent<GameButtonManager> ().GameStart ();
}
hash = PhotonNetwork.CurrentRoom.CustomProperties;
Debug.Log(hash);
if ((bool)hash[rpk.activeGame]) // Error on this line on client but not on master client. Says null reference.
{
GameObject.Find ("SoundManager").GetComponent<SoundManagerScript> ().PlayBackgroundTwo ();
GameObject.Find ("GameMenu").GetComponent<SubMenu> ().ChangeSubMenuActive (false);
}
}
I'm trying to run my if statement as a client but I get an error.

Thank you for choosing Photon!
To get a custom property, I recommend you use TryGetValue method as follows:
hash = PhotonNetwork.CurrentRoom.CustomProperties;
object temp;
string key = rpk.activeGame;
if (hash.TryGetValue(key, out temp))
{
if (temp is bool)
{
bool activeGame = (bool)temp;
}
else
{
// unexpected custom property value type
}
}
else
{
// custom property not found
}
If the custom property is not available yet, wait for the callback IInRoomCallbacks.OnRoomPropertiesUpdate(Hashtable propertiesThatChanged) (reference API).
Other notes and recommendations:
private void GameRoomSetup (string pOne, string pTwo, int pOneColor, int pTwoColor)
Not sure if it's supported or if it's a good idea to pass multiple parameters to a PUN RPC method.
To debug log a Dictionary or a Hashtable you could make use of SupportClass.DictionaryToString() method.
so instead of
Debug.Log(hash);
use
Debug.Log(SupportClass.DictionaryToString(hash));
Avoid calling expensive methods like GameObject.Find:
GameObject gameMenu = GameObject.Find ("GameMenu");
Also here you have duplicate calls to gameMenu.GetComponent<SubMenu>(), at least call it once and cache the component result found if any.
gameMenu.GetComponent<SubMenu> ().UpdatePlayers (pOne, pTwo, pOneColor, pTwoColor);
gameMenu.GetComponent<SubMenu> ().StartGameSetup ();
Comparing strings should not be done using == operator. At least use Equal method and proper StringComparison type. Read "How to compare strings in C#".
// If you are a player, change the active buttons that are visible.
if (PhotonNetwork.NickName == pOne || PhotonNetwork.NickName == pTwo)
{
gameMenu.GetComponent<GameButtonManager> ().GameStart ();
}
Besides, why do you use the Nickname to check if it's player one or two? maybe use ActorNumber or a custom player index. Or use the player count if the room is for 2 players only.

Related

Check when a variable has changed in Custom Editor

I am attempting to check whether any variable has changed in a custom Editor Class, but I can't seem to get a result from the check. This is my custom editor class. I am using EditorGUI.BeginChangeCheck() & EditorGUI.EndChangeCheck():
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(TileManager))]
[CanEditMultipleObjects]
public class TileManagerEditor : Editor
{
public override void OnInspectorGUI()
{
TileManager tileManager = (TileManager)target;
EditorGUI.BeginChangeCheck();
serializedObject.Update();
tileManager.tileCount = (int)Mathf.Clamp(EditorGUILayout.IntField("Tile Count", tileManager.tileCount), 0, Mathf.Infinity);
tileManager.resizeTileCount = EditorGUILayout.Toggle("Resize Tile Count", tileManager.resizeTileCount);
tileManager.tileSize = EditorGUILayout.Vector3Field("Tile Size", tileManager.tileSize);
tileManager.tileSpacing = Mathf.Clamp(EditorGUILayout.FloatField("Tile Spacing", tileManager.tileSpacing), 0, Mathf.Infinity);
tileManager.relativeSpacing = EditorGUILayout.Toggle("Relative Spacing", tileManager.relativeSpacing);
if (tileManager.relativeSpacing == true)
{
tileManager.spacingMultiplier = Mathf.Clamp(EditorGUILayout.FloatField("Spacing Multiplier", tileManager.spacingMultiplier), 0, Mathf.Infinity);
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("tilePrefab"), true);
EditorGUILayout.PropertyField(serializedObject.FindProperty("tileHolder"), true);
EditorGUILayout.PropertyField(serializedObject.FindProperty("tiles"), true);
serializedObject.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck())
{
tileManager.UpdateOnChange();
}
}
}
I have also tried Debug.Log(EditorGUI.EndChangeCheck()) instead of the if statement, but I don't get an output
There are two ways to track "something has changed" I can think off.
Both I would solve via a custom generic class changeTrackableValue<T> (a value where you can track changes). In both case you need a way to decide when all changes have become the "new normal".
Option 1 - Comparative
You keep a know "good" value. If the current and the know "good" value do not match, valueChanged would return true.
Properties:
if you take the long route back to the original value, it would read as "unchanged". usually adviseable for a Text Editor
It would basically double your memory demand, if not more then that (if parts are cut or added wholesale)
the original value does not need to be a property
Option 2 - the archive Bit
I copy that from the Filesystem. You use a property with a custom set for the value. You store one bool changed = false with each value. If the value is changed via set function, you set changed = true.
Properties:
minimal increase in memory useage
any change is tracked, even the ones back to the original state (can be good or bad)
you need a property with a public set and need to be able to modify said setter.
I can provide code for either one, if you need it.

How can i organize multiple animations? Need help to do a combination

i have a problem in Unity3d scripts.
I'm trying to make a sort of combination to open a box.
To open this box, i have to insert a combination of 3 buttons correctly.
This 3 buttons(That are simple GameObject, already placed in my scene) have already an animation, when my character collide with one of them, this one will fall down (same animation to other 2).
So, the combination that i want to insert is "the first correct button is "Button n*2", the second correct button is "Button n*1" and the third correct button is "Button n*3"", but i really don't have idea of how i have to do this.
I tried with if statements but if for example the combination is 123-312-123 the animation of the boxes will show up.
I want that only if i do the combination 213 the box is open, than if i go wrong i have to repeat the combination.
Can anyone help me?
Simple way, have a collection of right sequence:
int[] solution = new int[]{2,1,3};
then anytime a button is used, add its value to another collection:
List<int> sequence = new list<int>();
void OnPress(int buttonValue)
{
if(sequence.Contains(buttonValue)){ return; } // Don't add twice
sequence.Add(buttonValue);
if(sequence.Count == solution.Length)
{
if(CompareSequence())
{
// win
}
else
{
sequence.Clear();
}
}
}
bool CompareSequence()
{
// this should not be since we checked before but just to be sure
if(solution.Length != sequence.Count){ return false; }
for(int i = 0; i < solution.Length; i++)
{
if(solution[i] != sequence[i]){ return false; }
}
return true;
}
Each action on button would pass its own value that gets added to the list.
When the list and the solution are same length, they get compared. If they are same, you move to win section, if not, the sequence is cleared and user needs to refill content.

PUN 2 Getting Custom Properties

I've recently taken on the task of custom properties in Photon. I have been able to figure out how to set the custom properties, but not get the custom properties. My hashtable is in my player controller script, while the place where I set (and where I want to get) properties is in a round loop script.
From RoundSystem:
private IEnumerator TeamBalance()
{
angelCount = Mathf.Floor(PhotonNetwork.PlayerList.Length * angelPercent);
currentAngels = angelCount;
currentPlayers = PhotonNetwork.PlayerList.Length;
foreach (var item in PhotonNetwork.PlayerList)
{
var itemPhotonView = (PhotonView)item.TagObject;
itemPhotonView.RPC("SetPlayerTeam", item, citiString);
}
for (int i = 0; i < angelCount;)
{
var item = PhotonNetwork.PlayerList[Random.Range(0, PhotonNetwork.PlayerList.Length)];
var itemPhotonView = (PhotonView)item.TagObject;
if (/* random player selected's, AKA, item's team == citiString */)
{
itemPhotonView.RPC("SetPlayerTeam", item, angelString);
i++;
}
}
yield return null;
//the reason this is in an IEnumerator with 'yield return null'
//is because I plan to add a waiting period once I figure this out
//it's for the game loop
}
From PlayerController:
[PunRPC]
public void SetPlayerTeam(string teamString)
{
//in the class: private ExitGames.Client.Photon.Hashtable playerProperties;
if (!playerProperties.ContainsKey("team"))
{
playerProperties.Add("team", teamString);
}
playerProperties["team"] = teamString;
PhotonNetwork.LocalPlayer.SetCustomProperties(playerProperties);
}
At the beginning of the round, a percentage (in this case 1/3) of players are chosen to be an "angel". The check here is needed because in cases of multiple angels, you don't want an already existing angel to count as a new change. (Also, it's probably important to known generally how to get custom properties if I'm going to be using them.) If I don't include the check in RoundSystem, the outcome is 2 citizens and 1 angel (in a test with 3 players). Also, if you see any spaghetti code that could be improved on, please don't hesitate to tell me. :)
Use Player.CustomProperties dictionary to access player's custom properties.
foreach (var item in PhotonNetwork.PlayerList)
{
if (item.CustomProperties.ContainsKey("team"))
{
Debug.Log(item.CustomProperties["team"]);
}
}
Also, the RoundSystem can implement IInRoomCallbacks interface and listen to OnPlayerPropertiesUpdate to catch the exact moment when the team gets updated. https://doc-api.photonengine.com/en/pun/v2/interface_photon_1_1_realtime_1_1_i_in_room_callbacks.html

Checking current scene name using SceneManager

So I've decided to split my game into separate scenes(main menu, gameover, etc.) And I'm trying to use an if statement in my code to check if the current scene is "gameover" so I can perform some action. I know Unity now uses SceneManager instead of Application, but what's the equivalent of this function
if(Application.loadedLevelName == "gameover")
I've tried SceneManager.GetActiveScene == "gameover"
But I just get errors like can't use == here.
I have imported SceneManager aswell
You're getting an error here because SceneManager.GetActiveScene() returns an object of type SceneManager.Scene, not a string. However, according to the documentation, this gives you access to the public Scene.name, which is a string.
So the non-deprecated equivalent of:
if (Application.loadedLevelName == "gameover") {
// ...
}
Would be:
if (SceneManager.GetActiveScene().name == "gameover") {
// ...
}

How to lock in a single skeleton

I'm creating an application using the SDK, in which I must have only one user and lock it so if somebody else comes along, even if that person is closer to Kinect, the application keeps tracking the first skeleton it tracked.
From the msdn library I found I could use the Skeletom Stream Class:
Property: AppChoosesSkeletons = Gets or sets a Boolean value that determines whether the application chooses which skeletons to track.
Method: SkeletonStream.ChooseSkeletons (Int32) = Chooses one skeleton to track.
Syntax: public void ChooseSkeletons (int trackingId1)
I'm not very good at programming and I'm using C#, I thought of writing something like the code down, but it says that I'm using an Invalid Expression.
SkeletonFrame SFrame = e.OpenSkeletonFrame();
if (SFrame == null) return;
Skeleton[] Skeletons = new Skeleton[SFrame.SkeletonArrayLength];
SFrame.CopySkeletonDataTo(Skeletons);
int firstSkeleton = Skeletons[0].TrackingId;
sensor.SkeletonStream.ChooseSkeletons(int firstSkeleton);
if (firstSkeleton == null)
return;
if (SkeletonTrackingState.Tracked == firstSkeleton.TrackingState)
{
//body...
The problem is with the sensor.SkeletonStream.ChooseSkeletons(int firstSkeleton, it says int firstSkeleton cannot be used. Could someone please help me? Thanks!
sensor.SkeletonStream.ChooseSkeletons(int firstSkeleton);
What do you want to achive with this line ?
Imo if you want to cast firstSkeleton to int write it like this:
sensor.SkeletonStream.ChooseSkeletons((int) firstSkeleton);
if you don`t want to cast it and just to give and int variable to methid just write:
sensor.SkeletonStream.ChooseSkeletons(firstSkeleton);
You can not lock a skeleton, but you can choose the skeleton that you want to track, regardless of its position. It gets complicated when both people leave the Kinect's field of view.
By setting AppChoosesSkeletons to true you are able to chose the user that you want to track. To specify the user or users to track, call the SkeletonStream.ChooseSkeletons method and pass the tracking ID of one or two skeletons you want to track (or no parameters if no skeletons are to be tracked).
Something like this:
private void ChooseSkeleton()
{
if (this.kinect != null && this.kinect.SkeletonStream != null)
{
if (!this.kinect.SkeletonStream.AppChoosesSkeletons)
{
this.kinect.SkeletonStream.AppChoosesSkeletons = true; // Ensure AppChoosesSkeletons is set
}
foreach (Skeleton skeleton in this.skeletonData.Where(s => s.TrackingState != SkeletonTrackingState.NotTracked))
{
int ID { get.skeleton[1]}//Get ID here
}
if (ID = 0)
{
this.kinect.SkeletonStream.ChooseSkeletons(ID); // Track this skeleton
}
}
}

Categories