tile generation
This commit is contained in:
@@ -26,7 +26,6 @@ namespace Escape_Room_Engine.Engine.Scripts
|
||||
private static Engine _foundEngine;
|
||||
|
||||
[Required] public EngineConfiguration config;
|
||||
public Material roomMaterial;
|
||||
|
||||
private int NumberOfRooms => _rooms.Count;
|
||||
|
||||
|
||||
@@ -8,28 +8,43 @@ namespace Escape_Room_Engine.Engine.Scripts
|
||||
[CreateAssetMenu(menuName = "Engine Config")]
|
||||
public class EngineConfiguration : ScriptableObject
|
||||
{
|
||||
[BoxGroup("Size")]
|
||||
[Tooltip("The minimum size that should be allowed for rooms.")]
|
||||
#region Size
|
||||
|
||||
[BoxGroup("Size")] [Tooltip("The minimum size that should be allowed for rooms.")]
|
||||
public Vector2Int minRoomSize;
|
||||
|
||||
[BoxGroup("Size")]
|
||||
[Tooltip("The size of the physical play space available to the engine.")]
|
||||
[BoxGroup("Size")] [Tooltip("The size of the physical play space available to the engine.")]
|
||||
public Vector2Int playSpace;
|
||||
|
||||
#endregion
|
||||
|
||||
[BoxGroup("Doors")]
|
||||
[Required]
|
||||
#region Spaces
|
||||
|
||||
[BoxGroup("Spaces")] [Required]
|
||||
public SpaceTile spaceTile;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Doors
|
||||
|
||||
[BoxGroup("Doors")] [Required]
|
||||
public DoorModuleDescription spawnDoor;
|
||||
|
||||
[BoxGroup("Doors")]
|
||||
[ValidateInput("IsNotEmpty", "At least one exit door type is required")]
|
||||
[BoxGroup("Doors")] [ValidateInput("IsNotEmpty", "At least one exit door type is required")]
|
||||
public List<DoorModuleDescription> exitDoorTypes;
|
||||
|
||||
[BoxGroup("Puzzles")]
|
||||
[MinMaxSlider(0, 10)] public Vector2Int puzzleCount;
|
||||
#endregion
|
||||
|
||||
#region Puzzles
|
||||
|
||||
[BoxGroup("Puzzles")] [MinMaxSlider(0, 10)]
|
||||
public Vector2Int puzzleCount;
|
||||
|
||||
[BoxGroup("Puzzles")]
|
||||
public List<PuzzleModuleDescription> puzzleTypes;
|
||||
|
||||
#endregion
|
||||
|
||||
public ModuleDescription genericModule;
|
||||
}
|
||||
}
|
||||
@@ -52,16 +52,34 @@ namespace Escape_Room_Engine.Engine.Scripts
|
||||
|
||||
internal void InstantiateSpace(Transform parent, string name)
|
||||
{
|
||||
_spaceObject = new GameObject($"Space {name}", typeof(MeshFilter), typeof(MeshRenderer));
|
||||
_spaceObject = new GameObject($"Space {name}");
|
||||
_spaceObject.transform.SetParent(parent, false);
|
||||
_spaceObject.transform.localPosition = new Vector3(rrDimensions.x, 0, rrDimensions.z);
|
||||
|
||||
var meshFilter = _spaceObject.GetComponent<MeshFilter>();
|
||||
meshFilter.mesh = GenerateMesh();
|
||||
|
||||
var meshRenderer = _spaceObject.GetComponent<MeshRenderer>();
|
||||
meshRenderer.material = Engine.DefaultEngine.roomMaterial;
|
||||
|
||||
// build the space floor out of tiles
|
||||
for (var z = 0; z < rrDimensions.length; z++)
|
||||
{
|
||||
for (var x = 0; x < rrDimensions.width; x++)
|
||||
{
|
||||
var left = x == 0;
|
||||
var right = x == rrDimensions.width - 1;
|
||||
var bottom = z == 0;
|
||||
var top = z == rrDimensions.length - 1;
|
||||
|
||||
TileLocation location;
|
||||
if (bottom)
|
||||
location = left ? TileLocation.SW : right ? TileLocation.SE : TileLocation.S;
|
||||
else if (top)
|
||||
location = left ? TileLocation.NW : right ? TileLocation.NE : TileLocation.N;
|
||||
else
|
||||
location = left ? TileLocation.W : right ? TileLocation.E : TileLocation.C;
|
||||
|
||||
var tileObject = Object.Instantiate(Engine.DefaultEngine.config.spaceTile, _spaceObject.transform, false);
|
||||
tileObject.transform.localPosition = new Vector3(x, 0, z);
|
||||
tileObject.showTile = location;
|
||||
}
|
||||
}
|
||||
|
||||
// instantiate all modules inside this space
|
||||
Modules.ForEach(module => module.InstantiateModule(_spaceObject.transform));
|
||||
}
|
||||
@@ -77,41 +95,6 @@ namespace Escape_Room_Engine.Engine.Scripts
|
||||
/// <param name="rrPosition">The room relative (<i>RR</i>) position that should be converted to a space relative (<i>SR</i>) position.</param>
|
||||
internal Vector2Int ToSpaceRelative(Vector2Int rrPosition) => rrPosition - rrDimensions.Position;
|
||||
|
||||
private Mesh GenerateMesh()
|
||||
{
|
||||
var mesh = new Mesh();
|
||||
|
||||
mesh.vertices = new[]
|
||||
{
|
||||
new Vector3(0, 0, 0),
|
||||
new Vector3(rrDimensions.width, 0, 0),
|
||||
new Vector3(0, 0, rrDimensions.length),
|
||||
new Vector3(rrDimensions.width, 0, rrDimensions.length)
|
||||
};
|
||||
|
||||
mesh.triangles = new[]
|
||||
{
|
||||
0, 2, 1,
|
||||
2, 3, 1
|
||||
};
|
||||
|
||||
var normal = Vector3.up;
|
||||
mesh.normals = new[]
|
||||
{
|
||||
normal, normal, normal, normal
|
||||
};
|
||||
|
||||
mesh.uv = new[]
|
||||
{
|
||||
new Vector2(0, 0),
|
||||
new Vector2(1, 0),
|
||||
new Vector2(0, 1),
|
||||
new Vector2(1, 1)
|
||||
};
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
internal void Destroy()
|
||||
{
|
||||
Object.Destroy(_spaceObject);
|
||||
|
||||
62
Assets/Escape Room Engine/Engine/Scripts/SpaceTile.cs
Normal file
62
Assets/Escape Room Engine/Engine/Scripts/SpaceTile.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Escape_Room_Engine.Engine.Scripts.Utilities;
|
||||
using NaughtyAttributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Escape_Room_Engine.Engine.Scripts
|
||||
{
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public enum TileLocation
|
||||
{
|
||||
NW, N, NE,
|
||||
W, C, E,
|
||||
SW, S, SE
|
||||
}
|
||||
|
||||
public class SpaceTile : MonoBehaviour
|
||||
{
|
||||
public static HashSet<TileLocation> EveryTileLocation => new(new[]
|
||||
{
|
||||
TileLocation.NW, TileLocation.N, TileLocation.NE,
|
||||
TileLocation.W, TileLocation.C, TileLocation.E,
|
||||
TileLocation.SW, TileLocation.S, TileLocation.SE
|
||||
});
|
||||
|
||||
[BoxGroup("Style Prefabs")] [SerializeField]
|
||||
private UDictionary<TileLocation, GameObject> tilePrefabs;
|
||||
[BoxGroup("Style Prefabs")] [SerializeField]
|
||||
private Material material;
|
||||
|
||||
public TileLocation showTile;
|
||||
|
||||
private GameObject _tile;
|
||||
private TileLocation _showTile;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
SetTile();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_showTile != showTile)
|
||||
{
|
||||
SetTile();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTile()
|
||||
{
|
||||
if (_tile)
|
||||
{
|
||||
Destroy(_tile);
|
||||
}
|
||||
|
||||
_tile = Instantiate(tilePrefabs[showTile], transform);
|
||||
_tile.GetComponent<MeshRenderer>().material = material;
|
||||
|
||||
_showTile = showTile;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Escape Room Engine/Engine/Scripts/SpaceTile.cs.meta
Normal file
11
Assets/Escape Room Engine/Engine/Scripts/SpaceTile.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01a963b0c69d73e488ebc193ea70326b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,452 @@
|
||||
// https://gist.github.com/Moe-Baker/e36610361012d586b1393994febeb5d2
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
#endif
|
||||
|
||||
namespace Escape_Room_Engine.Engine.Scripts.Utilities
|
||||
{
|
||||
[Serializable]
|
||||
public class UDictionary
|
||||
{
|
||||
public class SplitAttribute : PropertyAttribute
|
||||
{
|
||||
public float Key { get; protected set; }
|
||||
public float Value { get; protected set; }
|
||||
|
||||
public SplitAttribute(float key, float value)
|
||||
{
|
||||
this.Key = key;
|
||||
this.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomPropertyDrawer(typeof(SplitAttribute), true)]
|
||||
[CustomPropertyDrawer(typeof(UDictionary), true)]
|
||||
public class Drawer : PropertyDrawer
|
||||
{
|
||||
SerializedProperty property;
|
||||
|
||||
public bool IsExpanded
|
||||
{
|
||||
get => property.isExpanded;
|
||||
set => property.isExpanded = value;
|
||||
}
|
||||
|
||||
SerializedProperty keys;
|
||||
SerializedProperty values;
|
||||
|
||||
public bool IsAligned => keys.arraySize == values.arraySize;
|
||||
|
||||
ReorderableList list;
|
||||
|
||||
GUIContent label;
|
||||
|
||||
SplitAttribute split;
|
||||
|
||||
public float KeySplit => split == null ? 30f : split.Key;
|
||||
public float ValueSplit => split == null ? 70f : split.Value;
|
||||
|
||||
public static float SingleLineHeight => EditorGUIUtility.singleLineHeight;
|
||||
|
||||
public const float ElementHeightPadding = 6f;
|
||||
public const float ElementSpacing = 10f;
|
||||
public const float ElementFoldoutPadding = 20f;
|
||||
|
||||
public const float TopPadding = 5f;
|
||||
public const float BottomPadding = 5f;
|
||||
|
||||
void Init(SerializedProperty value)
|
||||
{
|
||||
if (SerializedProperty.EqualContents(value, property)) return;
|
||||
|
||||
property = value;
|
||||
|
||||
keys = property.FindPropertyRelative(nameof(keys));
|
||||
values = property.FindPropertyRelative(nameof(values));
|
||||
|
||||
split = attribute as SplitAttribute;
|
||||
|
||||
list = new ReorderableList(property.serializedObject, keys, true, true, true, true);
|
||||
|
||||
list.drawHeaderCallback = DrawHeader;
|
||||
|
||||
list.onAddCallback = Add;
|
||||
list.onRemoveCallback = Remove;
|
||||
|
||||
list.elementHeightCallback = GetElementHeight;
|
||||
|
||||
list.drawElementCallback = DrawElement;
|
||||
|
||||
list.onReorderCallbackWithDetails += Reorder;
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
Init(property);
|
||||
|
||||
var height = TopPadding + BottomPadding;
|
||||
|
||||
if (IsAligned)
|
||||
height += IsExpanded ? list.GetHeight() : list.headerHeight;
|
||||
else
|
||||
height += SingleLineHeight;
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
label.text = $" {label.text}";
|
||||
|
||||
this.label = label;
|
||||
|
||||
Init(property);
|
||||
|
||||
rect = EditorGUI.IndentedRect(rect);
|
||||
|
||||
rect.y += TopPadding;
|
||||
rect.height -= TopPadding + BottomPadding;
|
||||
|
||||
if (IsAligned == false)
|
||||
{
|
||||
DrawAlignmentWarning(ref rect);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsExpanded)
|
||||
DrawList(ref rect);
|
||||
else
|
||||
DrawCompleteHeader(ref rect);
|
||||
}
|
||||
|
||||
void DrawList(ref Rect rect)
|
||||
{
|
||||
EditorGUIUtility.labelWidth = 80f;
|
||||
EditorGUIUtility.fieldWidth = 80f;
|
||||
|
||||
list.DoList(rect);
|
||||
}
|
||||
|
||||
void DrawAlignmentWarning(ref Rect rect)
|
||||
{
|
||||
var width = 80f;
|
||||
var spacing = 5f;
|
||||
|
||||
rect.width -= width;
|
||||
|
||||
EditorGUI.HelpBox(rect, " Misalignment Detected", MessageType.Error);
|
||||
|
||||
rect.x += rect.width + spacing;
|
||||
rect.width = width - spacing;
|
||||
|
||||
if (GUI.Button(rect, "Fix"))
|
||||
{
|
||||
if (keys.arraySize > values.arraySize)
|
||||
{
|
||||
var difference = keys.arraySize - values.arraySize;
|
||||
|
||||
for (int i = 0; i < difference; i++)
|
||||
keys.DeleteArrayElementAtIndex(keys.arraySize - 1);
|
||||
}
|
||||
else if (keys.arraySize < values.arraySize)
|
||||
{
|
||||
var difference = values.arraySize - keys.arraySize;
|
||||
|
||||
for (int i = 0; i < difference; i++)
|
||||
values.DeleteArrayElementAtIndex(values.arraySize - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Draw Header
|
||||
|
||||
void DrawHeader(Rect rect)
|
||||
{
|
||||
rect.x += 10f;
|
||||
|
||||
IsExpanded = EditorGUI.Foldout(rect, IsExpanded, label, true);
|
||||
}
|
||||
|
||||
void DrawCompleteHeader(ref Rect rect)
|
||||
{
|
||||
ReorderableList.defaultBehaviours.DrawHeaderBackground(rect);
|
||||
|
||||
rect.x += 6;
|
||||
rect.y += 0;
|
||||
|
||||
DrawHeader(rect);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
float GetElementHeight(int index)
|
||||
{
|
||||
SerializedProperty key = keys.GetArrayElementAtIndex(index);
|
||||
SerializedProperty value = values.GetArrayElementAtIndex(index);
|
||||
|
||||
var kHeight = GetChildernSingleHeight(key);
|
||||
var vHeight = GetChildernSingleHeight(value);
|
||||
|
||||
var max = Math.Max(kHeight, vHeight);
|
||||
|
||||
if (max < SingleLineHeight) max = SingleLineHeight;
|
||||
|
||||
return max + ElementHeightPadding;
|
||||
}
|
||||
|
||||
#region Draw Element
|
||||
|
||||
void DrawElement(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
rect.height -= ElementHeightPadding;
|
||||
rect.y += ElementHeightPadding / 2;
|
||||
|
||||
var areas = Split(rect, KeySplit, ValueSplit);
|
||||
|
||||
DrawKey(areas[0], index);
|
||||
DrawValue(areas[1], index);
|
||||
}
|
||||
|
||||
void DrawKey(Rect rect, int index)
|
||||
{
|
||||
var property = keys.GetArrayElementAtIndex(index);
|
||||
|
||||
rect.x += ElementSpacing / 2f;
|
||||
rect.width -= ElementSpacing;
|
||||
|
||||
DrawField(rect, property);
|
||||
}
|
||||
|
||||
void DrawValue(Rect rect, int index)
|
||||
{
|
||||
var property = values.GetArrayElementAtIndex(index);
|
||||
|
||||
rect.x += ElementSpacing / 2f;
|
||||
rect.width -= ElementSpacing;
|
||||
|
||||
DrawField(rect, property);
|
||||
}
|
||||
|
||||
void DrawField(Rect rect, SerializedProperty property)
|
||||
{
|
||||
rect.height = SingleLineHeight;
|
||||
|
||||
if (IsInline(property))
|
||||
{
|
||||
EditorGUI.PropertyField(rect, property, GUIContent.none);
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.x += ElementSpacing / 2f;
|
||||
rect.width -= ElementSpacing;
|
||||
|
||||
foreach (var child in IterateChildern(property))
|
||||
{
|
||||
EditorGUI.PropertyField(rect, child, false);
|
||||
|
||||
rect.y += SingleLineHeight + +2f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
void Reorder(ReorderableList list, int oldIndex, int newIndex)
|
||||
{
|
||||
values.MoveArrayElement(oldIndex, newIndex);
|
||||
}
|
||||
|
||||
void Add(ReorderableList list)
|
||||
{
|
||||
values.InsertArrayElementAtIndex(values.arraySize);
|
||||
|
||||
ReorderableList.defaultBehaviours.DoAddButton(list);
|
||||
}
|
||||
|
||||
void Remove(ReorderableList list)
|
||||
{
|
||||
values.DeleteArrayElementAtIndex(list.index);
|
||||
|
||||
ReorderableList.defaultBehaviours.DoRemoveButton(list);
|
||||
}
|
||||
|
||||
//Static Utility
|
||||
static Rect[] Split(Rect source, params float[] cuts)
|
||||
{
|
||||
var rects = new Rect[cuts.Length];
|
||||
|
||||
var x = 0f;
|
||||
|
||||
for (int i = 0; i < cuts.Length; i++)
|
||||
{
|
||||
rects[i] = new Rect(source);
|
||||
|
||||
rects[i].x += x;
|
||||
rects[i].width *= cuts[i] / 100;
|
||||
|
||||
x += rects[i].width;
|
||||
}
|
||||
|
||||
return rects;
|
||||
}
|
||||
|
||||
static bool IsInline(SerializedProperty property)
|
||||
{
|
||||
switch (property.propertyType)
|
||||
{
|
||||
case SerializedPropertyType.Generic:
|
||||
return property.hasVisibleChildren == false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static IEnumerable<SerializedProperty> IterateChildern(SerializedProperty property)
|
||||
{
|
||||
var path = property.propertyPath;
|
||||
|
||||
property.Next(true);
|
||||
|
||||
while (true)
|
||||
{
|
||||
yield return property;
|
||||
|
||||
if (property.NextVisible(false) == false) break;
|
||||
if (property.propertyPath.StartsWith(path) == false) break;
|
||||
}
|
||||
}
|
||||
|
||||
float GetChildernSingleHeight(SerializedProperty property)
|
||||
{
|
||||
if (IsInline(property)) return SingleLineHeight;
|
||||
|
||||
var height = 0f;
|
||||
|
||||
foreach (var child in IterateChildern(property))
|
||||
height += SingleLineHeight + 2f;
|
||||
|
||||
return height;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UDictionary<TKey, TValue> : UDictionary, IDictionary<TKey, TValue>
|
||||
{
|
||||
[SerializeField] List<TKey> keys;
|
||||
public List<TKey> Keys => keys;
|
||||
ICollection<TKey> IDictionary<TKey, TValue>.Keys => keys;
|
||||
|
||||
[SerializeField] List<TValue> values;
|
||||
public List<TValue> Values => values;
|
||||
ICollection<TValue> IDictionary<TKey, TValue>.Values => values;
|
||||
|
||||
public int Count => keys.Count;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
Dictionary<TKey, TValue> cache;
|
||||
|
||||
public bool Cached => cache != null;
|
||||
|
||||
public Dictionary<TKey, TValue> Dictionary
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cache == null)
|
||||
{
|
||||
cache = new Dictionary<TKey, TValue>();
|
||||
|
||||
for (int i = 0; i < keys.Count; i++)
|
||||
{
|
||||
if (keys[i] == null) continue;
|
||||
if (cache.ContainsKey(keys[i])) continue;
|
||||
|
||||
cache.Add(keys[i], values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get => Dictionary[key];
|
||||
set
|
||||
{
|
||||
var index = keys.IndexOf(key);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
Add(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
values[index] = value;
|
||||
if (Cached) Dictionary[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value) => Dictionary.TryGetValue(key, out value);
|
||||
|
||||
public bool ContainsKey(TKey key) => Dictionary.ContainsKey(key);
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item) => ContainsKey(item.Key);
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
keys.Add(key);
|
||||
values.Add(value);
|
||||
|
||||
if (Cached) Dictionary.Add(key, value);
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
var index = keys.IndexOf(key);
|
||||
|
||||
if (index < 0) return false;
|
||||
|
||||
keys.RemoveAt(index);
|
||||
values.RemoveAt(index);
|
||||
|
||||
if (Cached) Dictionary.Remove(key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item) => Remove(item.Key);
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
keys.Clear();
|
||||
values.Clear();
|
||||
|
||||
if (Cached) Dictionary.Clear();
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) =>
|
||||
(Dictionary as IDictionary).CopyTo(array, arrayIndex);
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => Dictionary.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => Dictionary.GetEnumerator();
|
||||
|
||||
public UDictionary()
|
||||
{
|
||||
values = new List<TValue>();
|
||||
keys = new List<TKey>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 238265e493429c44ebcd511153a19322
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user