Aizu-Progressive xr Lab blog

会津大学のVR部であるA-PxLの部員が持ち回りで投稿していくブログです。部員がそれぞれVRに関する出来事やVRにちなんだことについて学んだことを書いていきます。

「面白法人カヤックVR分室」としても活動しています。詳細はこちら

敵の編集を楽にしようと頑張ったお話

始め

Aizu-PxLの修士になりました、なおしです。
皆さんは敵の動きをどのように作成しますか?

  • プログラムを書く
  • NavMeshを使う
  • ランダムに動かす
  • etc

今まで私はこれらを活用して敵を作成したことがあります。ゲームにおいて敵の存在は切っても切れない存在だと思っていますが、作る上ではとても厄介なものだと感じています。

今回の内容は前半にこれまでの経験、後半に今やっていることをまとめます。

今まで敵を作成して感じたこと

NavMeshを使ったとき

敵をプレイヤーに向かわせるときNavMeshを使用することが多いと思います。もしくはプログラムでも実装するのはそう難しくはないかもしれません。

NavMeshを使用した際の利点として

  • 敵は必ずプレイヤーに向かう
  • プログラムを書くのが楽
  • 様々な敵の種類にも適用できる

があります。

しかし、ただプレイヤーに向かうだけの敵は想像以上に飽きるのが速いです。 また、NavMeshの弱点として地面に接地しない敵には使うことが出来ないところです。NavMesh使うのはプレイヤーを探索する時だけにして、一定距離に近づいたときに別の行動をするアルゴリズムにしたほうが面白くなるかなと思っています。(その分、作業量が増える)

個々の敵をプログラムする

個々の敵に対してプログラムを書くことによって

  • 様々な動きをする敵が作れる
  • 飽きにくい
  • 敵の種類に応じて動きを作成できる

という利点が生まれます。

しかし、プログラムで敵を動かす際に発生する欠点は

  • プログラムが複雑化したり、デバッグが大変になる
  • ゲームバランスの調整が難しい
  • 敵の種類を増やしにくい

と感じます。NavMeshを使ってプレイヤーに向かわせる、そこから敵に攻撃、プレイヤーが逃げたら追跡等モンスターの動きの幅が広がる一方でかなり編集に難があると思います。

ランダムに動かす

ランダムに敵を動かすプログラムを作るのは簡単ですが、それもアルゴリズムによりけりかなと感じました。例えば、Random関数などを使ってその方向に動かすとすればすぐに書けます。しかし、そのプログラムで動く敵はブルブル震えるだけになるでしょう。

アルゴリズムを考えて壁にぶつかるまで前進して、ぶつかった際はランダムで方向転換、そして前進するようなものにすればそれっぽく動くようになります。

ランダムに動かす場合の一番の問題点としてはゲームバランスの調整が先の2つに比べてかなり難しくなることだと思います。

簡単な総括

今までの開発経験を通して次のことが言えるのではないかって思っています。

  • 簡単にプログラムされた敵は編集、開発は楽であるが飽きやすい
  • 複雑なプログラムで作成された敵は飽きにくいが編集、開発が難しい

今やろうとしたこと

只今私は個人開発をしているのですが、その中で簡単に敵の動きを編集できるものを作ろうと頑張っています。だた、まだ完成してませんし完成する目途がまだ立っていない状態なのでご了承ください。

動きをクラスにする

今までは1つのプログラムに様々な動きを書いていました。そのプログラムを改良やリファクタリングした結果、敵の動きをクラスで表現するという結論に至りました。簡単なクラス図は次のようになります。

f:id:aizu-vr:20190603000933j:plain

まず、Monsterクラス、MonsterActionクラスを作成します。MonsterクラスはMonsterActionクラスのリストを保持します。MonsterActionクラスにはAction関数があり、MonsterActionクラスを継承したクラスはAction関数をオーバーライドします。最後にMonsterクラスはリストに入ったMonsterActionクラスのAction関数を呼び出し、多態性によりMonsterActionクラスを継承したクラスのAction関数を実行することが出来ます。


解説すると意味がわかりませんね。簡単に言うと動き(攻撃、移動など)をリストに保存してそれを順に実行していくということです。
ちなみに、このようなデザインパターンStateパターンと呼ぶそうです。
追記:もしかしたら違うかもしれません。間違っていたらごめんなさい

これらを参照しました。

ゲームプログラミングC++

ゲームプログラミングC++

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

エディタ拡張ReorderableListを使う

コンポーネントのリストをエディタで編集しようとすると結構めんどくさいです。また、今回はMonoBehaviorを継承していないものを編集するため普通のInspectorでは編集できません。

f:id:aizu-vr:20190603004259p:plain
要素の追加、入れ替えが面倒。そして見にくい

そこでReorderableListを使用したいと思います。

f:id:aizu-vr:20190603004555p:plain
Unityで見たことあると思います

これを使用することにより型によって表示する枠を変えたり、要素の入れ替えや追加が簡単になります。

ReorderableListについてやエディタ拡張については次を参照しました。 Unity エディター拡張入門

また、一度別の個人開発でも使用しました。Fire Commander!

今起きている問題

だた、まだこれが完成していません。今起きている問題が派生クラスの型が消えて基底クラスの型として表示されることです。始めは上手く動作しますが、リビルドされた際に型が消えてしまうというバグに悩まされています。

デバッグ用にメンバ変数(派生クラスも含む)をすべて表示する関数を用意しているのですが、それも派生クラスの関数が呼ばれていないので保存されているデータとしても基底クラスに変わっていしまっているみたいです。

過去に使った際は一つのクラスだけのリストでしたが今回はいくつかの派生クラスを保存するのでダウンキャストをしなければなりません。そして、UnityEditor上でどのようにデータが保存されているかまだ理解できていないことが原因だと思います。

解決策

考えうる解決策としてデザインパターンの変更を検討しています。Monsterクラスに保存するものをデータの構造体に変更し、そのデータを一度Factoryクラスに渡してMonsterActionクラスを生成するクラス図を考えています。

欠点としてデータの構造体にすべての動きに対応できるほどのメンバ変数を用意しないといけないこと、新しいMonsterActionクラスを作成した際にFactoryクラスも修正しないといけない点です。

最後に

みっともない話ですが、まだ今やろうとしていることが完成せずにブログにまとめています。ただ、エディタ拡張、デザインパターンを習得する価値は非常に高いと感じています。

この2つを使うことによってゲームの仕様変更やゲームバランスの調整、拡張が容易になるはずです。これからこれらも勉強していきますし、完成次第もう一度ブログにまとめ直す予定であります。

完成するまで応援して欲しい。

本格的VRキャラバン始動?!

お久しぶりです、会津VR部という名前のころの初代部長の秋山です。部のブログ投稿は本当に久しぶりですね。
今回は何度かブログにもでている「VRキャラバン活動」について書きたいと思います。


VRキャラバン活動とは

前提にVRキャラバン活動とは何かわからない方はこちらを読んでみてください。

aizu-vr.hatenablog.com


子どもの夢とおいしいもの祭り

www.minpo.jp

5/18, 19に「ふくしま産業省受賞企業プレゼンツ 2019 子どもの夢とおいしいもの祭り」が道の駅あいづ湯川・会津坂下にて開催されました。こちらのイベントで今回VRキャラバン活動を実施させてもらいました。


今までのキャラバン活動と違う点

今までのキャラバン活動を振り返ってみるとただ車で機材を搬入し、その場で会場のコンセントから延長コードを伸ばし電力を供給する普通すぎるやり方の、どの要素がキャラバンなんだろう...とつくづく思いますね。今回の展示はその点を改善すべくなんと電気自動車を使って展示をしてきました!
機材の運搬から電力供給をすべて電気自動車で行いました。タープを電気自動車の横に付けそれっぽい雰囲気にしようとしました!
f:id:aizu-vr:20190527153056j:plain

展示の様子

今回の展示に使用したHMDはVivePro。この機材に限りませんが直射日光は絶対に防ぎたいですね。そのためにタープとカーテンを用意しました。
f:id:aizu-vr:20190527131611j:plain
f:id:aizu-vr:20190527131617j:plain
突発的な提案だったのもあり見た目は非常に悪いですが、一応最低限の運用はすることができました。せっかくVRコンテンツを展示するのですからもっとサイバー感やコンテンツの雰囲気とあった(またはその全く逆)見た目にしたかったですね。次回までに揃えたいです。

今回の展示を通して

良かった点

  • 実際に直射日光が防げる簡易的な部屋を作ってしまえばプレイできることが確認できた

  • 屋外で展示することによって普段と違った点で展示コンテンツの改良点が見つかった

  • 屋外でタープを使用しての運用時や電気自動車を使用時の注意点が多く知れた

見つかった問題点/反省点

  • カーテンを2枚しか用意しなかったので日光の関係上、午前と午後で向きを変える必要性があった

  • カーテンでしきりを作るとタープの中が非常に暑くなってしまった。暑い時期には冷風機を用意すべき

  • 電気自動車のバッテリー残量が20%(多分ここは車種による)を切ると、省電力モードに自動で切り替わってしまう。省電力モードだと外部への電力供給ができなくなるので強制的に店仕舞いとなってしまった

  • プレイヤーのカメラの向きを変更するボタンを必ず作るべき。展示の関係上、タープの4辺の1辺は外部の方でも見れるように開けとく必要があるが、あまりにも日差しが強いとその方向を向いた瞬間にトラッキングが外れることがある。(コンテンツの質の問題。日光関係なしにカメラの方向転換ボタンは必ず作るべき)

  • 風が強くてタープが飛んでしまう。これが1番の問題だった。
    カーテンでしきると風を受ける面積が大きくなりタープが飛びやすくなった(よく考えれば事前にわかったはず)。
    今回ペットボトルに水をいれたものをタープの全足に括り付けたが、それだけでは全然足りなかった。旗の下によくある重石を常に用意しとくべき。カーテン用も含め10個あると安心できる。
    風を入れる穴をカーテンに開けると少しはましになった。

おわりに

電気自動車を使用したこのキャラバン活動はこれから本格的に始動していくと思います。
もし、来てほしい方がいらっしゃいましたらブログまたはTwitterなどで声をかけていただければご相談できると思います。その時はよろしくお願いします! f:id:aizu-vr:20190527131539j:plain

MRTKv2 サンプルシーンをHoloLens1で試した

はじめに

学部2年の木村です. 今年からHoloLensをいじったりしてます. v2の前のバージョンのMRTKのことはあまり知らないのですが, MRTKv2のサンプルをHololensでビルドしてみたのでやったことの記録のようなものを書きます.

環境

やった手順

まずは, 公式のGitHubからMRTKv2 RC1 Refreshの2つのアセットをダウンロードしてUnityへインポートします.

次に, サンプルのシーンを開きます. MRTKには機能などに応じて多くのサンプルシーンが用意されているようです. ここではAssets/MixedRealityToolKit.Examples/Demos/HandTracking/Scenes/HandInteractionExamplesを使います.


シーンを開くと, このようにTMP Importerのウィンドウが出てきました.

このシーンではText Mesh Proが使われているようなのでImport TMP Essentialsを選択して必要なパッケージをインポートしておきます.

次に, HoloLens用の設定です.
こんな感じで設定しました

  • Platform -> Universal Windows Platform
  • Target Device -> HoloLens
  • Architecture -> x86

Player Settings

これでシーンの準備はできたはずなので, ビルドする前にHolographic Remoting Playerでシーンを再生してみます.

Window/XR/Holographic Emulation を開いて

  • Emulation Mode -> Remote to Device
  • Remote Machine -> HoloLensのIPアドレスを入力

入力できたらHoloLensでHolographic Remoting Playerを開き, connectをクリックします.connectingになればokです.

シーンを再生するとこんな感じになりました

次はビルドします.
まずはUnity上での操作です.
Build Settingで現在のシーンをScenes In Buildへ追加したらBuildクリックして, 適当なフォルダを作成してそのフォルダを選択します.

Unityでのビルドが完了したらさっき選択したフォルダの中に.slnという拡張子のファイルがあるのでそれをダブルクリックします. すると, Visual Studioでslnファイルが開かれます.

今度はVisual Studioでの操作です.
slnファイルが開かれたら構成やプラットフォームの設定をします. 次の画像のように設定します.

  • ソリュージョン構成 -> Release
  • ソリュージョンプラットフォーム -> x86
  • その右のやつ -> リモートコンピューター

最後にデバッグ/プロジェクト名のプロパティページ/デバッグ/コンピューター名にHoloLensのIPアドレスを入力します.
あとは, Ctrl + F5でデバッグ無しでビルドすれば完了です.


動画ではfpsが低いんですけど録画中とかスクショを撮った瞬間にfpsが30~40くらいに下がっちゃうみたいですが, 録画とかしてなければ60fpsでした.

70行弱で書ける!音声データから話者を当てる人工知能を作ってみた!

こんにちは。学部3年の柴山です。
今回はPythonで音声データを機械学習させて、話者認識(誰が話しているかを判定する)をする方法を紹介したいと思います。
コード総数70行弱、しかし正答率98.7%コスパ良しな人工知能に興味を持っていただけたのなら、ぜひ最後までお付き合いください。

データの前処理

今回使用した音声データは「12人の話者が日本中の駅名を呟いたもの」です。
音声データは駅名ごとに用意してあり、総数は約4万件、一人当たり3千ちょいあります。
ちなみに日本の駅の総数は9500個ほどらしいので、これでも一部なんですね( ̄◇ ̄;)

以下のことに気をつけていただければ音声データはなんでも平気です。
全てのデータは.wavファイルにします。
ファイル名を全て「<話者の名前>_<番号>」という形式にします。
データは学習用とテスト用に7:3の割合でディレクトリを分けます。
学習用のディレクトリ名は「<話者の名前>」、テスト用のディレクトリ名は「<話者の名前>test」にします。
f:id:aizu-vr:20190425185127p:plain

コード

今回は以下のライブラリを使用します。

import scipy.io.wavfile as wav  # .wavファイルを扱うためのライブラリ
from sklearn.svm import SVC     # SVC(クラス分類をする手法)を使うためのライブラリ
import numpy                    # ndarray(多次元配列)などを扱うためのライブラリ
import librosa                  # 音声信号処理をするためのライブラリ
import os                       # osに依存する機能を利用するためのライブラリ


次にROOT_PATHに音声データの各ディレクトリが入っているディレクトリへの絶対パス、speakersに話者名の配列を代入します。
word_trainingとspeaker_trainingは後々、計算結果を入れたり、データのラベリング(そのデータがどの話者の音声なのかを対応づける)をしたりするために使います。

# ルートディレクトリ
ROOT_PATH = '<音声データのディレクトリが入っているディレクトリへの絶対パス>'

# 話者の名前(各話者のデータのディレクトリ名になっている)
speakers=['SP203', 'SP205', 'SP206', 'SP208', 'SP210', 'SP211',
    'SP502', 'SP605', 'SP619', 'SP622', 'SP704', 'SP708']

word_training=[]    # 学習用のFCCの値を格納する配列
speaker_training=[] # 学習用のラベルを格納する配列

次に、MFCC(メル周波数ケプストラム係数)を求めるための関数を定義します。
MFCCとは音声にどのような特徴があるかを数値化したものです。
この数値によって分類していきます。

# MFCCを求める関数
def getMfcc(filename):
    y, sr = librosa.load(filename)      # 引数で受けとったファイル名でデータを読み込む。
    return librosa.feature.mfcc(y=y, sr=sr) # MFCCの値を返します。

ディレクトリごとにデータをロードして、MFCC求めていきます。

# 各ディレクトリごとにデータをロードし、MFCCを求めていく
for speaker in speakers:
    # どの話者のデータを読み込んでいるかを表示
    print('Reading data of %s...' % speaker)
    # 話者名でディレクトリを作成しているため<ルートパス+話者名>で読み込める。
    path = os.path.join(ROOT_PATH + speaker)    
    # パス、ディレクトリ名、ファイル名に分けることができる便利なメソッド
    for pathname, dirnames, filenames in os.walk(path): 
        for filename in filenames:
            # macの場合は勝手に.DS_Storeやらを作るので、念の為.wavファイルしか読み込まないようにします。
            if filename.endswith('.wav'):
                mfcc=getMfcc(os.path.join(pathname, filename))
                word_training.append(mfcc.T)    # word_trainingにmfccの値を追加
                label=numpy.full((mfcc.shape[1] ,), 
                speakers.index(speaker), dtype=numpy.int)   # labelをspeakersのindexで全て初期化
                speaker_training.append(label)  # speaker_trainingにラベルを追加

word_training=numpy.concatenate(word_training)  # ndarrayを結合
speaker_training=numpy.concatenate(speaker_training)

ここで機械学習のプログラムを書く上でのちょっとしたコツを一つ。
上記のprint('Reading 〜')のところをご覧ください。
この行は本来機械学習をする上では一切必要ありません。ただ文字をコンソールに出力しているだけですから。
ですが、この一行がないと自らのプログラムがきちんと動いているのか疑心暗鬼になります。
余談ですが、上記の4万件のデータだと一回の実行に10時間以上かかりました。
その間何も映らない暗い画面だけ、タスクマネージャーを見るとCPU使用率が100%。
私は一度、数時間実行した挙句、自分のコードが信じられずにCtrlを押しながらCキーに指を当て...。
皆さんには同じ目にあって欲しくありません笑。
なのである程度進捗がわかるような構造を作りましょう。

そして、いよいよ学習部分ですが、なんと3行です。

# カーネル係数を1e-4で学習
clf = SVC(C=1, gamma=1e-4)      # SVCはクラス分類をするためのメソッド
clf.fit(word_training, speaker_training)    # MFCCの値とラベルを組み合わせて学習
print('Learning Done')

python機械学習をするためのライブラリが充実しているのがいいですね。
カーネル係数は長くなる上私も勉強中であまり下手なことは言えないので、
カーネル法という機械学習に用いられるパターン認識の手法に使う値」と思ってください。
この値をいじると正答率に直接影響します。私の場合はトライ&エラーの結果1e-4という値に落ち着きましたが、
データによってはある程度上下すると思います。

つぎに、学習したデータをもとに、〜testディレクトリのデータでテストします。

counts = []     # predictionの中で各値(予測される話者のインデックス)が何回出ているかのカウント
file_list = []  # file名を格納する配列

# 各話者のテストデータが入っている~testというディレクトリごとにMFCCを求めていく
for speaker in speakers:
    path = os.path.join(ROOT_PATH + '%stest' % speaker)
    for pathname, dirnames, filenames in os.walk(path):
        for filename in filenames:
            if filename.endswith('.wav'):
                mfcc = getMfcc(os.path.join(pathname, filename))
                prediction = clf.predict(mfcc.T)    # MFCCの値から予測した結果を代入
                # predictionの中で各値(予測される話者のインデックス)が何回出ているかをカウントして追加
                counts.append(numpy.bincount(prediction))   
                file_list.append(filename)  # 実際のファイル名を追加

最後は推測される話者のインデックスより、speakersから話者の名前を取得し、実際のファイル名がその名前から始まっていれば正解。
違っていれば間違いという判定で正答率を求めます。

total = 0   # データの総数
correct = 0 # 正解の数

# 推測される話者の名前がファイル名の頭と一致したらCorrect
for filename, count in zip(file_list, counts):
    total += 1
    result = speakers[numpy.argmax(count-count.mean(axis=0))]   # 
    if  filename.startswith(result):
        correct += 1

print('score : ' + str(correct / total))

まとめ

こうしてみると随分ライブラリ頼りで、70行弱なんてのは詐欺まがいですが、そこがpythonのいいところと許していただきたいです笑。
自分で想定していた以上の正答率が出たので嬉しいですが、課題としては如何せん実行時間がかかり過ぎかもしれません。
今回使用したMFCCというのは精度は高いですが、計算量が多めな手法だったので、今度は別の手法も試してみたいと思います!
最後まで読んでいただきありがとうございました!

学部3年 柴山 叶

Marvelous Designerの紹介

こんにちは、学部3年の丹野です。 今回私はMarvelous Designerというソフトの紹介をしていきたいと思います。 Marvelous Designerとは服作りを目的とした3DCGツールです。 機能としては服のモデリング以外にもUVの配置やテクスチャの適用。作成した服のアニメーションも作ることができます。

続きを読む

会津大学VR部の部員が持ち回りで投稿していくブログです。特にテーマに縛りを設けずに書いていきます!