You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
6.8 KiB
C#

namespace Fusion {
using System;
using System.Collections.Generic;
using Fusion.Analyzer;
using UnityEngine;
/// <summary>
/// Flags a MonoBehaviour class as a RunnerVisibilityControl recognized type.
/// Will be included in runner visibility handling, and will be found by <see cref="EnableOnSingleRunner"/> component finds.
/// </summary>
public interface IRunnerVisibilityRecognizedType {
}
/// <summary>
/// Identifies visible/audible components (such as renderers, canvases, lights) that should be enabled/disabled by runner visibility handling.
/// Automatically added to scene objects and spawned objects during play if running in <see cref="NetworkProjectConfig.PeerModes.Multiple"/>.
/// Additionally this component can be added manually at development time to identify specific Behaviours or Renderers you would like to restrict to one enabled copy at a time.
/// </summary>
[AddComponentMenu("")]
public sealed class RunnerVisibilityLink : MonoBehaviour {
/// <summary>
/// The peer runner that will be used if more than one runner is visible, and this node was manually added by developer (indicating only one instance should be visible at a time).
/// </summary>
public enum PreferredRunners {
/// <summary>
/// The peer/runner with input authority will be used if visible.
/// </summary>
InputAuthority,
/// <summary>
/// The server peer/runner will be used if visible.
/// </summary>
Server,
/// <summary>
/// The first client peer/runner will be used if visible.
/// </summary>
Client,
}
private enum ComponentType {
None,
Renderer,
Behaviour
}
/// <summary>
/// If more than one runner instance is visible, this indicates which peer's clone of this entity should be visible.
/// </summary>
[SerializeField]
#pragma warning disable IDE0044 // Add readonly modifier
public PreferredRunners PreferredRunner;
#pragma warning restore IDE0044 // Add readonly modifier
/// <summary>
/// The associated component with this node. This Behaviour or Renderer will be enabled/disabled when its NetworkRunner.IsVisible value is changed.
/// </summary>
public Component Component;
/// <summary>
/// Guid is used for common objects (user flagged components that should only run in one instance), to identify matching clones.
/// </summary>
[SerializeField]
[ReadOnly]
internal string Guid;
// TODO: This can be removed later. Here for backwards compat for the short term as users may still be using this component.
// Ultimately this component will always be invisible.
[SerializeField]
[HideInInspector]
internal bool _showAtRuntime;
// cached runtime
internal NetworkRunner _runner;
private ComponentType _componentType;
private bool _originalState;
/// <summary>
/// Set to false to indicate that this object should remain disabled even when <see cref="NetworkRunner.IsVisible"/> is set to true.
/// </summary>
public bool DefaultState {
get {
return _originalState;
}
set {
_originalState = value;
}
}
// internal LinkedListNode<RunnerVisibilityNode> _node;
internal bool Enabled {
get { return _componentType == ComponentType.Renderer ? (Component as Renderer).enabled : (Component as UnityEngine.Behaviour).enabled; }
set {
if (Component == null) {
return;
}
if (_componentType == ComponentType.Renderer)
(Component as Renderer).enabled = value;
else {
(Component as UnityEngine.Behaviour).enabled = value;
}
}
}
// TODO: Can be removed most likely now that Node is not user accessible.
// Reset finds the first viable component and automatically adds it
private void Reset() {
_showAtRuntime = true;
Guid = System.Guid.NewGuid().ToString();
}
private bool AssociateComponent(Component component) {
Component = component;
var type = component.GetType();
if (component as Renderer != null) {
_componentType = ComponentType.Renderer;
return true;
} else if (component as UnityEngine.Behaviour != null) {
_componentType = ComponentType.Behaviour;
return true;
}
return false;
}
private void OnValidate() {
if (Component != null) {
if (Component.transform != transform) {
Debug.LogWarning($"{nameof(RunnerVisibilityLink)} can only be associated with components on the same GameObject.");
Component = null;
return;
}
if (AssociateComponent(Component))
return;
Debug.LogWarning($"{nameof(RunnerVisibilityLink)} can only be associated with Components that can be enabled/disabled.");
Component = null;
}
}
private void Awake() {
// TODO: once deprecated, make this flag always the case and remove the bool check.
if (!_showAtRuntime)
this.hideFlags = HideFlags.HideInInspector;
}
private void OnDestroy() {
this.UnregisterNode();
}
internal void Initialize(UnityEngine.Component comp, NetworkRunner runner, LinkedListNode<RunnerVisibilityLink> node) {
_runner = runner;
if (comp is Renderer renderer) {
_componentType = ComponentType.Renderer;
_originalState = renderer.enabled;
renderer.enabled = runner.GetVisible() && _originalState;
//_node = node;
Component = comp;
} else if (comp is UnityEngine.Behaviour behaviour) {
_componentType = ComponentType.Behaviour;
_originalState = behaviour.enabled;
behaviour.enabled = runner.GetVisible() && _originalState;
// _node = node;
Component = comp;
}
}
/// <summary>
/// Sets the visibility state of this node.
/// </summary>
/// <param name="enabled"></param>
public void SetEnabled(bool enabled) {
if (enabled) {
// If this object was originally disabled, we will want to keep it that way, unless it looks like the user enabled the object directly since the last time this was called.
if (_originalState == false) {
// TODO: These only partially work
// User has directly enabled this object - assume it is meant to be enabled
if (Enabled) {
_originalState = true;
} else {
// original state was disabled, so leave it that way.
return;
}
}
Enabled = true;
} else {
// TODO: These only partially work
// Detect/store if user has manually disabled the component
//if (_originalState == true && Enabled == false) {
// _originalState = false;
//}
Enabled = false;
}
}
}
}