/** * Copyright(c) Live2D Inc. All rights reserved. * * Use of this source code is governed by the Live2D Open Software license * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ using Live2D.Cubism.Core; using UnityEngine; using Object = UnityEngine.Object; namespace Live2D.Cubism.Framework.LookAt { /// /// Controls s. /// public sealed class CubismLookController : MonoBehaviour, ICubismUpdatable { /// /// Blend mode. /// [SerializeField] public CubismParameterBlendMode BlendMode = CubismParameterBlendMode.Additive; /// /// backing field. /// [SerializeField, HideInInspector] private Object _target; /// /// Target. /// public Object Target { get { return _target; } set { _target = value.ToNullUnlessImplementsInterface(); } } /// /// backing field. /// private ICubismLookTarget _targetInterface; /// /// Interface of target. /// private ICubismLookTarget TargetInterface { get { if (_targetInterface == null) { _targetInterface = Target.GetInterface(); } return _targetInterface; } } /// /// Local center position. /// public Transform Center; /// /// Damping to apply. /// public float Damping = 0.15f; /// /// Source parameters. /// private CubismLookParameter[] Sources { get; set; } /// /// The actual parameters to apply the source values to. /// private CubismParameter[] Destinations { get; set; } /// /// Position at last frame. /// private Vector3 LastPosition { get; set; } /// /// Goal position. /// private Vector3 GoalPosition { get; set; } /// /// Buffer for velocity. /// // ReSharper disable once InconsistentNaming private Vector3 VelocityBuffer; /// /// Model has update controller component. /// [HideInInspector] public bool HasUpdateController { get; set; } /// /// Refreshes the controller. Call this method after adding and/or removing s. /// public void Refresh() { var model = this.FindCubismModel(); // Catch sources and destinations. Sources = model .Parameters .GetComponentsMany(); Destinations = new CubismParameter[Sources.Length]; for (var i = 0; i < Sources.Length; ++i) { Destinations[i] = Sources[i].GetComponent(); } // Get cubism update controller. HasUpdateController = (GetComponent() != null); } /// /// Called by cubism update controller. Order to invoke OnLateUpdate. /// public int ExecutionOrder { get { return CubismUpdateExecutionOrder.CubismLookController; } } /// /// Called by cubism update controller. Needs to invoke OnLateUpdate on Editing. /// public bool NeedsUpdateOnEditing { get { return false; } } /// /// Called by cubism update controller. Updates controller. /// public void OnLateUpdate() { // Return if it is not valid or there's nothing to update. if (!enabled || Destinations == null) { return; } // Return early if no target is available or if target is inactive. var target = TargetInterface; if (target == null || !target.IsActive()) { return; } // Update position. var position = LastPosition; GoalPosition = transform.InverseTransformPoint(target.GetPosition()) - Center.localPosition; if (position != GoalPosition) { position = Vector3.SmoothDamp( position, GoalPosition, ref VelocityBuffer, Damping); } // Update sources and destinations. for (var i = 0; i < Destinations.Length; ++i) { Destinations[i].BlendToValue(BlendMode, Sources[i].TickAndEvaluate(position)); } // Store position. LastPosition = position; } #region Unity Events Handling /// /// Called by Unity. Makes sure cache is initialized. /// private void Start() { // Default center if necessary. if (Center == null) { Center = transform; } // Initialize cache. Refresh(); } /// /// Called by Unity. Updates controller. /// private void LateUpdate() { if (!HasUpdateController) { OnLateUpdate(); } } #endregion } }