// Collect the data required to set the link back up at runtime static void CollectResolveData(CrossSceneReferenceResolver resolver, UnityEngine.Object target, Type targetType, MemberInfo member, string GUID) { if (resolver != null) { CrossSceneReferenceSetupData data = new CrossSceneReferenceSetupData() { ClassHash = CodeGenerator.ClassHashFromType(targetType), RouteHash = member.Name.GetHashCode(), Target = target, GUID = GUID }; resolver.AddResolverData(data); } }
// Find or create a resolver in the target GameObject static CrossSceneReferenceResolver GetOrCreateResolver(UnityEngine.Object subject) { Behaviour behaviour = subject as Behaviour; if (!behaviour.TryGetComponent(typeof(CrossSceneReferenceResolver), out Component resolverRef)) { resolverRef = behaviour.gameObject.AddComponent <CrossSceneReferenceResolver>(); } CrossSceneReferenceResolver resolver = resolverRef as CrossSceneReferenceResolver; resolver.Prune(); return(resolver); }
private static void CollectClass(List <LinkResolver> temporaryResolvers, CodegenClass refClass) { // Search all open scenes for GameObject components matching 'refClass' foreach (MonoBehaviour refUsage in UnityEngine.Object.FindObjectsOfType(refClass.ClassType, true)) { CrossSceneReferenceResolver resolver = GetOrCreateResolver(refUsage); foreach (CodegenClassMember member in refClass.Members) { // Get the field's current value UnityEngine.Object memberValue = member.InfoWrapper.GetValue(refUsage) as UnityEngine.Object; if (memberValue == null) { CreateNullResolver(resolver, refUsage, member.InfoWrapper); } else { GameObject fieldValueGameObject = GetGameObject(memberValue, out SupportedObjectType type); // We don't need to store anything for references within the same scene if (fieldValueGameObject.scene == refUsage.gameObject.scene) { // Null out any existing resolver data pointing to this field CreateNullResolver(resolver, refUsage, member.InfoWrapper); continue; } // Set up a CrossSceneReferenceLocator and get the GUID which has been assigned to that object string referencedGUID = GetCrossSceneReferenceGUID(memberValue); // Store the data required so that the link can be restored at runtime CollectResolveData(resolver, refUsage, refUsage.GetType(), member.InfoWrapper.MemberInfo, referencedGUID); // More immediate way to restore the links once save is complete temporaryResolvers.Add(new TemporaryLinkResolver() { Target = refUsage, Route = member.InfoWrapper, Value = memberValue }); // Set to null during Scene save, otherwise Unity will complain about cross scene refs member.InfoWrapper.SetValue(refUsage, null); } } } }
static void CreateNullResolver(CrossSceneReferenceResolver resolver, Behaviour refUsage, FieldOrPropertyInfo fieldInfoWrapper) { CollectResolveData(resolver, refUsage, refUsage.GetType(), fieldInfoWrapper?.MemberInfo, null); }
private static void CollectProxy(List <LinkResolver> temporaryResolvers, Type proxyType) //AnimTrackProxy { GameObject GetPassthroughGameObject(UnityEngine.Object obj) { if (obj is Component asComponent) { return(asComponent.gameObject); } else if (obj is GameObject asGO) { return(asGO); } throw new NotSupportedException($"Unsupported passthrough type {obj.GetType()}."); } var proxy = Activator.CreateInstance(proxyType) as CrossSceneReferenceProxy; // Search all open scenes for GameObject components matching the proxy's relevant component type foreach (Component relevantComponent in UnityEngine.Object.FindObjectsOfType(proxy.RelevantComponentType, true)) { // Generate a context for the proxy to effectively operate on targets & passthroughs. UnityEngine.Object context = proxy.AcquireContext(relevantComponent); // Iterate through all the proxy-type members contained within the relevant component instance. UnityEngine.Object[] proxyTargets = proxy.GetTargets(context); // However, if there are no valid targets, the context is useless. // No further logic is necessary for this iteration. Let's interrupt & dispose. if (proxyTargets == null || proxyTargets.Length == 0) { proxy.ReleaseContext(context); continue; } for (int i = 0; i < proxyTargets.Length; ++i) { var target = proxyTargets[i] as object; // Obtain the component at the other end of the proxy. UnityEngine.Object passthrough = proxy.GetPassthrough(ref target, context); if (passthrough == null) { continue; // No proxying to be done. Skip iteration. } // By now, we guarantee a GameObject component with a valid proxy endpoint. // Create a resolver to connect both proxy endpoints. CrossSceneReferenceResolver resolver = GetOrCreateResolver(relevantComponent); // Set up a CrossSceneReferenceLocator and get the GUID which has been assigned to the passthrough component. CrossSceneReferenceLocator loc = GetOrCreateLocator(GetPassthroughGameObject(passthrough)); int locGuidIdx = loc.Passthroughs.FindIndex(x => x == passthrough); if (locGuidIdx == -1) { // Only generate new GUID for unique entries. loc.ComponentGUIDS.Add(GUID.Generate().ToString()); loc.Passthroughs.Add(passthrough); locGuidIdx = loc.ComponentGUIDS.Count - 1; } // Mark relevant component's scene for saving to serialize the resolver on the Unity scene. Scene scene = relevantComponent.gameObject.scene; EditorSceneManager.MarkSceneDirty(scene); // Store the data required so that the link can be restored at runtime CrossSceneReferenceSetupData data = new CrossSceneReferenceSetupData() { ClassHash = CodeGenerator.ClassHashFromType(proxyType), RouteHash = proxy.GenerateRouteHash(passthrough, context), Target = target as UnityEngine.Object, GUID = loc.ComponentGUIDS[locGuidIdx], Context = context }; resolver.AddResolverData(data); // More immediate way to restore the links once save is complete temporaryResolvers.Add(new TemporaryProxyLinkResolver() // REVIEW: Consider not overloading Temp Link Resolver. Make specific classes for use case. { ProxyType = proxyType, Target = target, Value = passthrough, Context = context, Route = data.RouteHash }); // Set to null during Scene save, otherwise Unity will complain about cross scene refs proxy.Set(data.RouteHash, target, null, context); } } }