🪩

TikTok Effect Houseでエフェクトを作ろう

TikTok Effect Houseとは

TikTok Effect Houseトップページ

TikTok Effect HouseはTikTokが公式で提供しているARエフェクトの制作ツールです。顔に重ねるフィルターや、表情に反応する演出といった、TikTokでよく見かけるエフェクトを自分で作って投稿できます。

利用はWindowsとMacのデスクトップアプリから行い、無料で使えます。専門的な3D開発の知識がなくても、用意されたパーツを組み合わせるだけでエフェクトを形にできるのが特徴です。

この記事では、Effect Houseの中でも動きをつけるための仕組みであるビジュアルプログラミングのさわりだけを扱います。

ビジュアルプログラミングとは

ビジュアルプログラミングは、文字でコードを書く代わりに、ノードと呼ばれる箱を線でつないで処理の流れを組み立てる方式です。コードで書く「いつ(イベント)」「何を覚えておくか(変数)」「どういう時に(条件分岐)」といった要素が、それぞれノードとして用意されています。

Effect Houseではこの仕組みをビジュアルスクリプティングと呼びます。同じ処理をTypeScriptのコードで書くこともできるので、この記事では両方のやり方を見ていきます。

題材として、口の開閉を検知してFilterをON・OFFするエフェクトを実装してみます。

触ってみる前の準備

まずは公式サイトからアプリを入手します。

公式サイトからダウンロードして起動します。以下の画像のような画面が表示されたら準備完了です。

EffectHouse初回起動

ビジュアルスクリプティングで作る

最終的に、口の開閉を検知してFilterをかけるサンプルを作成します。完成イメージは以下の通りです。

口の開閉でFilterのON・OFF

Filterを用意する

まずは、上のアイコンが並んでいるメニューから「Filter」を選択します。すると以下の画面になります。右側にあるINSPECTORの「Texture」から、さまざまなエフェクトを選べます。今回は「Rainbow Wave」を使いました。

Filterを押下した後の画面

口の開閉を検知するノードを組む

ここからVISUAL SCRIPTINGタブを使います。パネルの空いている所で右クリックしてAdd nodeを押します。検索ボックスに mouth と入力し、「Mouth Open」系のノードを追加します。もう一度右クリックして set enabled(または set visible)を検索し、追加します。

ビジュアルプログラミング 口の開閉を検知するノードを作成

配置が終わったら、ノードをつなげます。

  • Begin: Exec → Set EnabledのEnabled OnをOnにしたノードのEnter
  • End: Exec → Set EnabledのEnabled OnをOffにしたノードのEnter
  • Set EnabledノードのComponent → Filterを指定

ビジュアルプログラミングノードの接続

Componentを選択するとシーン選択ウィンドウが立ち上がります。ここでTransformではなくFilterを選択してから、Filterを選びます。

set-enabled_選択画面set-enabled_Filter選択
ComponentのNoneの部分をクリックしてシーン選択ウィンドウを起動します。シーン選択ウィンドウ上部のセレクトからFilterを選択し、さらに階層構造になっているFilterを選択します。

これで、口を開いている間だけFilterが有効になるエフェクトができました。

TypeScriptで制御する

さきほどビジュアルスクリプティングで作ったFilterの切り替えは、TypeScriptのコードでも実現できます。ここではいきなり完成形を作らず、Hello, World!から段階を追って同じ動きにたどり着いてみます。まずはスクリプトを作成します。

スクリプトの作成

スクリプトを開くとコードエディタが立ち上がります。

コーディング初回起動

⌘+Sで保存され、プレビューに即時適用されます。コードを書きながら結果をすぐ確認できるので、動作を試しながら進められます。

Hello, World!

まずは動作確認も兼ねて、起動時にログを出してみます。

@component()
export class NewScriptComponent extends APJS.BasicScriptComponent {

  /**
   * Called before the first frame update
   */
  onStart() {
    console.log("Hello, World!🎉");
  }

  /**
   * Called once per frame
   */
  onUpdate(deltaTime: number) {

  }

  // Insert more for your logic
}

onStartはエフェクトの開始時に一度だけ呼ばれます。保存するとログに出力されます。

HelloWorldをログに出力

1秒ごとにログを出してみる

次に、毎フレーム呼ばれるonUpdateを使って、1秒ごとにログを出してみます。

@component()
export class NewScriptComponent extends APJS.BasicScriptComponent {

  // 変数(数を覚えておく箱)
  private elapsed: number = 0;  // 経過時間の合計
  private count: number = 0;    // 数えた秒数

  onStart() {
    console.log("スタート!カウント開始します");
  }

  onUpdate(deltaTime: number) {
    // deltaTime = 前のフレームからの経過秒数。毎回足していく
    this.elapsed += deltaTime;

    // 合計が1秒を超えたら…
    if (this.elapsed >= 1) {
      this.elapsed -= 1;       // 1秒分を引く
      this.count += 1;         // カウントを1増やす
      console.log(this.count + "秒経過");
    }
  }
}

onUpdateの引数deltaTimeには前のフレームからの経過秒数が入ります。これを足し続けて1秒を超えたタイミングでログを出すことで、1秒ごとのカウントを実現しています。

1秒ごとにログ出力

1秒ごとにFilterをON・OFFしてみる

最後に、ビジュアルスクリプティングで作ったFilterの切り替えをコードでも書いてみます。

@component()
export class NewScriptComponent extends APJS.BasicScriptComponent {

  private targetFilter: APJS.SceneObject | null = null;
  private elapsed: number = 0;
  private isOn: boolean = true;

  onStart() {
    const filterSceneObj = this.getSceneObject().parent;
    this.targetFilter = filterSceneObj;
  }

  onUpdate(deltaTime: number) {
    if (!this.targetFilter) return;

    this.elapsed += deltaTime;
    if (this.elapsed >= 1) {
      this.elapsed -= 1;
      this.isOn = !this.isOn;
      this.targetFilter.enabled = this.isOn;
      console.log("フィルター: " + this.isOn);
    }
  }
}

ここではスクリプトをFilterの子オブジェクトに配置している前提です。getSceneObject().parentで親をたどり、対象となるFilterを取得しています。あとは1秒ごとにenabledを切り替えるだけで、ビジュアルスクリプティングのSet Enabledノードと同じことをコードで行っているイメージです。

1秒ごとにONOFF

Gifに変換したのでチラついて見えますが、正常に動作しています。

さわってみた所感

クリエイターが作ったサンプルを見ると、顔の一部が変化するものやゲームとして遊べるものなど、面白いエフェクトが数多く存在しています。

ビジュアルスクリプティングなら線をつなぐだけ、TypeScriptなら書き慣れた文法でロジックを書けるので、入り口のハードルは思ったより低いと感じました。

興味を持ったらまず1つ作ってみるのが、楽しみながら覚えるいちばんの近道だと思います。

新着記事

関連ネットワーク(beta)

ドラッグで移動 / Ctrl+ホイールでズーム