AIプログラムとかUnityゲーム開発について

探索や学習などを活用したAI系ゲームを作りたいと思います。

疲れたら椅子に座る

f:id:yasu9780:20201031022009g:plain

SGSからの反省で、椅子を複製して配置したらコードに手を入れなくてもキャラクターがその椅子を認識して座るようにしてみた

SGSの時はキャラクターが空いている椅子を探して、そこに歩いていくようにしてたので、椅子が増えるとプログラムに都度登録していた。
今回は逆にキャラクターが近くに椅子を発見すると、その時疲れていたら座りに行って、体力が戻ったら再度歩き出す
今回はなるべく分散処理的にプログラムを作っていきたい

交通システム:信号追加。まだまだダメ

f:id:yasu9780:20201030233527g:plain

信号追加してみた
まだ、止まるタイミングが遅かったり、後続車に押されて横断歩道に乗り上げたりする
先行車と接触したら停車して、onColisionExit()で再発車にしてるけど、先行車が走り出したのに、後続車のExitイベントが発生しない場合が頻繁に発生する
信号は4秒ごとの青と赤を切り替える
歩行者は信号をまだ見てない
交差点は複雑だなあ

いまさらRTX2080Ti買って、CUDAで機械学習

色々と迷ったがRTX2080Ti中古を購入しました。税込み79800円
新品だといまだに15万円ぐらいで売ってるので約半額

ただ、RTX3080が10万円ぐらいで売ってるので微妙ではある
RTX3070も10/29の深夜販売が予定されていて、価格もおそらく79800円ぐらい
CUDA数は2080Tiより多い(ただしVRAMは少ない)
ドスパラ、GeForce RTX 3070の夜間販売を29日22時から実施 - AKIBA PC Hotline!


ただ、色々とググってみる限りRTX30xxでTensorflowはうまく行ってないって人が多い
Ubuntu上ならなんとかなる気もしたけど、Unityのゲーム開発はWindows上でやってるのでできれば環境はWindowsで作りたい
ということで、RTX2080Tiにしました。
電源の650Wのゴールド買ってるんでなんとかなるでしょうし
本当は、GTX1080あたりでもいいんだけど、価格差が5万円でCUDA数は激増するんで

GPUで動くように色々ダウンロード

まずはCUDA Toolkitをダウンロードする

qiita.com

Unityの中の人の記事を参考にtoolkit 9.0.176をダウンロードしてみたが、インストールしてみると、RTX2080Tiが新しすぎて対応してないエラーが出るので断念
10.1をインストールしてみる(現状11が出ているが、最新版は避ける)問題なくインストールできたらしい
cuda_10.1.243_426.00_win10.exe
というのをDLした

次はCUDA Toolkit10.1に合う、cuDNNをダウンロードする
これにはnVidiaのアカウントが必要。以前GTX1060を買った時にすでにアカウントは作っていたらしい
for 10.1になってるcuDNNをダウンロードする
cudnn-10.1-windows10-x64-v8.0.3.33.zip
というのをDLした

CUDA Toolkitのインストール先にcuDLLを解凍したものを上書きコピーする

あとは、以前と同じmlagents-learnからconfigファイルを指定して学習を開始
Unity editor側でplayを押せとでるので学習を開始する
RTX2080Tiの名前も出てるし、特にエラーになってない
学習自体も速くなっている気がする(前回のCPUでの学習で時間を計っておけばよかった)

f:id:yasu9780:20201028095946p:plain

実はcuDNNをCUDA toolkitにコピーするのを忘れててml-agentsやってみたんだけど
GPUも検出してたし、学習も速くなっていた
cuDNNをコピーして再度学習やってみたが、速度的には同じぐらいだった
cuDNNって本当にいるんだろうか? じつはCUDA ToolkitだけでGPU機械学習できるようになってるのかもしれない?

8万円も払ったありがたみを感じるためにもCPUだけの学習と速度比較をしたかったが
CPUのみの学習環境をまた作るのは面倒くさいので、もういいや
確かに速くなっているんだから ( ^ω^ )

DLLが無いってエラーが出ている?

コンソールの出力をよく見ると、cudnn54_7.dllが見つからないってエラーが出ている
ちょっと調べてみる
初めDLLにパスが通ってないだけかと思ったら、ダウンロードしたcuDNNがcudnn64_8.dllでバージョンが違うみたい
該当するcuDNNを再度ダウンロードしてみる

cudnn54_7に合うバージョンをNVIDIA GPU Computing Toolkit\CUDA\v10.1にコピーし直したらDLLがnot foundのエラーは出なくなった
環境構築成功

GPUの効果はあるのか?

深層学習 TensorFlow GPU を入れてみました | ecobioinfo.com – 環境・生物・情報処理 -

GTX1030みたいな1万円以下のビデオカードで学習が2時間→6分になったって書いてる記事があったけど
うちの環境大丈夫かな? そこまで速くなった気がしないんだけど。・゚・(ノ∀`)・゚・。 
学習中にGPUのパフォーマンスも全然上昇しないし
ml-agentsでGPU使っても速くならないって記事も見た覚えがあるんだけど?
大丈夫かなあ?。・゚・(ノ∀`)・゚・。 

ML-Agentsで強化学習サンプル動かしてみる - AIプログラムとかUnityゲーム開発について
CPUだけで学習した時の学習時間と比較してみたけど2倍ぐらいしか速くなってないんでは?
ちょっとまってよUnityさん大丈夫なの? 。・゚・(ノ∀`)・゚・。 


3Dballは2倍しか速くなってないけど、前に試したCPUだけのサッカー学習だと4倍ぐらい速くなってたんで
CUDAの効果は出てるっぽい
サッカーはエージェントが4人いてRayも出しまくってて学習データがでかいので、より効果があるのかな?
3dBallは単純なんで、CPUとGPUのやり取りとかのオーバーヘッドで効果が出にくいのかもしれない。
一応RTX2080Tiを買った意味はありそうなんで良かったです(*'ω'*)

ML-Agentsで強化学習サンプル動かしてみる - AIプログラムとかUnityゲーム開発について

サッカーは10分で40万stepsぐらい学習出来てるので、1時間で240万steps、24時間で5760万stepsぐらい学習できる感じ
でもタスクマネージャーで見るとGPUは8%ぐらいしか働いてないので、絶対遊んでるんだけどなあ
100%近く行けば、まだ10倍速くなるんじゃないのかねえ? どっか間違ってる気がする

RTX中古を安く買うには

古通販を色々と調べた限り、ねらい目はドスパラ古通販である

ドスパラPalitってメーカーと独占契約をしているので、BTO機などにPalitGeForceを採用している
そのせいか、中古ショップにPalitGeForceが山ほどある
現在(2020/10/28)RTX2080Tiが税抜き70900円で選び放題
ZOTACでLED点灯が故障してる奴は税抜き64900円という激安

今回はじゃんぱらで買ったが、Palit 77800円 Palit Gaming 79800円という値付け
他の中古でRTX2080Tiを7万円台で売ってる店は現状はほぼ無い
メルカリの個人だと75000円前後で買えるが、個人だと保障の面で問題がある
じゃんぱらは会員購入すれば初期不良1カ月)
ただ、RTX3070の販売が開始されれば中古RTX2080Tiはさらに値下がりするかもねえ
ただ、VRAMの大きさの違いは大きい

used.dospara.co.jp

3人でNavMeshSurface

f:id:yasu9780:20201019163401g:plain

3人で移動するので後から来た人に押されて2階から地面に落ちる場合がある
次はWayPointを入れて巡回させてみるかな?
で、椅子を見つけると疲れたら座って休むみたいな。

NPC Populator

モブが歩き回る$10アセット。動画見ていい感じだったんで買ってみたけど
実物は、Unityのロボットに障害物はキューブで、ただWayPointを巡回するだけの代物だった。・゚・(ノ∀`)・゚・。 
ま、人間のモデルと建物を用意したとしても本質は同じだし、プロトタイプとしては同一なんだけどね
実際、開発時に見た目に拘ると開発を抑制するだけだしね。

www.youtube.com

大衆シミュレーターは他の奴だと、歩道を片側歩行で動いて、横断歩道を渡って、テニスコートで応援したり、立ち止まって話してる風にするやつが$100で売ってた
これに自動車と信号が加わると$400とかで売ってる

動的にbakeできるNavMeshSurface

試そう試そうと思いながら数年経ってしまった。・゚・(ノ∀`)・゚・。 
やっと実験しました

マウスでクリックした場所にエージェントが向かう。
壁を動かしても経路変更に対応して移動できる

マウスでクリックしたら改めてNavMeshをbakeし直してます。

navMeshの経路の細さとか登れる坂の角度とかはいままで通りNavigationタブの中で指定します
が、そこで焼きません
オブジェクトをstaticにする必要もありません
(staticにしてmeshが変な風に固まって何度泣かされたことか? あとなぜか2階と1階に移動できる経路が勝手にできたり)

焼くのはNavMeshSurfaceの中のbakeか、スクリプトからです。
複雑な経路だとある程度時間がかかるかもしれないので、焼き終わったかは監視した方がいいかもしれませんね

NavMeshSurface NavSur;
NavMeshAgent navAgent;
Animator anim;
Vector3 targetPosition;

void Start()
{
    NavSur = GameObject.Find("/Main").GetComponent<NavMeshSurface>();
    navAgent = GetComponent<NavMeshAgent>();

    anim = GetComponent<Animator>();
    anim.SetFloat("speed", 0f);
}
void Update()
{
    if (Input.GetButtonDown("Fire1"))
    {
        NavSur.BuildNavMesh();
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
         if (Physics.Raycast(ray, out hit, 50f))
         {
             targetPosition = hit.point;
             navAgent.SetDestination(targetPosition);
             anim.SetFloat("speed", 1f);
         }
     }
}

動的な経路探索焼きこみができると様々な用途で使えるので夢が広がります。・゚・(ノ∀`)・゚・。 
SGSでは家具屋で家の壁を買えますが、あれはあくまで障害物なので、屋根を売ることができませんでした
動的BAKEできれば屋根を売れますし、はしごや階段も売れます。
ユーザーが自分で家を建設できるようになります


参考記事
tsubakit1.hateblo.jp

gametukurikata.com

light11.hatenadiary.com

trail(軌跡)を試してみる

ゲーム作って軌跡はまだ使ったことが無いんだけど、やっぱりあった方が派手になるのでちょっと練習してみた

基本、ParticleSystemですね。
f:id:yasu9780:20201018185436g:plain

攻撃アニメーションが終わったらtrailを止めないといけないので、アニメーションの終了判定が必要

        animInfo = anim.GetCurrentAnimatorStateInfo(0);
        float animTimer = animInfo.normalizedTime%1.0f;

animInfo.normalizedTimeは、0~1までが返る(元のアニメーションが何秒であろうと正規化されて0~1が返る)
注意しないといけないのはループした場合は、0に戻らずに1から始まる。さらに次のループは2から始まる
よって1で割って余りを取得すれば、0~1で判定できる
GetCurrentAnimatorStateInfo(0)は毎回取得しないとダメらしい。

(normalizedTime%1)が前フレームより減ったらアニメが終了したので、パーティクルを止めて、ランダムに次のパーティクルを選ぶ
アニメーションはtestの値で切り替えてるので、アニメーションも切り替える
これで、次々に違うアニメを再生しながら、違うtrail演出を行う

Animator anim;
AnimatorStateInfo animInfo;
public int trailNo;
public ParticleSystem[] pa;
float preTime=0;

void Start() {
    anim = GetComponent<Animator>();
     trailNo = Random.Range(0, pa.Length);
     pa[trailNo].Play();
     anim.SetInteger("test", Random.Range(0,3) );
}

void Update() {
    animInfo = anim.GetCurrentAnimatorStateInfo(0);
    float animTimer = animInfo.normalizedTime%1.0f;
    //Debug.Log("time="+(animTimer) );

    if (preTime > animTimer)
    {
        pa[trailNo].Stop();
        trailNo = Random.Range(0, pa.Length);
        pa[trailNo].Play();
        anim.SetInteger("test", Random.Range(0, 3));
    }
    preTime = animTimer;
}