mirror of
https://github.com/unity-atoms/unity-atoms.git
synced 2025-01-21 23:58:49 -05:00
More efficient AtomicTags (#12)
* - added assembly defintions, and unit tests - improved AtomicTags in regards of #8, #9 and #10 * Fixes #11 - Added Equality Members (inclusive HashCode) for ScriptableObjectVariableBase * removed Rider Plugins from git
This commit is contained in:
parent
81209d83b5
commit
53d6adc07b
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,6 +7,9 @@
|
||||
|
||||
# Visual Studio 2015 cache directory
|
||||
/.vs/
|
||||
/.idea/
|
||||
|
||||
/Assets/Plugins/Editor/JetBrains*
|
||||
|
||||
# Autogenerated VS/MD/Consulo solution and project files
|
||||
ExportedObj/
|
||||
|
8
Assets/Plugins.meta
Normal file
8
Assets/Plugins.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e5e1194a41a16c4491397f36861f748
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,10 +1,90 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityAtoms
|
||||
{
|
||||
public class AtomicTags : MonoBehaviour
|
||||
{
|
||||
public List<StringConstant> Tags;
|
||||
[SerializeField] private List<StringConstant> tags = new List<StringConstant>();
|
||||
/// <summary>
|
||||
/// An Immutable, Sorted version of the Tags
|
||||
/// </summary>
|
||||
public StringConstant[] Tags { get; private set; }
|
||||
|
||||
|
||||
private static Dictionary<string, List<GameObject>> taggedGOs = new Dictionary<string, List<GameObject>>();
|
||||
private static Dictionary<GameObject, AtomicTags> instances = new Dictionary<GameObject, AtomicTags>();
|
||||
|
||||
public void AddTag(StringConstant tag) {
|
||||
tags.Add(tag);
|
||||
tags.Sort((x, y) => String.Compare(x.Value, y.Value, StringComparison.Ordinal));
|
||||
Tags = tags.ToArray();
|
||||
}
|
||||
|
||||
public void RemoveTag(string tag) {
|
||||
for (var i = tags.Count - 1; i >= 0; i--) {
|
||||
if (tags[i].Value == tag) {
|
||||
tags.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
tags.Sort((x, y) => String.Compare(x.Value, y.Value, StringComparison.Ordinal));
|
||||
Tags = tags.ToArray();
|
||||
}
|
||||
|
||||
private void OnValidate() {
|
||||
tags.Sort((x, y) => String.Compare(x.Value, y.Value, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private void OnEnable() {
|
||||
tags.Sort((x, y) => String.Compare(x.Value, y.Value, StringComparison.Ordinal));
|
||||
Tags = tags.ToArray();
|
||||
|
||||
if(! instances.ContainsKey(this.gameObject)) instances.Add(this.gameObject, this);
|
||||
foreach (var stringConstant in tags) {
|
||||
var tag = stringConstant.Value;
|
||||
if(! taggedGOs.ContainsKey(tag)) taggedGOs.Add(tag, new List<GameObject>());
|
||||
taggedGOs[tag].Add(this.gameObject);
|
||||
};
|
||||
}
|
||||
|
||||
private void OnDisable(){
|
||||
if(instances.ContainsKey(this.gameObject)) instances.Remove(this.gameObject);
|
||||
foreach (var stringConstant in tags) {
|
||||
var tag = stringConstant.Value;
|
||||
if(taggedGOs.ContainsKey(tag)) taggedGOs[tag].Remove(this.gameObject);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Faster alternative to go.GetComponent<AtomicTags>() since they are already cached in a dictionary
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// - null if the GameObject does not have AtomicTags or they (or the GO) are disabled
|
||||
/// - the AtomicTag component
|
||||
/// </returns>
|
||||
public static AtomicTags GetForGameObject(GameObject go) {
|
||||
if (!instances.ContainsKey(go)) return null;
|
||||
return instances[go];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all AtomicTags for a given GameObject
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// - null if the GameObject does not have AtomicTags or they (or the GO) are disabled
|
||||
/// - an array of strings containing the tags
|
||||
/// </returns>
|
||||
public static string[] GetAtomicTags(GameObject go) {
|
||||
if (!instances.ContainsKey(go)) return null;
|
||||
var atomicTags = instances[go];
|
||||
string[] result = new string[atomicTags.tags.Count];
|
||||
for (var i = 0; i < result.Length; i++) {
|
||||
result[i] = atomicTags.tags[i].Value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,29 @@ namespace UnityAtoms
|
||||
[Multiline]
|
||||
public string DeveloperDescription = "";
|
||||
|
||||
public virtual T Value { get { return value; } set { } }
|
||||
public virtual T Value { get { return value; } set { throw new NotImplementedException(); } }
|
||||
|
||||
[SerializeField]
|
||||
protected T value;
|
||||
|
||||
protected bool Equals(ScriptableObjectVariableBase<T> other) {
|
||||
return EqualityComparer<T>.Default.Equals(value, other.value);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((ScriptableObjectVariableBase<T>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
unchecked {
|
||||
return EqualityComparer<T>.Default.GetHashCode(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(ScriptableObjectVariableBase<T> left, ScriptableObjectVariableBase<T> right) { return Equals(left, right); }
|
||||
public static bool operator !=(ScriptableObjectVariableBase<T> left, ScriptableObjectVariableBase<T> right) { return !Equals(left, right); }
|
||||
}
|
||||
}
|
16
Assets/UnityAtoms/Editor/UnityAtomsEditor.asmdef
Normal file
16
Assets/UnityAtoms/Editor/UnityAtomsEditor.asmdef
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "UnityAtomsEditor",
|
||||
"references": [
|
||||
"UnityAtoms"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
7
Assets/UnityAtoms/Editor/UnityAtomsEditor.asmdef.meta
Normal file
7
Assets/UnityAtoms/Editor/UnityAtomsEditor.asmdef.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61871b5b170c4024c8ceb1924221649b
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,48 +1,103 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityAtoms
|
||||
{
|
||||
public static class GameObjectExtensions
|
||||
{
|
||||
public static List<StringConstant> GetTags(this GameObject go)
|
||||
{
|
||||
return go.GetComponent<AtomicTags>() ? go.GetComponent<AtomicTags>().Tags : null;
|
||||
}
|
||||
|
||||
public static bool HasTag(this GameObject go, string str)
|
||||
{
|
||||
var tags = go.GetComponent<AtomicTags>() ? go.GetComponent<AtomicTags>().Tags : null;
|
||||
|
||||
for (int i = 0; tags != null && i < tags.Count; ++i)
|
||||
{
|
||||
if (tags[i].Value == str)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#region AtomicTagExtensions
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all AtomicTags for a given GameObject
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// - null if the GameObject does not have Atomic Tags or they (or the GO) are disabled)
|
||||
/// - an array of strings containing the tags
|
||||
/// </returns>
|
||||
public static string[] GetAtomicTags(this GameObject go) {
|
||||
return AtomicTags.GetAtomicTags(go);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/// <returns>
|
||||
/// - False if the GameObject does not have the AtomicTag, else True
|
||||
/// </returns>
|
||||
public static bool HasTag(this GameObject go, string str) {
|
||||
var atomicTags = AtomicTags.GetForGameObject(go);
|
||||
if (atomicTags == null) return false;
|
||||
|
||||
public static bool HasTag(this GameObject go, StringConstant stringConstant)
|
||||
{
|
||||
return go.HasTag(stringConstant.Value);
|
||||
}
|
||||
|
||||
public static bool HasAnyTag(this GameObject go, List<StringConstant> stringConstants)
|
||||
{
|
||||
for (int i = 0; stringConstants != null && i < stringConstants.Count; ++i)
|
||||
{
|
||||
if (go.HasTag(stringConstants[i].Value))
|
||||
var tags = atomicTags.Tags;
|
||||
for (int i = 0; tags != null && i < tags.Length; ++i)
|
||||
{
|
||||
return true;
|
||||
if (tags[i].Value == str)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <returns>
|
||||
/// - False if the GameObject does not have the AtomicTag, else True
|
||||
/// </returns>
|
||||
public static bool HasTag(this GameObject go, StringConstant stringConstant)
|
||||
{
|
||||
return go.HasTag(stringConstant.Value);
|
||||
}
|
||||
|
||||
|
||||
public static bool HasAnyTag(this GameObject go, List<string> strings)
|
||||
{
|
||||
var atomicTags = AtomicTags.GetForGameObject(go);
|
||||
if (atomicTags == null) return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
strings.Sort();
|
||||
var tags = atomicTags.Tags;
|
||||
|
||||
// this makes use of Tags being always sorted
|
||||
// instead of O(n*m) this is worst case: O(n + m) +(because of the sort) O(n * log(n))
|
||||
// O(n*m) ~= O(n^2) <-> O(n+m)+O(n * log(n)) ~= O(3n * log(n))
|
||||
for (int i = 0, j = 0; i < strings.Count && j < tags.Length;) {
|
||||
var x = String.CompareOrdinal(strings[i], tags[j].Value);
|
||||
if (x == 0) {
|
||||
return true;
|
||||
}
|
||||
if (x > 0) {
|
||||
++j;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static bool HasAnyTag(this GameObject go, List<StringConstant> stringConstants)
|
||||
{
|
||||
// basically same method as above, the code is mostly copy and pasted because its not preferable to convert
|
||||
// stringconstants to strings and calling the other method, because of memory allocation
|
||||
var atomicTags = AtomicTags.GetForGameObject(go);
|
||||
if (atomicTags == null) return false;
|
||||
|
||||
stringConstants.Sort((x, y) => String.Compare(x.Value, y.Value, StringComparison.Ordinal));
|
||||
var tags = atomicTags.Tags;
|
||||
|
||||
for (int i = 0, j = 0; i < stringConstants.Count && j < tags.Length;) {
|
||||
var x = String.CompareOrdinal(stringConstants[i].Value, tags[j].Value);
|
||||
if (x == 0) {
|
||||
return true;
|
||||
}
|
||||
if (x > 0) {
|
||||
++j;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
8
Assets/UnityAtoms/Tests.meta
Normal file
8
Assets/UnityAtoms/Tests.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8baf1a7129e6f4b4898cbc0d78f59323
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
55
Assets/UnityAtoms/Tests/AtomicTagTests.cs
Normal file
55
Assets/UnityAtoms/Tests/AtomicTagTests.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using UnityAtoms;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Tests
|
||||
{
|
||||
public class AtomicTagTests
|
||||
{
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AtomicTagTests_HasAnyTagTest(){
|
||||
var go = new GameObject();
|
||||
var atomicTags = go.AddComponent<AtomicTags>();
|
||||
|
||||
string[] random_tags_raw = new[] {"a", "c", "e", "g", "i", "k", "m"};
|
||||
var random_tags_shuffled = random_tags_raw.OrderBy((s => Random.value)).ToList();
|
||||
|
||||
|
||||
var fieldinfo = typeof(StringConstant).GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
|
||||
for (int i = 0; i < random_tags_shuffled.Count; ++i) {
|
||||
var stringConstant = ScriptableObject.CreateInstance<StringConstant>();
|
||||
fieldinfo.SetValue(stringConstant, random_tags_shuffled[i]);
|
||||
atomicTags.AddTag(stringConstant);
|
||||
}
|
||||
|
||||
yield return null;
|
||||
yield return new WaitForFixedUpdate();
|
||||
|
||||
Assert.NotNull(atomicTags);
|
||||
Assert.NotNull(atomicTags.Tags);
|
||||
|
||||
// check if the tags are actually sorted:
|
||||
// Debug.Log(atomicTags.Tags.Select(t => t.Value).Aggregate((a, b) => a + ", " + b));
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var atomicTagsTag in atomicTags.Tags) {
|
||||
Assert.AreEqual(random_tags_raw[i], atomicTagsTag.Value);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsFalse(go.HasAnyTag(new List<string>() {"b", "d", "f", "h", "j", "l"}));
|
||||
Assert.IsTrue(go.HasAnyTag(new List<string>() {"b", "d", "f", "h", "j", "m"}));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
11
Assets/UnityAtoms/Tests/AtomicTagTests.cs.meta
Normal file
11
Assets/UnityAtoms/Tests/AtomicTagTests.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43f8d616142c6304286351b7d295c4e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
32
Assets/UnityAtoms/Tests/ScriptableObjectBaseTest.cs
Normal file
32
Assets/UnityAtoms/Tests/ScriptableObjectBaseTest.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using UnityAtoms;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Tests {
|
||||
public class ScriptableObjectBaseTest {
|
||||
|
||||
[Test]
|
||||
public void ScriptableObjectBaseTest_EqualityMembers() {
|
||||
|
||||
var fieldinfo = typeof(StringConstant).GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
var stringConstant = ScriptableObject.CreateInstance<StringConstant>();
|
||||
fieldinfo.SetValue(stringConstant, "some constant string");
|
||||
|
||||
var stringConstant2 = ScriptableObject.CreateInstance<StringConstant>();
|
||||
fieldinfo.SetValue(stringConstant2, "some constant string");
|
||||
|
||||
var stringConstant3 = ScriptableObject.CreateInstance<StringConstant>();
|
||||
fieldinfo.SetValue(stringConstant3, "some other string");
|
||||
|
||||
Assert.AreEqual("some constant string".GetHashCode(), stringConstant.Value.GetHashCode());
|
||||
Assert.AreEqual("some constant string".GetHashCode(), stringConstant.GetHashCode());
|
||||
Assert.AreEqual(stringConstant2, stringConstant);
|
||||
Assert.IsTrue(stringConstant2 == stringConstant);
|
||||
Assert.IsFalse(stringConstant3 == stringConstant);
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/UnityAtoms/Tests/ScriptableObjectBaseTest.cs.meta
Normal file
3
Assets/UnityAtoms/Tests/ScriptableObjectBaseTest.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d737766b78a458fb94e1d3df4f581a8
|
||||
timeCreated: 1551103675
|
16
Assets/UnityAtoms/Tests/Tests.asmdef
Normal file
16
Assets/UnityAtoms/Tests/Tests.asmdef
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Tests",
|
||||
"references": [
|
||||
"UnityAtoms"
|
||||
],
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
7
Assets/UnityAtoms/Tests/Tests.asmdef.meta
Normal file
7
Assets/UnityAtoms/Tests/Tests.asmdef.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da7b807e38659454ca2f017967aab020
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
3
Assets/UnityAtoms/UnityAtoms.asmdef
Normal file
3
Assets/UnityAtoms/UnityAtoms.asmdef
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "UnityAtoms"
|
||||
}
|
7
Assets/UnityAtoms/UnityAtoms.asmdef.meta
Normal file
7
Assets/UnityAtoms/UnityAtoms.asmdef.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cdb7ffd54e5ff4439104ecd1863bbbf
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,10 +1,11 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"com.unity.ads": "2.0.8",
|
||||
"com.unity.analytics": "2.0.16",
|
||||
"com.unity.package-manager-ui": "1.9.11",
|
||||
"com.unity.analytics": "3.2.2",
|
||||
"com.unity.collab-proxy": "1.2.15",
|
||||
"com.unity.package-manager-ui": "2.0.3",
|
||||
"com.unity.purchasing": "2.0.3",
|
||||
"com.unity.textmeshpro": "1.2.4",
|
||||
"com.unity.textmeshpro": "1.3.0",
|
||||
"com.unity.modules.ai": "1.0.0",
|
||||
"com.unity.modules.animation": "1.0.0",
|
||||
"com.unity.modules.assetbundle": "1.0.0",
|
||||
|
@ -1,21 +1,23 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!159 &1
|
||||
EditorSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 7
|
||||
m_ExternalVersionControlSupport: Visible Meta Files
|
||||
m_SerializationMode: 2
|
||||
m_LineEndingsForNewScripts: 2
|
||||
m_DefaultBehaviorMode: 1
|
||||
m_SpritePackerMode: 4
|
||||
m_SpritePackerPaddingPower: 1
|
||||
m_EtcTextureCompressorBehavior: 1
|
||||
m_EtcTextureFastCompressor: 1
|
||||
m_EtcTextureNormalCompressor: 2
|
||||
m_EtcTextureBestCompressor: 4
|
||||
m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd
|
||||
m_ProjectGenerationRootNamespace:
|
||||
m_UserGeneratedProjectSuffix:
|
||||
m_CollabEditorSettings:
|
||||
inProgressEnabled: 1
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!159 &1
|
||||
EditorSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 7
|
||||
m_ExternalVersionControlSupport: Visible Meta Files
|
||||
m_SerializationMode: 2
|
||||
m_LineEndingsForNewScripts: 2
|
||||
m_DefaultBehaviorMode: 1
|
||||
m_PrefabRegularEnvironment: {fileID: 0}
|
||||
m_PrefabUIEnvironment: {fileID: 0}
|
||||
m_SpritePackerMode: 4
|
||||
m_SpritePackerPaddingPower: 1
|
||||
m_EtcTextureCompressorBehavior: 1
|
||||
m_EtcTextureFastCompressor: 1
|
||||
m_EtcTextureNormalCompressor: 2
|
||||
m_EtcTextureBestCompressor: 4
|
||||
m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef
|
||||
m_ProjectGenerationRootNamespace:
|
||||
m_CollabEditorSettings:
|
||||
inProgressEnabled: 1
|
||||
m_EnableTextureStreamingInPlayMode: 1
|
||||
|
@ -1 +1 @@
|
||||
m_EditorVersion: 2018.2.14f1
|
||||
m_EditorVersion: 2018.3.3f1
|
||||
|
Loading…
Reference in New Issue
Block a user