Skip to content

Commit

Permalink
fix: CVRAvatar prevents removal of Animator (#260)
Browse files Browse the repository at this point in the history
* fix: CVRAvatar prevents removal of Animator
The error "Can't remove Animator because CVRAvatar (Script) depends on it" occurs in ChilloutVR avatars during the recreation of the Animator component.
This is because CVRAvatar has a RequireComponent annotation for Animator.
This commit fixes this issue by removing CVRAvatar first, and recreates it in the same way.

Tested on ChilloutVR CCK v3.9 RELEASE

* review pass: introspect RequireComponent attribute instead

* chore: update CHANGELOG.md

---------

Co-authored-by: Haï~ <hai-vr@users.noreply.github.com>
Co-authored-by: bd_ <bd_@nadena.dev>
  • Loading branch information
3 people authored Jun 13, 2024
1 parent 623f928 commit 9ea8e65
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- [#257] Proxy renderers no longer appear in the hierarchy.
- [#260] [ChilloutVR] Fix: Build fails due to CVRAvatar preventing recreation of Animator (contributed by @hai-vr)

### Changed

Expand Down
40 changes: 39 additions & 1 deletion Editor/ApplyOnPlay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#region

using System;
using System.Collections.Generic;
using System.Linq;
using nadena.dev.ndmf.config;
using nadena.dev.ndmf.runtime;
Expand Down Expand Up @@ -135,19 +136,56 @@ private static void RecreateAnimators(Transform avatar)
var tmpAnimator = tmpObject.AddComponent<Animator>();
bool enabled = animator.enabled;

// Support components that need to be destroyed before the Animator is destroyed,
// such as ChilloutVR's CVRAvatar component.
var tmpComponentsRequiringAnimator = new List<Component>();
foreach (var componentRequiringAnimator in FindSiblingComponentsRequiringAnimator(animator))
{
var tmpComponentRequiringAnimator = tmpObject.AddComponent(componentRequiringAnimator.GetType());
tmpComponentsRequiringAnimator.Add(tmpComponentRequiringAnimator);
EditorUtility.CopySerialized(componentRequiringAnimator, tmpComponentRequiringAnimator);
// Destroy this first before destroying the Animator below.
UnityObject.DestroyImmediate(componentRequiringAnimator);
}

EditorUtility.CopySerialized(animator, tmpAnimator);
UnityObject.DestroyImmediate(animator);
var newAnimator = obj.AddComponent<Animator>();
newAnimator.enabled = false;
EditorUtility.CopySerialized(tmpAnimator, newAnimator);
newAnimator.enabled = enabled;

foreach (var tmpComponentRequiringAnimator in tmpComponentsRequiringAnimator)
{
var newComponent = obj.AddComponent(tmpComponentRequiringAnimator.GetType());
EditorUtility.CopySerialized(tmpComponentRequiringAnimator, newComponent);
// Even in the temporary object, destroy this first before destroying the Animator below.
UnityObject.DestroyImmediate(tmpComponentRequiringAnimator);
}

UnityObject.DestroyImmediate(tmpAnimator);
}

UnityObject.DestroyImmediate(tmpObject);
}


private static IEnumerable<Component> FindSiblingComponentsRequiringAnimator(Animator animator)
{
return animator.GetComponents<Component>()
// GetComponents may return null elements on unloaded MonoBehaviour scripts
.Where(component => component != null)
.Where(component =>
{
var requiresAnimator = component.GetType()
.GetCustomAttributes(typeof(RequireComponent), true)
.Cast<RequireComponent>()
.Any(requireComponent => requireComponent.m_Type0 == typeof(Animator)
|| requireComponent.m_Type1 == typeof(Animator)
|| requireComponent.m_Type2 == typeof(Animator));
return requiresAnimator;
});
}

private static void OnPlayModeStateChanged(PlayModeStateChange obj)
{
if (obj == PlayModeStateChange.EnteredPlayMode)
Expand Down

0 comments on commit 9ea8e65

Please sign in to comment.