IDXWT3DH3C3X7PP4VVKUZTCGDALW6NH7KREI4E2VFQVMCBJKDMTAC
HUULCHM5GFGZ7GKLCULFHC333LQ3LBI62VKII3YL5O4C5CDGCT4AC
O236B5LO6PHJ4TPZGYXDVSLB5EGXXRRLYYVWI46DPL5LEGXEIHZQC
XF52N4U7HWXOF4PDSCR7LCUENLTSXLWEZGS2IJ6562KYI567Z2GAC
XIPU27GAVXMAKDP42DDSYNA6DSN5WBHH6AG4EK6YIIM43AQQHQAAC
R5JJA7VQ5HCQ6TZEFMORFMPYBXTMIOBAH6YMDCG4QZKBJZCQLR4AC
VPXUP5WZTVC3OVD73TNKPK43IAGFXGUGCEJT56JM4IT4APYQXUHAC
CXQW7UICWDJFL3M44O2XVH6JNCRDGRBMPMYR2LEGFJCANPG4YASAC
3JTZIZGE6WPLZCIWXXLG3SQA2LVSVP26P7U52YBMBTY2VCC6LBLAC
W2OVIBJVB5UOIM6SFCM7QS7YIPC6L2UICCXNUPS4XXOJEFJDKDQAC
6BA5NHNF3UWSL65KCWSXLRTPGYVS4N4NORDJ4FZTZ5SRRDPI4YZAC
6PTVYMJH7JKB54XWYGZG2TWYKC5Y2LP4FUN7GJKSW4LO33DE5OTQC
CD5FF75KTOBTMVMTMCKMR6F5DFKOF26I5K43ITNHGBI3ZAZHA4RAC
HXTSBPAP75A7EC4RKWYQMVPPHPNZFPHUORBZWDHGEB6MPAGI7G7AC
S256EPZUSOF4TV2KGOFZDJXUFDD57GZFYTCARFJ3SD24RPC56PHAC
fileFormatVersion: 2
guid: 98e78fba19625ee419c9b9b712a790a2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using TagFighter.Resources;
using UnityEngine;
namespace TagFighter.UI
{
public class LookAtCamera : MonoBehaviour
{
Transform _camera;
protected void Awake() {
_camera = Camera.main.transform;
}
protected void LateUpdate() {
Vector3 dirToCamera = (_camera.position - transform.position).normalized;
transform.LookAt(transform.position + dirToCamera * -1);
}
}
}
fileFormatVersion: 2
guid: 98e78fba19625ee419c9b9b712a790a2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using UnityEngine;
{
}
}
}
}
}
protected void LateUpdate() {
Vector3 dirToCamera = (cameraTransform.position - transform.position).normalized;
transform.LookAt(transform.position + dirToCamera * -1);
protected void Start() {
transform.Find($"RedTag").GetComponent<ResourceWatcher>().WatchedResource = GetComponentInParent<RedTag>();
transform.Find($"GreenTag").GetComponent<ResourceWatcher>().WatchedResource = GetComponentInParent<GreenTag>();
transform.Find($"BlueTag").GetComponent<ResourceWatcher>().WatchedResource = GetComponentInParent<BlueTag>();
protected void Awake() {
cameraTransform = Camera.main.transform;
public class TagUIController : MonoBehaviour
{
Transform cameraTransform;
namespace TagFighter.UI
using TagFighter.Resources;
objectReference: {fileID: 0}
- target: {fileID: 5194998107043930577, guid: ffa8c2c800c07c74b951cc3710469917,
type: 3}
propertyPath: watch._classRef
value: TagFighter.Resources.GreenTag, Assembly-CSharp
objectReference: {fileID: 0}
- target: {fileID: 5194998107043930577, guid: ffa8c2c800c07c74b951cc3710469917,
type: 3}
propertyPath: _watch._classRef
value: TagFighter.Resources.GreenTag, Assembly-CSharp
- target: {fileID: 5194998107043930577, guid: ffa8c2c800c07c74b951cc3710469917,
type: 3}
propertyPath: watch._classRef
value: TagFighter.Resources.BlueTag, Assembly-CSharp
objectReference: {fileID: 0}
- target: {fileID: 5194998107043930577, guid: ffa8c2c800c07c74b951cc3710469917,
type: 3}
propertyPath: _watch._classRef
value: TagFighter.Resources.BlueTag, Assembly-CSharp
objectReference: {fileID: 0}
fileFormatVersion: 2
guid: e3f7ebdc26195794b90ef4106fbb35ab
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: c0d3a2bba6ec8f24a973ba83f00d99ec
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 6a936c5e8011ef24db75cefabc58683a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
// Copyright (c) Rotorz Limited. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root.
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace TypeReferences {
/// <summary>
/// Custom property drawer for <see cref="ClassTypeReference"/> properties.
/// </summary>
[CustomPropertyDrawer(typeof(ClassTypeReference))]
[CustomPropertyDrawer(typeof(ClassTypeConstraintAttribute), true)]
public sealed class ClassTypeReferencePropertyDrawer : PropertyDrawer {
#region Type Filtering
/// <summary>
/// Gets or sets a function that returns a collection of types that are
/// to be excluded from drop-down. A value of <c>null</c> specifies that
/// no types are to be excluded.
/// </summary>
/// <remarks>
/// <para>This property must be set immediately before presenting a class
/// type reference property field using <see cref="EditorGUI.PropertyField"/>
/// or <see cref="EditorGUILayout.PropertyField"/> since the value of this
/// property is reset to <c>null</c> each time the control is drawn.</para>
/// <para>Since filtering makes extensive use of <see cref="ICollection{Type}.Contains"/>
/// it is recommended to use a collection that is optimized for fast
/// lookups such as <see cref="HashSet{Type}"/> for better performance.</para>
/// </remarks>
/// <example>
/// <para>Exclude a specific type from being selected:</para>
/// <code language="csharp"><![CDATA[
/// private SerializedProperty _someClassTypeReferenceProperty;
///
/// public override void OnInspectorGUI() {
/// serializedObject.Update();
///
/// ClassTypeReferencePropertyDrawer.ExcludedTypeCollectionGetter = GetExcludedTypeCollection;
/// EditorGUILayout.PropertyField(_someClassTypeReferenceProperty);
///
/// serializedObject.ApplyModifiedProperties();
/// }
///
/// private ICollection<Type> GetExcludedTypeCollection() {
/// var set = new HashSet<Type>();
/// set.Add(typeof(SpecialClassToHideInDropdown));
/// return set;
/// }
/// ]]></code>
/// </example>
public static Func<ICollection<Type>> ExcludedTypeCollectionGetter { get; set; }
private static List<Type> GetFilteredTypes(ClassTypeConstraintAttribute filter) {
var types = new List<Type>();
var excludedTypes = (ExcludedTypeCollectionGetter != null ? ExcludedTypeCollectionGetter() : null);
var assembly = Assembly.GetExecutingAssembly();
FilterTypes(assembly, filter, excludedTypes, types);
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
FilterTypes(Assembly.Load(referencedAssembly), filter, excludedTypes, types);
types.Sort((a, b) => a.FullName.CompareTo(b.FullName));
return types;
}
private static void FilterTypes(Assembly assembly, ClassTypeConstraintAttribute filter, ICollection<Type> excludedTypes, List<Type> output) {
foreach (var type in assembly.GetTypes()) {
if (!type.IsVisible || !type.IsClass)
continue;
if (filter != null && !filter.IsConstraintSatisfied(type))
continue;
if (excludedTypes != null && excludedTypes.Contains(type))
continue;
output.Add(type);
}
}
#endregion
#region Type Utility
private static Dictionary<string, Type> s_TypeMap = new Dictionary<string, Type>();
private static Type ResolveType(string classRef) {
Type type;
if (!s_TypeMap.TryGetValue(classRef, out type)) {
type = !string.IsNullOrEmpty(classRef) ? Type.GetType(classRef) : null;
s_TypeMap[classRef] = type;
}
return type;
}
#endregion
#region Control Drawing / Event Handling
private static readonly int s_ControlHint = typeof(ClassTypeReferencePropertyDrawer).GetHashCode();
private static GUIContent s_TempContent = new GUIContent();
private static string DrawTypeSelectionControl(Rect position, GUIContent label, string classRef, ClassTypeConstraintAttribute filter) {
if (label != null && label != GUIContent.none)
position = EditorGUI.PrefixLabel(position, label);
int controlID = GUIUtility.GetControlID(s_ControlHint, FocusType.Keyboard, position);
bool triggerDropDown = false;
switch (Event.current.GetTypeForControl(controlID)) {
case EventType.ExecuteCommand:
if (Event.current.commandName == "TypeReferenceUpdated") {
if (s_SelectionControlID == controlID) {
if (classRef != s_SelectedClassRef) {
classRef = s_SelectedClassRef;
GUI.changed = true;
}
s_SelectionControlID = 0;
s_SelectedClassRef = null;
}
}
break;
case EventType.MouseDown:
if (GUI.enabled && position.Contains(Event.current.mousePosition)) {
GUIUtility.keyboardControl = controlID;
triggerDropDown = true;
Event.current.Use();
}
break;
case EventType.KeyDown:
if (GUI.enabled && GUIUtility.keyboardControl == controlID) {
if (Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.Space) {
triggerDropDown = true;
Event.current.Use();
}
}
break;
case EventType.Repaint:
// Remove assembly name from content of popup control.
var classRefParts = classRef.Split(',');
s_TempContent.text = classRefParts[0].Trim();
if (s_TempContent.text == "")
s_TempContent.text = "(None)";
else if (ResolveType(classRef) == null)
s_TempContent.text += " {Missing}";
EditorStyles.popup.Draw(position, s_TempContent, controlID);
break;
}
if (triggerDropDown) {
s_SelectionControlID = controlID;
s_SelectedClassRef = classRef;
var filteredTypes = GetFilteredTypes(filter);
DisplayDropDown(position, filteredTypes, ResolveType(classRef), filter.Grouping);
}
return classRef;
}
private static void DrawTypeSelectionControl(Rect position, SerializedProperty property, GUIContent label, ClassTypeConstraintAttribute filter) {
try {
bool restoreShowMixedValue = EditorGUI.showMixedValue;
EditorGUI.showMixedValue = property.hasMultipleDifferentValues;
property.stringValue = DrawTypeSelectionControl(position, label, property.stringValue, filter);
EditorGUI.showMixedValue = restoreShowMixedValue;
}
finally {
ExcludedTypeCollectionGetter = null;
}
}
private static void DisplayDropDown(Rect position, List<Type> types, Type selectedType, ClassGrouping grouping) {
var menu = new GenericMenu();
menu.AddItem(new GUIContent("(None)"), selectedType == null, s_OnSelectedTypeName, null);
menu.AddSeparator("");
for (int i = 0; i < types.Count; ++i) {
var type = types[i];
string menuLabel = FormatGroupedTypeName(type, grouping);
if (string.IsNullOrEmpty(menuLabel))
continue;
var content = new GUIContent(menuLabel);
menu.AddItem(content, type == selectedType, s_OnSelectedTypeName, type);
}
menu.DropDown(position);
}
private static string FormatGroupedTypeName(Type type, ClassGrouping grouping) {
string name = type.FullName;
switch (grouping) {
default:
case ClassGrouping.None:
return name;
case ClassGrouping.ByNamespace:
return name.Replace('.', '/');
case ClassGrouping.ByNamespaceFlat:
int lastPeriodIndex = name.LastIndexOf('.');
if (lastPeriodIndex != -1)
name = name.Substring(0, lastPeriodIndex) + "/" + name.Substring(lastPeriodIndex + 1);
return name;
case ClassGrouping.ByAddComponentMenu:
var addComponentMenuAttributes = type.GetCustomAttributes(typeof(AddComponentMenu), false);
if (addComponentMenuAttributes.Length == 1)
return ((AddComponentMenu)addComponentMenuAttributes[0]).componentMenu;
return "Scripts/" + type.FullName.Replace('.', '/');
}
}
private static int s_SelectionControlID;
private static string s_SelectedClassRef;
private static readonly GenericMenu.MenuFunction2 s_OnSelectedTypeName = OnSelectedTypeName;
private static void OnSelectedTypeName(object userData) {
var selectedType = userData as Type;
s_SelectedClassRef = ClassTypeReference.GetClassRef(selectedType);
var typeReferenceUpdatedEvent = EditorGUIUtility.CommandEvent("TypeReferenceUpdated");
EditorWindow.focusedWindow.SendEvent(typeReferenceUpdatedEvent);
}
#endregion
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
return EditorStyles.popup.CalcHeight(GUIContent.none, 0);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
DrawTypeSelectionControl(position, property.FindPropertyRelative("_classRef"), label, attribute as ClassTypeConstraintAttribute);
}
}
}
fileFormatVersion: 2
guid: bbeadb5396d227843b94f9b779273d08
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
// Copyright (c) Rotorz Limited. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root.
using System;
using UnityEngine;
namespace TypeReferences {
/// <summary>
/// Reference to a class <see cref="System.Type"/> with support for Unity serialization.
/// </summary>
[Serializable]
public sealed class ClassTypeReference : ISerializationCallbackReceiver {
public static string GetClassRef(Type type) {
return type != null
? type.FullName + ", " + type.Assembly.GetName().Name
: "";
}
/// <summary>
/// Initializes a new instance of the <see cref="ClassTypeReference"/> class.
/// </summary>
public ClassTypeReference() {
}
/// <summary>
/// Initializes a new instance of the <see cref="ClassTypeReference"/> class.
/// </summary>
/// <param name="assemblyQualifiedClassName">Assembly qualified class name.</param>
public ClassTypeReference(string assemblyQualifiedClassName) {
Type = !string.IsNullOrEmpty(assemblyQualifiedClassName)
? Type.GetType(assemblyQualifiedClassName)
: null;
}
/// <summary>
/// Initializes a new instance of the <see cref="ClassTypeReference"/> class.
/// </summary>
/// <param name="type">Class type.</param>
/// <exception cref="System.ArgumentException">
/// If <paramref name="type"/> is not a class type.
/// </exception>
public ClassTypeReference(Type type) {
Type = type;
}
[SerializeField]
private string _classRef;
#region ISerializationCallbackReceiver Members
void ISerializationCallbackReceiver.OnAfterDeserialize() {
if (!string.IsNullOrEmpty(_classRef)) {
_type = System.Type.GetType(_classRef);
if (_type == null)
Debug.LogWarning(string.Format("'{0}' was referenced but class type was not found.", _classRef));
}
else {
_type = null;
}
}
void ISerializationCallbackReceiver.OnBeforeSerialize() {
}
#endregion
private Type _type;
/// <summary>
/// Gets or sets type of class reference.
/// </summary>
/// <exception cref="System.ArgumentException">
/// If <paramref name="value"/> is not a class type.
/// </exception>
public Type Type {
get { return _type; }
set {
if (value != null && !value.IsClass)
throw new ArgumentException(string.Format("'{0}' is not a class type.", value.FullName), "value");
_type = value;
_classRef = GetClassRef(value);
}
}
public static implicit operator string(ClassTypeReference typeReference) {
return typeReference._classRef;
}
public static implicit operator Type(ClassTypeReference typeReference) {
return typeReference.Type;
}
public static implicit operator ClassTypeReference(Type type) {
return new ClassTypeReference(type);
}
public override string ToString() {
return Type != null ? Type.FullName : "(None)";
}
}
}
fileFormatVersion: 2
guid: 264b275c28ca9ab47b1e70d0cb885f43
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
// Copyright (c) Rotorz Limited. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root.
using System;
using UnityEngine;
namespace TypeReferences {
/// <summary>
/// Indicates how selectable classes should be collated in drop-down menu.
/// </summary>
public enum ClassGrouping {
/// <summary>
/// No grouping, just show type names in a list; for instance, "Some.Nested.Namespace.SpecialClass".
/// </summary>
None,
/// <summary>
/// Group classes by namespace and show foldout menus for nested namespaces; for
/// instance, "Some > Nested > Namespace > SpecialClass".
/// </summary>
ByNamespace,
/// <summary>
/// Group classes by namespace; for instance, "Some.Nested.Namespace > SpecialClass".
/// </summary>
ByNamespaceFlat,
/// <summary>
/// Group classes in the same way as Unity does for its component menu. This
/// grouping method must only be used for <see cref="MonoBehaviour"/> types.
/// </summary>
ByAddComponentMenu,
}
/// <summary>
/// Base class for class selection constraints that can be applied when selecting
/// a <see cref="ClassTypeReference"/> with the Unity inspector.
/// </summary>
public abstract class ClassTypeConstraintAttribute : PropertyAttribute {
private ClassGrouping _grouping = ClassGrouping.ByNamespaceFlat;
private bool _allowAbstract = false;
/// <summary>
/// Gets or sets grouping of selectable classes. Defaults to <see cref="ClassGrouping.ByNamespaceFlat"/>
/// unless explicitly specified.
/// </summary>
public ClassGrouping Grouping {
get { return _grouping; }
set { _grouping = value; }
}
/// <summary>
/// Gets or sets whether abstract classes can be selected from drop-down.
/// Defaults to a value of <c>false</c> unless explicitly specified.
/// </summary>
public bool AllowAbstract {
get { return _allowAbstract; }
set { _allowAbstract = value; }
}
/// <summary>
/// Determines whether the specified <see cref="Type"/> satisfies filter constraint.
/// </summary>
/// <param name="type">Type to test.</param>
/// <returns>
/// A <see cref="bool"/> value indicating if the type specified by <paramref name="type"/>
/// satisfies this constraint and should thus be selectable.
/// </returns>
public virtual bool IsConstraintSatisfied(Type type) {
return AllowAbstract || (!type.IsAbstract && !type.ContainsGenericParameters);
}
}
/// <summary>
/// Constraint that allows selection of classes that extend a specific class when
/// selecting a <see cref="ClassTypeReference"/> with the Unity inspector.
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class ClassExtendsAttribute : ClassTypeConstraintAttribute {
/// <summary>
/// Initializes a new instance of the <see cref="ClassExtendsAttribute"/> class.
/// </summary>
public ClassExtendsAttribute() {
}
/// <summary>
/// Initializes a new instance of the <see cref="ClassExtendsAttribute"/> class.
/// </summary>
/// <param name="baseType">Type of class that selectable classes must derive from.</param>
public ClassExtendsAttribute(Type baseType) {
BaseType = baseType;
}
/// <summary>
/// Gets the type of class that selectable classes must derive from.
/// </summary>
public Type BaseType { get; private set; }
/// <inheritdoc/>
public override bool IsConstraintSatisfied(Type type) {
return base.IsConstraintSatisfied(type)
&& BaseType.IsAssignableFrom(type) && type != BaseType;
}
}
/// <summary>
/// Constraint that allows selection of classes that implement a specific interface
/// when selecting a <see cref="ClassTypeReference"/> with the Unity inspector.
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class ClassImplementsAttribute : ClassTypeConstraintAttribute {
/// <summary>
/// Initializes a new instance of the <see cref="ClassImplementsAttribute"/> class.
/// </summary>
public ClassImplementsAttribute() {
}
/// <summary>
/// Initializes a new instance of the <see cref="ClassImplementsAttribute"/> class.
/// </summary>
/// <param name="interfaceType">Type of interface that selectable classes must implement.</param>
public ClassImplementsAttribute(Type interfaceType) {
InterfaceType = interfaceType;
}
/// <summary>
/// Gets the type of interface that selectable classes must implement.
/// </summary>
public Type InterfaceType { get; private set; }
/// <inheritdoc/>
public override bool IsConstraintSatisfied(Type type) {
if (base.IsConstraintSatisfied(type)) {
foreach (var interfaceType in type.GetInterfaces())
if (interfaceType == InterfaceType)
return true;
}
return false;
}
}
}