まゆたまガジェット開発逆引き辞典

電子工作やプログラミングのHowtoを逆引き形式で掲載しています。作りたいモノを決めて学んでいくスタイル。プログラマではないので、コードの汚さはお許しを。参照していないものに関しては、コピペ改変まったく問いません

同じプロジェクト間、違うスクリプトでのデータの受け渡し

同じプロジェクトの別のスクリプトで変数とそのデータを使いまわしたいことがあります。
そのときのデータを受け渡し(正確には読み取り)のメモです。

準備

1.「GameObject」メニュー→「Create Empty」で空のオブジェクトを2つ作る
2.それぞれの名前を「Send」「Recieve」などにする。同じ名前にしないよう気をつける
3.「Assets」を右クリックしてスクリプトを2つ作り、「SendScript」「RecieveScript」などと名前をつける
4.「Send」オブジェクトには「SendScript」を、「Recieve」オブジェクトには「RecieveScript」をドラッグ&ドロップする
5.「SendScript」と「RecieveScript」をダブルクリックしてスクリプトを書く

スクリプト 「SendScript」データを送る方(参照先)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SendScript : MonoBehaviour {

//この値を「RecieveScript」に送る(参照する)
public int ScoreData = 100;

	void Start () {
		
		

	}
	
	// Update is called once per frame
	void Update () {
		
	}
	
}

スクリプト 「RecieveScript」データを受け取る方(処理する先)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class recieveScript : MonoBehaviour {

//この2つの変数が必要
 GameObject scoreBox;
SendScript script; 


	// Use this for initialization
	void Start () {
	
//Sendというオブジェクトを探す
		scoreBox = GameObject.Find ("Send");
        script = scoreBox.GetComponent<SendScript>();

//新しく変数を作って、「SendScript」の変数「ScoreData」を入れる
		int score = script.ScoreData;

        Debug.Log ("スコアは" + score);


		
	}
	
	// Update is called once per frame
	void Update () {
		
		
	}


    
}

VRMキャラクターの表情を一定時間ランダムに変える

こちら2つの応用編です。コルーチン使用部分はたぶん力技。
prince9.hatenablog.com
prince9.hatenablog.com


マイクの最大音量を計測し、そのときにランダムに表情を変えるというスクリプトを書いてる途中のメモです。
キーボードを押すごとにランダムに表情が変わり、2秒後に元に戻ります。

準備

こちらを参考にして、必要な表情を作成しておきます。
prince9.hatenablog.com

スクリプト

「BlendList = new List(){"Neutral","Smile","Setunai","SHINKEN"};」で使いたい表情をリスト化します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRM;

public class FaceKeep : MonoBehaviour {
private VRMBlendShapeProxy proxy;


public List<string> BlendList;
private string rBlend;

//「処理を止める処理」を停止するか否か
bool isRunning = false;

	// Use this for initialization
	void Start () {
//使いたい表情のリスト
		BlendList = new List<string>(){"Neutral","Smile","Setunai","SHINKEN"};
		
	}
	
	// Update is called once per frame
	void Update () {
		

if (proxy == null)
        {
            proxy = GetComponent<VRMBlendShapeProxy>();
        }
        else
        {
			//上矢印キーをキーコードで判断
	if(Input.GetKeyDown(KeyCode.UpArrow)) {

//ランダムにリストから表情を選ぶ
		rBlend = BlendList[ Random.Range(0, BlendList.Count) ];
           Debug.Log(rBlend);

		proxy.SetValue(rBlend, 1.0f);

//時を止める処理
		StartCoroutine(TimeStop());

	} else {
			proxy.SetValue(BlendList[0], 1.0f);





	}
			


		}
		
	}

	IEnumerator TimeStop() {  
		//「処理を止める処理」を開始する
	if( isRunning ) { yield break; }
	isRunning = true;

//2秒時を止める
		yield return new WaitForSeconds(2.0f);

//2秒後表情を通常に戻す
		proxy.SetValue(rBlend, 0f);
				proxy.SetValue(BlendList[0], 1.0f);

				//「処理を止める処理」を停止する(動き出す)
isRunning = false;

	}
}

一定以上のボリュームのときに大きさが変わる

音量が一定以上になったときにキャラクターの表情を変えたいと思い、そのテストとしてやってみました。
一定以上のボリュームになったら、オブジェクトのサイズと色が変わります。
ただ実際に実装したいのが「表情」なので、サイズと色が変わったらそれを2秒保持する(=2秒次の処理を止める)ことを行なっています。
瞬間的に表情が変わり続けるのはどうかと思うので・・・

数秒後に処理を行う、など時間を扱う処理である「コルーチン」を使いましたが、力技なような気がしてます。

大きさの変化に関して参考にさせて頂いたのがこちら。
tips.hecomi.com

前準備 再生したいサウンドファイルを設定する

スクリプトを書いて、オブジェクトにコードをドラッグ&ドロップします。
そうするとInspectorに「Audio Source」の項目ができるので、▶︎をクリックして「AudioClip」に再生したいサウンドファイルをドラッグ&ドロップします。

スクリプト

状態を保持する時間を作っているので、音との同期が確実に正確ではありませんが、実験ではまあまあでした。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/* 
ボリュームに合わせてオブジェクトの大きさを変えたい場合に必要
using System.Linq;
*/

[RequireComponent(typeof(AudioSource))]	


public class SoundMoveScript : MonoBehaviour {

//256を1024にするともっと細かく検出できる
float[] wData = new float[256];

//「処理を止める処理」を停止するか否か
bool isRunning = false;



private AudioSource audioS;

	// Use this for initialization
	void Start () {
		
       audioS = GetComponent<AudioSource>();
	   
		
	}
	
	// Update is called once per frame
	void Update () {

/* 
ボリュームの変化に合わせてオブジェクトの大きさを変える場合はここだけ。他の変数との兼ね合いで変数名を変えてますが、例ほぼそのままです
		AudioListener.GetOutputData(wData, 1);
        var volume = wData.Select(x => x*x).Sum() / wData.Length;
        transform.localScale = Vector3.one * volume * 10;

		*/


		
		 float vol = GetMaxVolume();
		  Debug.Log(vol);
 
//ボリューム最大値が0.5以上のとき

   if(vol >= 0.5) {
	   gameObject.GetComponent<Renderer>().material.color = Color.blue;
	   transform.localScale = new Vector3(4, 4, 4);

   } 
  
    else {
		//0.5以上になったときの処理を停止する=状態を保持して、次の状態へ移行する
		StartCoroutine(TimeStop());


	}
 
 

	}


	float GetMaxVolume()
{ 

    float a = 0;
    audioS.GetOutputData(wData,0);
    foreach(float vMax in wData)
    {
    //最大値を取り出す
		a += Mathf.Max(vMax);
    }
	  
    return a/256.0f;
	
}


 



IEnumerator TimeStop() {  
	//「処理を止める処理」を開始する
	if( isRunning ) { yield break; }
	isRunning = true;


 	//2秒止める(状態を保持する)
  yield return new WaitForSeconds(2.0f);

//次の処理へ移行
	transform.localScale = new Vector3(1, 1, 1);
	gameObject.GetComponent<Renderer>().material.color = Color.red;

		//2秒止める(状態を保持する)
	yield return new WaitForSeconds(2.0f);
	//「処理を止める処理」を停止する(動き出す)
isRunning = false;



}


}

VRMで数秒後に表情を変化させる

音楽のアタックに合わせて表情を変えようとしてる途中の産物です。
笑顔の表情の数秒後に元の表情に戻したい場合の処理をメモしました。

表情をつくる

VRoidや購入したりダウンロードしたVRMファイルはあらかじめいくつかの表情がプリセットとして登録されています。
そのプリセットを呼び出す場合は、
proxy.SetValue("Neutral", 1.0f);
となります。この場合、「Neutral」がプリセット名(BlendShapeName)になります。
f:id:prince9:20181116194316p:plain

プリセットを作成する場合は、
1.「Hierarchy」のキャラクターをクリックして、Playボタンを押す
2.「Inpector」→「VRM Blend Shape Proxy」の「Blend Shape Avatar」→「Blend Shape(Blend Shape Avatar)」をダブルクリックする
f:id:prince9:20181116194427p:plain
3.「Add BlendShapeClip」を押して、作りたい表情の名前を入力する。その後Saveを押して保存する。
このときの名前がプリセット名となります
f:id:prince9:20181116194459p:plain
4.「▶︎ Face」の▶︎を押して展開し、スライダを動かして表情をつくる
5.表情を作成したら、「Apply」を押して登録する
6.スクリプトの中で呼び出すときは、
proxy.SetValue("Smile", 1.0f);
とする。この場合は「Smile」が3.で登録したプリセット名

VRMで表情を変化させる

ご本家の通り、下記の2つが必要になります。
BlendShapeを操作する - dwango on GitHub

・proxy = GetComponent();
・proxy.SetValue("Smile", 1.0f) 表情ON
・proxy.SetValue("Smile", 0f) 表情OFF

例として、キーボードで表情を変える例が下記です。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRM;

public class FaceChange : MonoBehaviour {

private VRMBlendShapeProxy proxy;

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {

	
        if (proxy == null)
        {
            proxy = GetComponent<VRMBlendShapeProxy>();
        }
        else
        {

//キーボード入力
	if(Input.GetKey(KeyCode.UpArrow)) {

//表情呼び出し
		proxy.SetValue("Neutral", 1.0f);
	} else
            {
                proxy.SetValue("Neutral", 0f);
            }
	
	if(Input.GetKey(KeyCode.DownArrow)) {
		proxy.SetValue("Smile", 1.0f);
		
	} else
            {
                proxy.SetValue("Smile", 0f);
            }

	if(Input.GetKey(KeyCode.RightArrow)) {
		proxy.SetValue("Setunai", 1.0f);
	} else
            {
                proxy.SetValue("Setunai", 0f);
            }

	if(Input.GetKey(KeyCode.LeftArrow)) {
		proxy.SetValue("SHINKEN", 1.0f);
	} else
            {
                proxy.SetValue("SHINKEN", 0f);
            }

		}


		
	}
}

一定時間後に表情を変える

笑顔から一定時間後に元の表情に戻したい場合の処理がこちらです。
TimeCount -= Time.deltaTime;を使っています。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRM;

public class FaceKeep : MonoBehaviour {
private VRMBlendShapeProxy proxy;

//3秒後に表情を戻す(別の表情にする)
float TimeCount = 3;

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		

if (proxy == null)
        {
            proxy = GetComponent<VRMBlendShapeProxy>();
        }
        else
        {

			proxy.SetValue("Smile", 1.0f);

//表情処理終章後にカウント開始
			TimeCount -= Time.deltaTime;

//カウントが0になったら=この場合は3秒経ったら
			if(TimeCount <= 0)
                {
               
				proxy.SetValue("Smile", 0f);
//表情変化
				proxy.SetValue("Neutral", 1.0f);

                 }



		}



		
	}
}

マウスドラッグでオブジェクトを動かす

ググってみて動かしてみたら、どれをやっても動かない。英語で検索してみたところ、解決は「Main Camera」のタグを「MainCamera」にするとのこと。どの日本語サイトもそれについては言及しておらず、動かない!とマウスを投げる方がいそうなので、メモしました。

カメラのタグを「MainCamera」にする

Hierarchyの「Main Camera」をクリックし、Inspectorの「Tag」を「MainCamera」にする
f:id:prince9:20181108031333p:plain

スクリプトを書く

下記のスクリプトを書きます。「Vector3 mousePos」のZの値は適当でOKです。マウスはXY軸にしか動かないからですが、ないとエラーになってしまいます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MouseDragScript : MonoBehaviour {


private void OnMouseDrag()
    {

		//10の値は適当。マウスはXY方向にしか動かないので、Z軸は適当な値でOK。値なしだと動かない
        Vector3 mousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10);
        Vector3 objectPos = Camera.main.ScreenToWorldPoint(mousePos);

        transform.position = objectPos;
    }

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}


	
}

TidalCyclesでライブコーディング 準備編

プログラミングをしつつ音を生成したりVJしたりする「ライブコーディング」が急にしたくなったので、環境構築のメモです。
今回は「TidalCycles」という音生成の環境を使って演奏していきます。
www.youtube.com

TidalCyclesのインストール(Mac)

1.下記から「Download.zip」をクリックし、ダウンロードする
tidal-bootstrap

2.下記からAppleIDでログインし、自分のOSに合ったcommand line toolsをダウンロードする
Sign in with your Apple ID - Apple Developer

3.「ユーティリティ」→「ターミナル」アプリを立ち上げ、下記を入力してエンターキーを押す

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"


4.1.でダウンロードした中にある「tidal-bootstrap.command」をダブルクリックする。TidalCyclesのインストールが始まる。
途中「y/n (or press Enter to accept)」とキーボード入力を求められたら、「Y」キーを押してからエンターキーを押す
5.「プロセスが完了しました」という表示が出たら、ターミナルを終了させる
6.「アプリケーション」の中にある「SuperCollider」アプリをダブルクリックして立ち上げる
7.エディタが立ち上がるので、左の画面に下記を入力する

include("SuperDirt")

f:id:prince9:20181101114347p:plain

8.「include("SuperDirt")」の部分をマウスドラッグして選択し、エンターキーを押す
9.インストールが始まるので、しばし待つ。「-> SuperDirt」が出てきたら終了
10.SuperColliderを終了させ、再度起動させる
11.「SuperDirt.start;」と入力し、この部分をマウスドラッグで選択して「command+enter」キーを押す
12.右下に「SuperDirt: listening to Tidal on port 57120」と出たらOK
f:id:prince9:20181101114719p:plain

13.「アプリケーション」から「Atom」をダブルクリックして開く
14.「Atom」メニュー→「Preference」を選択し、「+Install」をクリックする
f:id:prince9:20181101132544p:plain

15.「Tydalcycles」と入力して、「Package」を押す
f:id:prince9:20181101132639p:plain

16.Tydalcyclesの「Install」をクリックしてインストールする
f:id:prince9:20181101132732p:plain

17.ターミナルを再度立ち上げ、「brew install ghc」と入力してエンターキーを押す

Tydalcyclesを動かす

1.SuperColliderで右下に「SuperDirt: listening to Tidal on port 57120」と出ているか確認する
2.Atomを立ち上げ、「Packages」→「TidalCycles」→「Boot TidalCycles」を選択する
3.「d1 $ sound "bd sn"」と入力する
4.「File」→「Save」を選択し、「Test.tidal」など、拡張子が「.tidal」にして保存する
5.「d1 $ sound "bd sn"」をドラッグして選択し、「command+enter」キーを押す

上記の5.でエラーが出た場合

1.下記から「Download Core」をクリックしてダウンロード、インストールする
www.haskell.org

2.ターミナルを立ち上げ、「cabal install tidal」と入力してエンターキーを押す
3.SuperColliderを立ち上げ「SuperDirt.start;」と入力し、この部分をマウスドラッグで選択して「command+enter」キーを押す
4.Atomで「Tydalcyclesを動かす」の4.で保存したファイルを開く
5.「Packages」→「TidalCycles」→「Boot TidalCycles」を選択する
6.「d1 $ sound "bd sn"」と入力する
7.音が鳴る。止めたいときは「hush」と入力して、この部分をマウスドラッグで選択して「command+enter」キーを押す

IBM Watsonで音声認識→テキスト変換をする(難易度:易)

この記事はVTuber Tech #2 Advent Calendar 2018 22 日目の記事です。
qiita.com

Advent Calendar投稿につき、加筆しています。

自己紹介

リアル/バーチャルで学芸員をしております、よーへんと申します。
プログラマーではありませんが、仕事でインタラクティブ作品を制作したり展示のお手伝いをしています。
バーチャルYouTuberとしてアート・テクノロジートークイベントの司会や配信をしている関係で、今回の記事を書いてみました。
エンジニア・プログラマーではないので、小目汚しのコードがあったらすみません。

概要

UnityとIBM Watsonの音声認識機能を使って、生配信でリアルタイムに日本語字幕を表示します。
一応YouTube公式でリアルタイム字幕機能はあるようですが、チャンネル1万人以上が対象のようです。
アーカイブの方が観る人多いんだから、自動字幕オンにしてもらえばいいじゃんというツッコミはなしで・・・

Watson公式でこんなページがありますが、現在仕様が変わっており全く使えません。Oh...
www.ibm.com

2018年10月末現在において、いちおう下記の方法で「日本語で」音声認識をしてテキスト化ができました。使用したのはUnity2018+iMac5Kです。
f:id:prince9:20181027203053p:plain

下記が実際の配信画面のスクショになります。
f:id:prince9:20181222000807p:plain

下記のリンクですでに似た記事がありますが、本記事は実際に運用した際のものになります。
qiita.com

システム概要

配信はバーチャルルーム作成サービス「cluster」とYouTubeLiveを使用しました。
clusterで配信専用のアカウントを司会アカウントとは別に作成し、ミキサー「AG06」のループバック機能を使って、配信専用アカウントで流れてきた音声すべてをUnity+Watsonの音声認識に通しています。
clusterの画面とUnityのゲームビューをOBSで合成し、clusterの音声とともにYouTubeに流しています。

やってみた所感

ウチが主催するトークイベントで、出演者さんの許可を得て試してみました。
認識精度はまずまずでしたが、「あー」「えー」などのタメも変換されてしまうため、そこだけ削除するスクリプトを入れるべきでした。
あとこもった喋り方をされる出演者さんの認識が極端に残念でした。
認識のタイムラグは1秒もなかったような気がしています(ただ音量確認のため実際配信された音声をモニタリングしていたので、もしかしたら2秒ほどラグがあったかもしれません)。

Watsonの登録をして、アセットをインストールする

下記のサイトを参照して、「アセットのインストール」までを行います(「サンプルを動かす」の前まで)。
kan-kikuchi.hatenablog.com

IDとパスワードをコピーする

1.IBM Cloudにログインしている状態で、「IBM Cloud」のロゴをクリックする
2.ダッシュボードが出てくるので、作成したプロジェクト名をクリックする
3.「資格情報」のところの「URL」「ユーザ名」「パスワード」をどこかにコピーしておく

サンプルを改良して、テキストを表示する

1.「IBM Watson SDK for Unity」をダウンロード&インポートすると、「Assets」に「Watson」というフォルダができているので、「Watson」→「Examples」→「ServiceExamples」の中の「ExampleStreaming」シーンをダブルクリックして開く
2.「Assets」フォルダに日本語フォントの「.ttf」ファイルをドラッグ&ドロップする
2.Hierarchyの「Canvas」→TextStreamingをクリックし、Inspectorの「Font」に2.のファイルをドラッグ&ドロップする
3.FontSizeを適切な大きさにする(30くらい)
4.Colorで色を変える
5.「Pos X」「Pos Y」で位置を調整する
6.Textの部分に日本語を入力し、表示されるか確認する

画像を表示する

テロップなどを出したい場合は下記のようにします。なお、OBSを使ってネット配信することを考え、Canvasのサイズを1280×720にしています。
下記の場合はOBSでクロマキー抜きをするため、画像ファイルの背景を緑色にしています。
f:id:prince9:20181027202652p:plain

1.Assetsに画像ファイルを読み込む
2.画像ファイルをクリックし、Texture Typeを「Sprite(2D and UI)」にする
f:id:prince9:20181027201519p:plain
3.「GameObject」メニュー→「UI」→「Image」を選び、Inspectorの「Source Image」に1.の画像をドラッグ&ドロップする
4.キーボードで画像を切り替えたい場合は、スクリプトを作ってこの「Image」にドラッグ&ドロップする
5.Hierarchyの「Canvas」をクリックし、下記のような設定にする
f:id:prince9:20181027201405p:plain
6..Hierarchyの「Canvas」→「Image」をクリックし、下記のような設定にする
f:id:prince9:20181027201608p:plain

音声認識をしてテキスト表示させる

1.Hierarchyの「ExampleStreaming」をクリックし、Inspectorに下記をコピペする。
・Service URL この記事の「IDとパスワードをコピーする」の3.でコピーした「URL」
・Username この記事の「IDとパスワードをコピーする」の3.でコピーした「ユーザ名」
・Password この記事の「IDとパスワードをコピーする」の3.でコピーした「パスワード」

2.「Watson」→「Examples」→「ServiceExamples」→「Scripts」→「ExampleStreaming」をダブルクリックし、スクリプトを編集できるようにする
3.日本語が使えるように下記のように追加する
・public bool Active { 〜以下の部分に、「_service.RecognizeModel = "ja-JP_BroadbandModel";」を追加する

追加後:

_service.DetectSilence = true;
                _service.EnableWordConfidence = true;
                _service.EnableTimestamps = true;
                _service.SilenceThreshold = 0.01f;
                _service.MaxAlternatives = 0;
                _service.EnableInterimResults = true;
                _service.OnError = OnError;
                _service.InactivityTimeout = -1;
                _service.ProfanityFilter = false;
                _service.SmartFormatting = true;
                _service.SpeakerLabels = false;
                _service.WordAlternativesThreshold = null;

                //日本語設定
                _service.RecognizeModel = "ja-JP_BroadbandModel";

                _service.StartListening(OnRecognize, OnRecognizeSpeaker);

3.テキストの表示を変えるため、下記のように編集する
・string text = string.Format 〜を削除するかコメントアウトして、「string text = string.Format("{0}", alt.transcript);」と書き換える

コメントアウト&追加後:

foreach (var alt in res.alternatives)
                {
                    //string text = string.Format("{0} ({1}, {2:0.00})\n", alt.transcript, res.final ? "Final" : "Interim", alt.confidence);
                    
                    //表示されるテキスト
                     string text = string.Format("{0}", alt.transcript);

                    Log.Debug("ExampleStreaming.OnRecognize()", text);
                    ResultsField.text = text;
                }

4.マイクを用意し、接続する。MacWindowsもOSの環境設定のサウンドの項目で入力(マイク)を設定する
5.再生ボタンを押して、マイクに向かってしゃべる

難易度高ですが、認識をチューニングしたい(カスタムモデルを使いたい)という場合は合わせて下記をご覧ください。
qiita.com