mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-02-06 13:52:49 +08:00
GC unused game objects (#229)
This commit is contained in:
parent
99f8052dd9
commit
2a321612fc
@ -188,6 +188,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
context.AnimationDatabase.Commit();
|
context.AnimationDatabase.Commit();
|
||||||
|
|
||||||
|
new GCGameObjectsPass(context, avatarGameObject).OnPreprocessAvatar();
|
||||||
|
|
||||||
AfterProcessing?.Invoke(avatarGameObject);
|
AfterProcessing?.Invoke(avatarGameObject);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2f3bf44d18c6489ab4425ec3cdee360d
|
||||||
|
timeCreated: 1677488644
|
||||||
|
folderAsset: yes
|
@ -0,0 +1,170 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using VRC.SDK3.Dynamics.PhysBone.Components;
|
||||||
|
|
||||||
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Remove all GameObjects which have no influence on the avatar.
|
||||||
|
/// </summary>
|
||||||
|
internal class GCGameObjectsPass
|
||||||
|
{
|
||||||
|
private readonly BuildContext _context;
|
||||||
|
private readonly GameObject _root;
|
||||||
|
private readonly HashSet<GameObject> referencedGameObjects = new HashSet<GameObject>();
|
||||||
|
|
||||||
|
internal GCGameObjectsPass(BuildContext context, GameObject root)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OnPreprocessAvatar()
|
||||||
|
{
|
||||||
|
MarkAll();
|
||||||
|
Sweep();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MarkAll()
|
||||||
|
{
|
||||||
|
foreach (var obj in GameObjects(_root,
|
||||||
|
node =>
|
||||||
|
{
|
||||||
|
if (node.CompareTag("EditorOnly"))
|
||||||
|
{
|
||||||
|
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
||||||
|
{
|
||||||
|
// Retain EditorOnly objects (in case they contain camera fixtures or something),
|
||||||
|
// but ignore references _from_ them. (TODO: should we mark from them as well?)
|
||||||
|
MarkObject(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
))
|
||||||
|
{
|
||||||
|
foreach (var component in obj.GetComponents<Component>())
|
||||||
|
{
|
||||||
|
switch (component)
|
||||||
|
{
|
||||||
|
case Transform t: break;
|
||||||
|
|
||||||
|
case VRCPhysBone pb:
|
||||||
|
MarkObject(obj);
|
||||||
|
MarkPhysBone(pb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AvatarTagComponent _:
|
||||||
|
// Tag components will not be retained at runtime, so pretend they're not there.
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
MarkObject(obj);
|
||||||
|
MarkAllReferencedObjects(component);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MarkPhysBone(VRCPhysBone pb)
|
||||||
|
{
|
||||||
|
var rootTransform = pb.GetRootTransform();
|
||||||
|
var ignoreTransforms = pb.ignoreTransforms ?? new List<Transform>();
|
||||||
|
|
||||||
|
foreach (var obj in GameObjects(rootTransform.gameObject,
|
||||||
|
obj => !obj.CompareTag("EditorOnly") && !ignoreTransforms.Contains(obj.transform)))
|
||||||
|
{
|
||||||
|
MarkObject(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark colliders, etc
|
||||||
|
MarkAllReferencedObjects(pb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MarkAllReferencedObjects(Component component)
|
||||||
|
{
|
||||||
|
var so = new SerializedObject(component);
|
||||||
|
var sp = so.GetIterator();
|
||||||
|
|
||||||
|
bool enterChildren = true;
|
||||||
|
while (sp.Next(enterChildren))
|
||||||
|
{
|
||||||
|
enterChildren = true;
|
||||||
|
|
||||||
|
switch (sp.propertyType)
|
||||||
|
{
|
||||||
|
case SerializedPropertyType.String:
|
||||||
|
enterChildren = false;
|
||||||
|
continue;
|
||||||
|
case SerializedPropertyType.ObjectReference:
|
||||||
|
if (sp.objectReferenceValue != null)
|
||||||
|
{
|
||||||
|
if (sp.objectReferenceValue is GameObject refObj)
|
||||||
|
{
|
||||||
|
MarkObject(refObj);
|
||||||
|
}
|
||||||
|
else if (sp.objectReferenceValue is Component comp)
|
||||||
|
{
|
||||||
|
MarkObject(comp.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MarkObject(GameObject go)
|
||||||
|
{
|
||||||
|
while (go != null && referencedGameObjects.Add(go) && go != _root)
|
||||||
|
{
|
||||||
|
go = go.transform.parent?.gameObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Sweep()
|
||||||
|
{
|
||||||
|
foreach (var go in GameObjects())
|
||||||
|
{
|
||||||
|
if (!referencedGameObjects.Contains(go))
|
||||||
|
{
|
||||||
|
Debug.Log("Purging object: " + RuntimeUtil.AvatarRootPath(go));
|
||||||
|
UnityEngine.Object.DestroyImmediate(go);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<GameObject> GameObjects(GameObject node = null,
|
||||||
|
Func<GameObject, bool> shouldTraverse = null)
|
||||||
|
{
|
||||||
|
if (node == null) node = _root;
|
||||||
|
if (shouldTraverse == null) shouldTraverse = obj => !obj.CompareTag("EditorOnly");
|
||||||
|
|
||||||
|
if (!shouldTraverse(node)) yield break;
|
||||||
|
|
||||||
|
yield return node;
|
||||||
|
if (node == null) yield break;
|
||||||
|
|
||||||
|
// Guard against object deletion mid-traversal
|
||||||
|
List<Transform> children = new List<Transform>();
|
||||||
|
foreach (Transform t in node.transform)
|
||||||
|
{
|
||||||
|
children.Add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in children)
|
||||||
|
{
|
||||||
|
foreach (var grandchild in GameObjects(child.gameObject, shouldTraverse))
|
||||||
|
{
|
||||||
|
yield return grandchild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3c155e44d9874f2ba44e67f129112d63
|
||||||
|
timeCreated: 1677488653
|
Loading…
Reference in New Issue
Block a user