HarmonicMotion (Cocos Creator)

Updated: 03/14/2023

This page is for Cubism version 4.2 or earlier. Click here for the latest version.

Summary

HarmonicMotion is a function that periodically iterates the values of specified parameters.
It is used primarily for things that are in constant motion, such as breathing.
Click here for more information on how to set up HarmonicMotion.

HarmonicMotion in Cubism SDK for Cocos Creator consists of two elements.

  1. Components for specifying parameters to operate
  2. Components that manipulate the value of each parameter

1. Components for Specifying Parameters to Operate

Use CubismHarmonicMotionParameter to specify the parameters to be used for HarmonicMotion.

CubismHarmonicMotionParameter is a component that inherits from Component and attaches to the Node placed under [Prefab root]/Parameters/.

It will cycle through the values of the parameters with the same ID as the Node to which it is attached.

CustomHarmonicMotionParameter has five setting items.

  /** Timescale channel. */
  @property({ type: CCInteger, serializable: true, visible: true })
  public channel: number = 0;

  /** Motion direction. */
  @property({ type: Enum(CubismHarmonicMotionDirection), serializable: true, visible: true })
  public direction: CubismHarmonicMotionDirection = CubismHarmonicMotionDirection.Left;

  /**
   * Normalized origin of motion.
   * The actual origin used for evaluating the motion depends limits of the {@link CubismParameter}.
   */
  @property({ type: CCFloat, slide: true, range: [0.0, 1.0, 0.01], serializable: true, visible: true, })
  public normalizedOrigin: number = 0.5;

  /**
   * Normalized range of motion.
   * The actual origin used for evaluating the motion depends limits of the {@link CubismParameter}.
   */
  @property({ type: CCFloat, slide: true, range: [0.0, 1.0, 0.01], serializable: true, visible: true, })
  public normalizedRange: number = 0.5;

  /** Duration of one motion cycle in seconds. */
  @property({ type: CCFloat, slide: true, range: [0.01, 10.0, 0.01], serializable: true, visible: true, })
  public duration: number = 3.0;
  • channel

    Specifies the multiplier for the sine wave period set by the CubismHarmonicMotionController.

    HarmonicMotion allows multiple periods for a single model, which can be set with the CustomHarmonicMotionController.

    Here, set the index of the CubismHarmonicMotionController.channelTimescales.
  • direction

    Sets the range of periodic operation with respect to the center of the parameter.

    There are three setting items.
  • Left: It works only in the left half of the range from the center of the parameter.
  • Right: It works only in the right half of the range from the center of the parameter.
  • Centric: It works across the entire range of parameters.
  • normalizedOrigin

    Sets the center of the parameter to be referenced by direction.

    The center will be set to the value when the minimum value of that parameter is set to 0 and the maximum value of that parameter is set to 1.
  • normalizedRange

    Sets the amplitude at which to operate the value from the center of the value set in normalizedOrigin.

    Set the distance traveled from the center when the minimum value of that parameter is 0 and the maximum value is 1.

    This value can only range from the center position set by normalizedOrigin to the minimum or maximum value of the parameter.
  • duration

    Adjusts the duration of the parameter.
  /** Evaluates the parameter. */
  public evaluate(): number {
    // Lazily initialize.
    if (!this.isInitialized) {
      this.initialize();
    }

    // Restore origin and range.
    let origin = this.minimumValue + this.normalizedOrigin * this.valueRange;
    let range = this.normalizedRange * this.valueRange;

    // Clamp the range so that it stays within the limits.
    const outputArray = this.clamp(origin, range);

    const originIndex = 0;
    const rangeIndex = 1;
    origin = outputArray[originIndex];
    range = outputArray[rangeIndex];

    // Return result.
    return origin + range * Math.sin((this.t * (2 * Math.PI)) / this.duration);
  }

  /**
   * Clamp origin and range based on {@link direction}.
   * @param origin Origin to clamp.
   * @param range Range to clamp.
   * @returns
   */
  private clamp(origin: number, range: number): [number, number] {
    switch (this.direction) {
      case CubismHarmonicMotionDirection.Left: {
        if (origin - range >= this.minimumValue) {
          range /= 2;
          origin -= range;
        } else {
          range = (origin - this.minimumValue) / 2.0;
          origin = this.minimumValue + range;
          this.normalizedRange = (range * 2.0) / this.valueRange;
        }
        break;
      }
      case CubismHarmonicMotionDirection.Right: {
        if (origin + range <= this.maximumValue) {
          range /= 2.0;
          origin += range;
        } else {
          range = (this.maximumValue - origin) / 2.0;
          origin = this.maximumValue - range;
          this.normalizedRange = (range * 2.0) / this.valueRange;
        }
        break;
      }
      case CubismHarmonicMotionDirection.Centric:
        break;
      default: {
        const neverCheck: never = this.direction;
        break;
      }
    }

    // Clamp both range and NormalizedRange.
    if (origin - range < this.minimumValue) {
      range = origin - this.minimumValue;
      this.normalizedRange = range / this.valueRange;
    } else if (origin + range > this.maximumValue) {
      range = this.maximumValue - origin;
      this.normalizedRange = range / this.valueRange;
    }

    return [origin, range];
  }

The CustomHarmonicMotionParameter is also used as a marker for the CustomHarmonicMotionController to obtain references.

2. Components that Manipulate the Value of Each Parameter

Use the CubismHarmonicMotionController to apply opening and closing values to each parameter.
This is a component that inherits from Component and attaches to the root of Cubism’s Prefab when used.

At initialization, obtains a reference to all CubismHarmonicMotionParameters attached to the Prefab.
If you add/remove a parameter whose value is to operate cyclically during execution, call CubismHarmonicMotionController.refresh() to get the reference again.

  /** Refreshes the controller. Call this method after adding and/or removing {@link CubismHarmonicMotionParameter}. */
  public refresh() {
    const model = CoreComponentExtensionMethods.findCubismModel(this);

    if (model == null || model.parameters == null) {
      return;
    }

    // Catch sources and destinations.
    this.sources = FrameworkComponentExtensionMethods.getComponentsMany(
      model.parameters,
      CubismHarmonicMotionParameter
    );
    this.destinations = new Array<CubismParameter>(this.sources.length);

    for (let i = 0; i < this.sources.length; ++i) {
      this.destinations[i] = this.sources[i].getComponent(CubismParameter);
    }

    // Get cubism update controller.
    this.hasUpdateController = this.getComponent(CubismUpdateController) ! = null;
  }

  ...

  /** Called by Cocos Creator. Makes sure cache is initialized. */
  protected start() {
    // Initialize cache.
    this.refresh();
  }

CustomHarmonicMotionController applies the calculated values for the parameters marked by CustomHarmonicMotionParameter at the timing of lateUpdate() in every frame.

  /** Called by cubism update controller. Updates controller. */
  protected onLateUpdate(deltaTime: number) {
    // Return if it is not valid or there's nothing to update.
    if (!this.enabled || this.sources == null) {
      return;
    }

    // Update sources and destinations.
    for (let i = 0; i < this.sources.length; ++i) {
      this.sources[i].play(this.channelTimescales);

      CubismParameterExtensionMethods.blendToValue(
        this.destinations[i],
        this.blendMode,
        this.sources[i].evaluate()
      );
    }
  }

  ...

  /** Called by Cocos Creator. Updates controller. */
  protected lateUpdate(deltaTime: number) {
    if (!this.hasUpdateController) {
      this.onLateUpdate(deltaTime);
    }
  }

CustomHarmonicMotionController has two setting items.

  /** Blend mode. */
  @property({ type: Enum(CubismParameterBlendMode), serializable: true, visible: true })
  public blendMode: CubismParameterBlendMode = CubismParameterBlendMode.Additive;

  /** The timescales for each channel. */
  @property({ type: [CCFloat], serializable: true, visible: true })
  public channelTimescales: number[] = [];
  • blendMode

    Blend Mode for applying values to parameters.

    The following three values can be set.
    • Override: Overwrite the current value.
    • Additive: Add to the current value.
    • Multiply: Multiply by the current value.
  • channelTimescales

    Set the period of the sine wave.
    Multiple cycles can be set.
Was this article helpful?
YesNo
Please let us know what you think about this article.