Replace the asset path attribute with a stable GUID identity
CultInspectorAssetPathAttribute stored an engine asset path, which breaks when the asset moves. CultInspectorAssetGuidAttribute stores the asset's stable identity (in Unity, the .meta GUID) instead. The Studio drawer now resolves the stored GUID to a path only to load the preview object, and writes back AssetPathToGUID on a change. No path form survives for the next field to pick up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
244154e2f5
commit
95a3c32250
5 changed files with 34 additions and 14 deletions
|
|
@ -64,12 +64,13 @@ namespace GameCult.Caching
|
|||
public float Max { get; }
|
||||
}
|
||||
|
||||
// A string member holding an engine asset path. AssetType is the engine's asset type, named by the consumer
|
||||
// (typeof(UnityEngine.Texture2D) in a Unity assembly); a lowering that does not know the type ignores it.
|
||||
// A string member holding an engine asset's stable identity; in Unity, the `.meta` GUID. AssetType is the
|
||||
// engine's asset type, named by the consumer (typeof(UnityEngine.Texture2D) in a Unity assembly); a lowering
|
||||
// that does not know the type ignores it.
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true)]
|
||||
public sealed class CultInspectorAssetPathAttribute : Attribute
|
||||
public sealed class CultInspectorAssetGuidAttribute : Attribute
|
||||
{
|
||||
public CultInspectorAssetPathAttribute(Type? assetType = null)
|
||||
public CultInspectorAssetGuidAttribute(Type? assetType = null)
|
||||
{
|
||||
AssetType = assetType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace GameCult.Caching
|
|||
Order = Find<CultInspectorOrderAttribute>()?.Order;
|
||||
Range = Find<CultInspectorRangeAttribute>();
|
||||
TextArea = Find<CultInspectorTextAreaAttribute>();
|
||||
AssetPath = Find<CultInspectorAssetPathAttribute>();
|
||||
AssetGuid = Find<CultInspectorAssetGuidAttribute>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<Attribute> Attributes { get; }
|
||||
|
|
@ -53,7 +53,7 @@ namespace GameCult.Caching
|
|||
public int? Order { get; }
|
||||
public CultInspectorRangeAttribute? Range { get; }
|
||||
public CultInspectorTextAreaAttribute? TextArea { get; }
|
||||
public CultInspectorAssetPathAttribute? AssetPath { get; }
|
||||
public CultInspectorAssetGuidAttribute? AssetGuid { get; }
|
||||
|
||||
public T? Find<T>() where T : Attribute => Attributes.OfType<T>().FirstOrDefault();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,12 +167,13 @@ namespace GameCult.Unity.Caching.Editor
|
|||
|
||||
private static string DrawString(string label, string value, CultInspectorMetadata metadata)
|
||||
{
|
||||
if (metadata.AssetPath != null)
|
||||
if (metadata.AssetGuid != null)
|
||||
{
|
||||
var assetType = metadata.AssetPath.AssetType != null && typeof(Object).IsAssignableFrom(metadata.AssetPath.AssetType) ? metadata.AssetPath.AssetType : typeof(Object);
|
||||
var asset = string.IsNullOrEmpty(value) ? null : AssetDatabase.LoadAssetAtPath(value, assetType);
|
||||
var assetType = metadata.AssetGuid.AssetType != null && typeof(Object).IsAssignableFrom(metadata.AssetGuid.AssetType) ? metadata.AssetGuid.AssetType : typeof(Object);
|
||||
var path = string.IsNullOrEmpty(value) ? string.Empty : AssetDatabase.GUIDToAssetPath(value);
|
||||
var asset = string.IsNullOrEmpty(path) ? null : AssetDatabase.LoadAssetAtPath(path, assetType);
|
||||
var next = EditorGUILayout.ObjectField(label, asset, assetType, false);
|
||||
return next == asset ? value : next == null ? string.Empty : AssetDatabase.GetAssetPath(next);
|
||||
return next == asset ? value : next == null ? string.Empty : AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(next));
|
||||
}
|
||||
|
||||
var textArea = metadata.TextArea;
|
||||
|
|
|
|||
|
|
@ -150,8 +150,8 @@ public sealed class ItemData
|
|||
public string Notes = string.Empty;
|
||||
|
||||
[Key(3)]
|
||||
[CultInspectorAssetPath]
|
||||
public string IconPath = string.Empty;
|
||||
[CultInspectorAssetGuid]
|
||||
public string IconGuid = string.Empty;
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -161,8 +161,9 @@ public sealed class ItemData
|
|||
- `CultInspectorOrder`
|
||||
- `CultInspectorTextArea`
|
||||
- `CultInspectorRange`
|
||||
- `CultInspectorAssetPath` (optional engine asset `Type`, named by the
|
||||
consumer; the Studio uses it when it is a `UnityEngine.Object` type)
|
||||
- `CultInspectorAssetGuid` (a string member holding an engine asset's stable
|
||||
identity, in Unity the `.meta` GUID; optional engine asset `Type`, named by
|
||||
the consumer; the Studio uses it when it is a `UnityEngine.Object` type)
|
||||
- `CultInspectorDrawer` (on drawer classes)
|
||||
|
||||
CultMesh collaboration should feed the same CultCache mutation surface rather
|
||||
|
|
|
|||
|
|
@ -42,6 +42,15 @@ namespace GameCult.Caching.Tests
|
|||
Assert.That(hidden.ValueType, Is.EqualTo(typeof(string)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AssetGuidMemberExposesItsAttributeAndDeclaredType()
|
||||
{
|
||||
var member = Model().MembersOf(typeof(InspectAsset)).Single();
|
||||
|
||||
Assert.That(member.Metadata.AssetGuid, Is.Not.Null);
|
||||
Assert.That(member.Metadata.AssetGuid!.AssetType, Is.EqualTo(typeof(Uri)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClaimsResolveAttributeThenTypeThenBuiltIn()
|
||||
{
|
||||
|
|
@ -321,6 +330,14 @@ namespace GameCult.Caching.Tests
|
|||
public string Name = string.Empty;
|
||||
}
|
||||
|
||||
[CultDocument("tests.inspect_asset", "tests.inspect_asset.v1")]
|
||||
[MessagePackObject]
|
||||
public sealed class InspectAsset
|
||||
{
|
||||
[Key(0)] [CultInspectorAssetGuid(typeof(Uri))]
|
||||
public string Guid = string.Empty;
|
||||
}
|
||||
|
||||
[CultDocument("tests.inspect_fixed", "tests.inspect_fixed.v1")]
|
||||
[MessagePackObject]
|
||||
public sealed class InspectFixed
|
||||
|
|
|
|||
Loading…
Reference in a new issue