public override void OnInspectorGUI() { this.m_Target = (PhotonView)target; bool isProjectPrefab = PhotonEditorUtils.IsPrefab(this.m_Target.gameObject); if (this.m_Target.ObservedComponents == null) { this.m_Target.ObservedComponents = new System.Collections.Generic.List <Component>(); } if (this.m_Target.ObservedComponents.Count == 0) { this.m_Target.ObservedComponents.Add(null); } EditorGUILayout.BeginHorizontal(); // Owner if (isProjectPrefab) { EditorGUILayout.LabelField("Owner:", "Set at runtime"); } else if (!this.m_Target.isOwnerActive) { EditorGUILayout.LabelField("Owner", "Scene"); } else { PhotonPlayer owner = this.m_Target.owner; string ownerInfo = (owner != null) ? owner.NickName : "<no PhotonPlayer found>"; if (string.IsNullOrEmpty(ownerInfo)) { ownerInfo = "<no playername set>"; } EditorGUILayout.LabelField("Owner", "[" + this.m_Target.ownerId + "] " + ownerInfo); } // ownership requests EditorGUI.BeginDisabledGroup(Application.isPlaying); OwnershipOption own = (OwnershipOption)EditorGUILayout.EnumPopup(this.m_Target.ownershipTransfer, GUILayout.Width(100)); if (own != this.m_Target.ownershipTransfer) { // jf: fixed 5 and up prefab not accepting changes if you quit Unity straight after change. // not touching the define nor the rest of the code to avoid bringing more problem than solving. EditorUtility.SetDirty(this.m_Target); Undo.RecordObject(this.m_Target, "Change PhotonView Ownership Transfer"); this.m_Target.ownershipTransfer = own; } EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); // View ID if (isProjectPrefab) { EditorGUILayout.LabelField("View ID", "Set at runtime"); } else if (EditorApplication.isPlaying) { EditorGUILayout.LabelField("View ID", this.m_Target.viewID.ToString()); } else { int idValue = EditorGUILayout.IntField("View ID [1.." + (PhotonNetwork.MAX_VIEW_IDS - 1) + "]", this.m_Target.viewID); if (this.m_Target.viewID != idValue) { Undo.RecordObject(this.m_Target, "Change PhotonView viewID"); this.m_Target.viewID = idValue; } } // Locally Controlled if (EditorApplication.isPlaying) { string masterClientHint = PhotonNetwork.isMasterClient ? "(master)" : ""; EditorGUILayout.Toggle("Controlled locally: " + masterClientHint, this.m_Target.isMine); } // ViewSynchronization (reliability) if (this.m_Target.synchronization == ViewSynchronization.Off) { GUI.color = Color.grey; } EditorGUILayout.PropertyField(serializedObject.FindProperty("synchronization"), new GUIContent("Observe option:")); if (this.m_Target.synchronization != ViewSynchronization.Off && this.m_Target.ObservedComponents.FindAll(item => item != null).Count == 0) { GUILayout.BeginVertical(GUI.skin.box); GUILayout.Label("Warning", EditorStyles.boldLabel); GUILayout.Label("Setting the synchronization option only makes sense if you observe something."); GUILayout.EndVertical(); } DrawSpecificTypeSerializationOptions(); GUI.color = Color.white; DrawObservedComponentsList(); // Cleanup: save and fix look if (GUI.changed) { #if !UNITY_MIN_5_3 EditorUtility.SetDirty(this.m_Target); #endif PhotonViewHandler.HierarchyChange(); // TODO: check if needed } GUI.color = Color.white; #if !UNITY_MIN_5_3 EditorGUIUtility.LookLikeControls(); #endif }
public static bool IsInTheSceneInPlayMode(GameObject go) { return(Application.isPlaying && !PhotonEditorUtils.IsPrefab(go)); }
public override void OnInspectorGUI() { this.serializedObject.UpdateIfRequiredOrScript(); VoiceLogger.ExposeLogLevel(this.serializedObject, this.connection); EditorGUI.BeginChangeCheck(); this.connection.GlobalRecordersLogLevel = VoiceLogger.ExposeLogLevel(this.globalRecordersLogLevelSp); this.connection.GlobalSpeakersLogLevel = VoiceLogger.ExposeLogLevel(this.globalSpeakersLogLevelSp); EditorGUILayout.PropertyField(this.autoCreateSpeakerIfNotFoundSp, new GUIContent("Create Speaker If Not Found", "Auto instantiate a GameObject and attach a Speaker component to link to a remote audio stream if no candidate could be foun")); EditorGUILayout.PropertyField(this.updateIntervalSp, new GUIContent("Update Interval (ms)", "time [ms] between consecutive SendOutgoingCommands calls")); if (PhotonVoiceEditorUtils.IsInTheSceneInPlayMode(this.connection.gameObject)) { this.connection.PrimaryRecorder = EditorGUILayout.ObjectField( new GUIContent("Primary Recorder", "Main Recorder to be used for transmission by default"), this.connection.PrimaryRecorder, typeof(Recorder), true) as Recorder; if (this.connection.SpeakerPrefab == null) { EditorGUILayout.HelpBox("Speaker prefab needs to have a Speaker component in the hierarchy.", MessageType.Info); } this.connection.SpeakerPrefab = EditorGUILayout.ObjectField(new GUIContent("Speaker Prefab", "Prefab that contains Speaker component to be instantiated when receiving a new remote audio source info"), this.connection.SpeakerPrefab, typeof(GameObject), false) as GameObject; EditorGUILayout.PropertyField(this.globalPlayDelaySettingsSp, new GUIContent("Global Playback Delay Configuration", "Remote audio stream playback delay to compensate packets latency variations."), true); this.connection.SetGlobalPlaybackDelaySettings( this.globalPlayDelaySettingsSp.FindPropertyRelative("MinDelaySoft").intValue, this.globalPlayDelaySettingsSp.FindPropertyRelative("MaxDelaySoft").intValue, this.globalPlayDelaySettingsSp.FindPropertyRelative("MaxDelayHard").intValue); } else { EditorGUILayout.PropertyField(this.enableSupportLoggerSp, new GUIContent("Support Logger", "Logs additional info for debugging.\nUse this when you submit bugs to the Photon Team.")); #if !UNITY_ANDROID && !UNITY_IOS EditorGUILayout.PropertyField(this.runInBackground, new GUIContent("Run In Background", "Sets Unity's Application.runInBackground: Should the application keep running when the application is in the background?")); #endif #if !UNITY_IOS EditorGUILayout.PropertyField(this.keepAliveInBackgroundSp, new GUIContent("Background Timeout (ms)", "Defines for how long the Fallback Thread should keep the connection, before it may time out as usual.")); #endif EditorGUILayout.PropertyField(this.applyDontDestroyOnLoadSp, new GUIContent("Don't Destroy On Load", "Persists the GameObject across scenes using Unity's GameObject.DontDestroyOnLoad")); if (this.applyDontDestroyOnLoadSp.boolValue && !PhotonEditorUtils.IsPrefab(this.connection.gameObject)) { if (this.connection.transform.parent != null) { EditorGUILayout.HelpBox("DontDestroyOnLoad only works for root GameObjects or components on root GameObjects.", MessageType.Warning); if (GUILayout.Button("Detach")) { this.connection.transform.parent = null; } } } EditorGUILayout.PropertyField(this.primaryRecorderSp, new GUIContent("Primary Recorder", "Main Recorder to be used for transmission by default")); GameObject prefab = this.speakerPrefabSp.objectReferenceValue as GameObject; if (prefab == null) { EditorGUILayout.HelpBox("Speaker prefab needs to have a Speaker component in the hierarchy.", MessageType.Info); } prefab = EditorGUILayout.ObjectField(new GUIContent("Speaker Prefab", "Prefab that contains Speaker component to be instantiated when receiving a new remote audio source info"), prefab, typeof(GameObject), false) as GameObject; if (prefab == null || prefab.GetComponentInChildren <Speaker>() != null) { this.speakerPrefabSp.objectReferenceValue = prefab; } else { Debug.LogError("SpeakerPrefab must have a component of type Speaker in its hierarchy.", this); } EditorGUILayout.PropertyField(this.globalPlayDelaySettingsSp, new GUIContent("Global Playback Delay Settings", "Remote audio stream playback delay to compensate packets latency variations."), true); } if (!this.connection.Client.IsConnected) { this.DisplayAppSettings(); } EditorGUILayout.PropertyField(this.statsResetInterval, new GUIContent("Stats Reset Interval (ms)", "time [ms] between statistics calculations")); if (EditorGUI.EndChangeCheck()) { this.serializedObject.ApplyModifiedProperties(); } if (PhotonVoiceEditorUtils.IsInTheSceneInPlayMode(this.connection.gameObject)) { this.DisplayVoiceStats(); this.DisplayDebugInfo(this.connection.Client); this.DisplayCachedVoiceInfo(); this.DisplayTrafficStats(this.connection.Client.LoadBalancingPeer); } }
// this method corrects the IDs for photonviews in the scene and in prefabs // make sure prefabs always use viewID 0 // make sure instances never use a owner // this is a editor class that should only run if not playing internal static void HierarchyChange() { if (Application.isPlaying) { //Debug.Log("HierarchyChange ignored, while running."); CheckSceneForStuckHandlers = true; // done once AFTER play mode. return; } if (CheckSceneForStuckHandlers) { CheckSceneForStuckHandlers = false; PhotonNetwork.InternalCleanPhotonMonoFromSceneIfStuck(); } HashSet <PhotonView> pvInstances = new HashSet <PhotonView>(); HashSet <int> usedInstanceViewNumbers = new HashSet <int>(); bool fixedSomeId = false; //// the following code would be an option if we only checked scene objects (but we can check all PVs) //PhotonView[] pvObjects = GameObject.FindSceneObjectsOfType(typeof(PhotonView)) as PhotonView[]; //Debug.Log("HierarchyChange. PV Count: " + pvObjects.Length); string levelName = SceneManagerHelper.ActiveSceneName; #if UNITY_EDITOR levelName = SceneManagerHelper.EditorActiveSceneName; #endif int minViewIdInThisScene = PunSceneSettings.MinViewIdForScene(levelName); //Debug.Log("Level '" + Application.loadedLevelName + "' has a minimum ViewId of: " + minViewIdInThisScene); PhotonView[] pvObjects = Resources.FindObjectsOfTypeAll(typeof(PhotonView)) as PhotonView[]; foreach (PhotonView view in pvObjects) { // first pass: fix prefabs to viewID 0 if they got a view number assigned (cause they should not have one!) if (PhotonEditorUtils.IsPrefab(view.gameObject)) { if (view.viewID != 0 || view.prefixBackup != -1 || view.instantiationId != -1) { #if !UNITY_2018_3_OR_NEWER Debug.LogWarning("PhotonView on persistent object being fixed (id and prefix must be 0). Was: " + view); #endif view.viewID = 0; view.prefixBackup = -1; view.instantiationId = -1; EditorUtility.SetDirty(view); // even in Unity 5.3+ it's OK to SetDirty() for non-scene objects. fixedSomeId = true; } } else { // keep all scene-instanced PVs for later re-check pvInstances.Add(view); } } Dictionary <GameObject, int> idPerObject = new Dictionary <GameObject, int>(); // second pass: check all used-in-scene viewIDs for duplicate viewIDs (only checking anything non-prefab) // scene-PVs must have user == 0 (scene/room) and a subId != 0 foreach (PhotonView view in pvInstances) { if (view.ownerId > 0) { Debug.Log("Re-Setting Owner ID of: " + view); } view.ownerId = 0; // simply make sure no owner is set (cause room always uses 0) view.prefix = -1; // TODO: prefix could be settable via inspector per scene?! if (view.viewID != 0) { if (view.viewID < minViewIdInThisScene || usedInstanceViewNumbers.Contains(view.viewID)) { view.viewID = 0; // avoid duplicates and negative values by assigning 0 as (temporary) number to be fixed in next pass } else { usedInstanceViewNumbers.Add(view.viewID); // builds a list of currently used viewIDs int instId = 0; if (idPerObject.TryGetValue(view.gameObject, out instId)) { view.instantiationId = instId; } else { view.instantiationId = view.viewID; idPerObject[view.gameObject] = view.instantiationId; } } } } // third pass: anything that's now 0 must get a new (not yet used) ID (starting at 0) int lastUsedId = (minViewIdInThisScene > 0) ? minViewIdInThisScene - 1 : 0; foreach (PhotonView view in pvInstances) { if (view.viewID == 0) { Undo.RecordObject(view, "Automatic viewID change for: " + view.gameObject.name); // Debug.LogWarning("setting scene ID: " + view.gameObject.name + " ID: " + view.subId.ID + " scene ID: " + view.GetSceneID() + " IsPersistent: " + EditorUtility.IsPersistent(view.gameObject) + " IsSceneViewIDFree: " + IsSceneViewIDFree(view.subId.ID, view)); int nextViewId = PhotonViewHandler.GetID(lastUsedId, usedInstanceViewNumbers); view.viewID = nextViewId; int instId = 0; if (idPerObject.TryGetValue(view.gameObject, out instId)) { view.instantiationId = instId; } else { view.instantiationId = view.viewID; idPerObject[view.gameObject] = nextViewId; } lastUsedId = nextViewId; fixedSomeId = true; #if !UNITY_MIN_5_3 EditorUtility.SetDirty(view); #endif } } if (fixedSomeId) { //Debug.LogWarning("Some subId was adjusted."); // this log is only interesting for Exit Games } }