TinyUnityUnity

TinyUnityのサンプルを徹底解析!AudioForest編

TinyUnityとは?

Unity Editorで小型で軽量のHTML5ゲームやアプリケーションを作成できるUnityのModeの1つです。

Unity 2018 3.0b以上で起動できます。

導入や概要については以前の記事に記載しました!

TinyUnity 0.13.2-preview 試して見ました!

今回はこちらのTinyUnityのサンプルプロジェクトの解析を徹底的におこなって行こうかと思います。

・対象のサンプルプロジェクトを開く

前回解説した記事通りにTinyUnityのpackageをインストールした前提で進めていきます!

Assets/TinySamples/AudioForest/AudioForest.utproject

を開きます。

・どんなサンプルゲーム?

起動するとAudioが再生され、画面をクリックするとガサガサっという効果音が再生されます。

BGM再生や効果音再生確認目的のサンプルプロジェクトのようです。

・プロジェクトの構成

・Hierarchyの構成

・Camera

2Dのレンダリングエンティティです。

エンティティとは?

Tiny Modeエンティティコンポーネントと通常のUnity GameObjectコンポーネントとの間の類似性は強いです。通常のUnityコンポーネントと同様に、Cameraレンダリング、衝突、オーディオ、パーティクル、UIなどの機能をカバーするいくつかのTiny Modeコンポーネントが組み込まれています。インスペクタのエンティティに追加することもできます組み込みのコンポーネントをGameObjectsに追加します。さらに、通常のUnity MonoBehavioursと同様に、カスタムコンポーネントを自分で作成してエンティティに追加することもできます。

 

しかし、いくつかの重要な違いがあります。Tinyモードコンポーネントは純粋にデータコンテナです。int、float、Vectors、Colors、Strings、列挙型などの整理された、名前付きの、関連するデータセットを含むことができますが、コードを含むことはできません。これは、データとコードの両方を含むことができる通常のUnityコンポーネントとは対照的です。これは、高速パフォーマンスと小さなランタイムフットプリントを実現するために、Tinyモードが従うECSパターンの一部です。

 

もう1つの重要な違いは、エンティティに同じタイプのコンポーネントを複数追加できないことです。

公式ドキュメントより抜粋

要は、GameObjectの機能制限版みたいな感じ??

エンティティは純粋なデータコンテナであり、動作や振る舞いなどのロジックを含むことはできないようです。

 

インスペクターからカメラの設定を行うことができます。

・Forest

SpriteRendererのエンティティのようです。

・BackgroundMusic

AudioSourceを保持するためのエンティティのようです。

・Breeze

「ザーザー」という森の環境音を保持しているエンティティのようです。

・TreeWind

「ゴウンゴウン」となりひびく環境音を保持するエンティティのようです。

・Walk

「ガサガサ」という効果音を保持するエンティティのようです。

・プロジェクトアセット構成について

・Entities

エンティティグループが格納されています。

(エンティティをまとめたもの)

(通常のUnityのSceneに近い感じ)

・Scripts

Audioを各種KeyInputに応じて再生させるコードが記載されています。

詳細は次のコード解析にて

・Sounds

プロジェクト中で使われている各種AudioClipが格納されています。

・Textures

プロジェクト中で使われているTextureが格納されています。

・プロジェクトのコード解析

・AudioInputSystem

プロジェクト中のBGMや効果音をキー入力に応じて再生させる為のスクリプト
以下コード

namespace game {

  @ut.executeAfter(ut.Shared.UserCodeStart)
  @ut.executeBefore(ut.Shared.UserCodeEnd)
  export class AudioInputSystem extends ut.ComponentSystem {

    OnUpdate(): void {

      // On iPhone, you need to interact at least once to play audio clips
      if (ut.Runtime.Input.getMouseButtonDown(0)) {
        this.playClip("Walk");
      }

      if (ut.Runtime.Input.getKeyDown(ut.Core2D.KeyCode.T)) {
        this.playClip("TreeWind");
      }

      if (ut.Runtime.Input.getKeyDown(ut.Core2D.KeyCode.B)) {
        this.playClip("Breeze");
      }

      if (ut.Runtime.Input.getKeyDown(ut.Core2D.KeyCode.W)) {
        this.playClip("Walk");
      }

      if (ut.Runtime.Input.getKeyDown(ut.Core2D.KeyCode.S)) {
        // Stop all AudioSources except BackgroundMusic
        this.world.forEach([ut.Entity, ut.Audio.AudioSource], (entity, audioSource) => {
          let name = this.world.getEntityName(entity);
          if (name != "BackgroundMusic") {
            this.stopClip(name);
          }
        });
      }

      if (ut.Runtime.Input.getKeyDown(ut.Core2D.KeyCode.P)) {
        // Pause all AudioSources
        this.world.forEach([ut.Audio.AudioConfig], (audioConfig) => {
          audioConfig.paused = true;
        });
      }

      if (ut.Runtime.Input.getKeyDown(ut.Core2D.KeyCode.R)) {
        // Resume all AudioSources
        this.world.forEach([ut.Audio.AudioConfig], (audioConfig) => {
          audioConfig.paused = false;
        });
      }
    }

    // Play an AudioClip
    private playClip(audioSourceEntityName: string): void {
      let audioSourceEntity = this.world.getEntityByName(audioSourceEntityName);
      if (!this.world.hasComponent(audioSourceEntity, ut.Audio.AudioSourceStart)) {
        this.world.addComponent(audioSourceEntity, ut.Audio.AudioSourceStart);
      }
    }

    // Stop an AudioClip
    private stopClip(audioSourceEntityName: string): void {
      let audioSourceEntity = this.world.getEntityByName(audioSourceEntityName);
      if (!this.world.hasComponent(audioSourceEntity, ut.Audio.AudioSourceStop)) {
        this.world.addComponent(audioSourceEntity, ut.Audio.AudioSourceStop);
      }
    }

  }

}

各部位のコード解析

  @ut.executeAfter(ut.Shared.UserCodeStart)
  @ut.executeBefore(ut.Shared.UserCodeEnd)

 

TypeScriptの作法がわからないからなんとも言えないが・・・
現状は何かのおまじないとして覚えておく・・・!!

  export class AudioInputSystem extends ut.ComponentSystem {

TinyUnityのエンティティグループを制御するためのクラスは
ut.ComponentSystemを extendsして使うっぽい

private playClip(audioSourceEntityName: string): void {
      let audioSourceEntity = this.world.getEntityByName(audioSourceEntityName);
      if (!this.world.hasComponent(audioSourceEntity, ut.Audio.AudioSourceStart)) {
        this.world.addComponent(audioSourceEntity, ut.Audio.AudioSourceStart);
      }
    }

Audio再生用のprivateメソッド
let audioSourceEntity = this.world.getEntityByName(audioSourceEntityName);
で名前からEntityグループ内のEntityを取得
this.world.addComponent(audioSourceEntity, ut.Audio.AudioSourceStart);
で再生をおこなっている?

private stopClip(audioSourceEntityName: string): void {
      let audioSourceEntity = this.world.getEntityByName(audioSourceEntityName);
      if (!this.world.hasComponent(audioSourceEntity, ut.Audio.AudioSourceStop)) {
        this.world.addComponent(audioSourceEntity, ut.Audio.AudioSourceStop);
      }
    }

 

this.world.addComponent(audioSourceEntity, ut.Audio.AudioSourceStop);
で対象の再生停止をおこなっているっぽい。

  OnUpdate(): void {
}

 

Updateで{}内の記述を実行する。
今回はKeyInput周りの記述が書かれていた。

  if (ut.Runtime.Input.getMouseButtonDown(0)) {
        this.playClip("Walk");
      }

 

左マウスボタンがクリックされた時に実行される。
this.playClip(“Walk”);
で、対象のエンティティグループ内の[Walk]エンティティが再生される。

  if (ut.Runtime.Input.getKeyDown(ut.Core2D.KeyCode.T)) {
        this.playClip("TreeWind");
      }

 

Tキーが押された時に実行される。
this.playClip(“TreeWind”);
で、対象のエンティティグループ内の[TreeWind]エンティティが再生される。

  if (ut.Runtime.Input.getKeyDown(ut.Core2D.KeyCode.S)) {
        // Stop all AudioSources except BackgroundMusic
        this.world.forEach([ut.Entity, ut.Audio.AudioSource], (entity, audioSource) => {
          let name = this.world.getEntityName(entity);
          if (name != "BackgroundMusic") {
            this.stopClip(name);
          }
        });
      }

 

Sキーが押された時に実行される。

this.world.forEach([ut.Entity, ut.Audio.AudioSource], (entity, audioSource) => {
});

this.world.forEach([ut.Entity, ut.Audio.AudioSource]
を行うことで、Entityグループ内のAudioEntityをforEachで回すことが可能

let name = this.world.getEntityName(entity);
でEntityの名前を取得し
this.stopClip(name);
で対象の名前のEntityのAudioをstopできるようです。

this.world.forEach([ut.Audio.AudioConfig], (audioConfig) => {
          audioConfig.paused = true;
        });

 

こちらでAudioをポーズできるようです。

・このサンプルプロジェクトについてのまとめ

音楽再生や停止の処理を実装する時のサンプルとして適切かと感じました!

・次にやりたいこと

他のサンプルゲームの構造を徹底的に解析していきます!!!