- 投稿日:2021-02-25T23:02:52+09:00
SkyBoxのフェード切り替え
はじめに
SkyBoxを別のテクスチャにフェードで切り替えたい。
しかし、ビルトインシェーダー内に、2つのCubeMapをブレンドして表示するようなシェーダーはなかったため、もともと入っているシェーダーを改造して作ることにするやりかた
Unityのあらかじめ入っているシェーダのソースコードは、Unity ダウンロード アーカイブから指定バージョンのダウンロードボタンを押すとできます。「ビルトインシェーダー」を押して、ダウンロードします。今回は例として、2019.4.6を落とします
さっそく改造しようと思ったら、なんか初めからできそうなのがはいってる。。
これを使えば解決しました笑
マテリアルにCubeMapを2つ指定して、Valueを動かすとブレンドされます
その他のアプローチ
カメラを2つ使って、RenderTextureでフェードするのも手
参考
UnityのStandardシェーダーのソースコードを取得する
https://bluebirdofoz.hatenablog.com/entry/2019/07/25/093410
ビルトインシェーダガイド
http://www.mikame.net/sample/unity_documentation/Components/Built-in%20Shader%20Guide.html
- 投稿日:2021-02-25T17:54:34+09:00
UnityのMLAgentsでPython使って機械学習 2021年最新版
はじめに
こんにちは。陰キャ大学生です。
前置きはさておき、Pythonを使って機械学習していると、「物理エンジン使ってやってみたいなぁ」なんて思う時があります。
ありません
そこで今回は、Unityのために作られた機械学習ライブラリML Agents を使って物理エンジンで機械学習してみました。
実はすでにQiita上には多くのMLAgentsの記事が上がっています。【ML-Agents】UnityとPythonのTensorFlowをつかって機械学習させてみた(v0.11β対応)
しかし、このMLAgents、バージョンアップが頻繁なうえに、バージョン変わると関数やメソッド名も変わるというなんともQiitaライター泣かせな仕様となっています。
そこで今回は、現在(2021年2月25日)最新であるRelease13 について記事を書きます。
おそらく数か月後には古くて使い物にならない記事になると思いますが、今この瞬間にMLAgentsを使いたいんだ!!という人に届いてほしいと思います。MLAgentsについて
こちらの記事が大変わかりやすく参考になりますので、ぜひご覧ください
環境
MLAgentsの推奨環境
- Unity (2018.4以降)
- Python (3.6.1以降)
今回私が使用した環境
- Unity (2019.4.15f1)
- Python (3.7.9) Anaconda
推奨環境を満たしていれば大丈夫だと思いますが、Unityはバージョン等によりUIが変わる可能性があるのでご容赦ください。
前準備
MLAgentsのダウンロード
こちらのサイトから、Release13をダウンロードしてください。
ダウンロードされたZIPファイルを任意の場所に展開してください。PythonとUnityのダウンロード&インストール
参考記事がわかりやすくまとめてくださったので引用します。参照されてください。
【ML-Agents】UnityとPythonのTensorFlowをつかって機械学習させてみた(v0.11β対応)
準備
Pythonの環境構築
Pytorchのインストール
ターミナルで以下を実行します
pip install torch~=1.7.1 -f https://download.pytorch.org/whl/torch_stable.html関連ライブラリのインストール
展開したディレクトリに移動して、以下を実行
pip install -e ./ml-agents-envs pip install -e ./ml-agents実際、
pip install mlagents
でもインストールできますが、この際は常に最新版がインストールされますので、バージョンの整合性をとるためにもダウンロードしたSetup.pyから参照することをお勧めします。0.24.0がインストールされたことを確認してください
Unitでプロジェクト作成
上のメニューから、
Window>Package Manager
の順に進んで、左上の+マークから、Add Package from disk...
を選択して、<展開したフォルダ>/com.unity.ml-agents/package.json
を選択します。
インポートが始まり、
In Project
にML Agents 1.8.0
があれば大丈夫です。
今回は、箱をボールが追いかけるAIを作成します。(詳しくは記事の最後を見てね)
Unityの詳しい説明等はここでは省略します。ステージの作成
GameObject>3D Object>Plane
を選択し、Stageに改名、Positionを(0, 0, 0)Scaleを(1, 1, 1)に設定
箱の設定
GameObject>3D Object>Cube
を選択し、Targetに改名、Positionを(3, 0.5, 3)Scaleを(1, 1, 1)に設定
ボールの設定
GameObject>3D Object>Sphere
を選択し、RollerAgentに改名、Positionを(0, 0.5, 0)Scaleを(1, 1, 1)に設定Inspector下部の
Add Component
から、Physics>Rigidbody
を追加 ?これめちゃ大事です!!!
後で複製できるように、グループ化しておきます。
GameObject>Create Empty
を選択し、Stage、Target、RollerAgentをドラッグしてグループ化します名前は何でもいいです。
ボールのスクリプト作成
UnityのProject内に
Scripts
フォルダを作り、その中にRollerAgent.cs
を作成します
中身をお好きなエディタで以下のように書き換えてください。
解説は適宜コメントで挿入してあるので見てみてくださいね。RollerAgent.cs// 使用ライブラリのインポート using System.Collections.Generic; using UnityEngine; using Unity.MLAgents; using Unity.MLAgents.Sensors; using Unity.MLAgents.Actuators; // Agentクラスを継承し、必要なところだけ書き換えていきます public class RollerAgent:Agent { Rigidbody rBody; // スタートしたときに呼び出される関数 void Start () { // ボールの物体を変数に格納 rBody = GetComponent<Rigidbody>(); } // Targetをグローバルに宣言 public Transform Target; // エピソード(学習のステップ)が始まった時に呼び出される関数 public override void OnEpisodeBegin() { // ボールのY座標が0=落下したとき if (this.transform.localPosition.y < 0) { // 初期化 this.rBody.angularVelocity = Vector3.zero; this.rBody.velocity = Vector3.zero; this.transform.localPosition = new Vector3( 0, 0.5f, 0); } // ターゲットをランダムな位置に Target.localPosition = new Vector3(Random.value * 8 - 4, 0.5f, Random.value * 8 - 4); } // 観測データ(この場合で言うと学習に必要な数値)の取得 public override void CollectObservations(VectorSensor sensor) { // ボールと箱の座標(x, y, z) x 2 sensor.AddObservation(Target.localPosition); sensor.AddObservation(this.transform.localPosition); // ボールのスピード(x, z) sensor.AddObservation(rBody.velocity.x); sensor.AddObservation(rBody.velocity.z); // トータルで入力次元は8次元になる } // 力を加えるときの乗数 public float forceMultiplier = 10; // アクションが起きたときに呼び出される関数 public override void OnActionReceived(ActionBuffers actionBuffers) { // XとZ軸の入力に合わせてボールに力を加える Vector3 controlSignal = Vector3.zero; controlSignal.x = actionBuffers.ContinuousActions[0]; controlSignal.z = actionBuffers.ContinuousActions[1]; rBody.AddForce(controlSignal * forceMultiplier); // 箱とボールの距離 float distanceToTarget = Vector3.Distance(this.transform.localPosition, Target.localPosition); // 箱にボールが到達したとき if (distanceToTarget < 1.42f) { // 報酬を1に設定 SetReward(1.0f); // エピソードを終了 EndEpisode(); } // ボールが落下したとき else if (this.transform.localPosition.y < 0) { // エピソードを終了 EndEpisode(); } } // 手で動かす際の設定 public override void Heuristic(in ActionBuffers actionsOut) { var continuousActionsOut = actionsOut.ContinuousActions; continuousActionsOut[0] = Input.GetAxis("Horizontal"); continuousActionsOut[1] = Input.GetAxis("Vertical"); } }ファイルのアタッチ
作成したスクリプトを
RollerAgent
にドラッグします。
RollerAgent (Script)
のTarget
に、Target(ゲームオブジェクト)
をドラッグ
Add Component
から、ML Agents>Behavior Parameters
を追加
Vector Observation>Space Size
を8
に変更
Actions>Continuous Actions
を2
に変更
Behavior name
をRollerBall
に変更
Add Component
から、ML Agents>Decision Requester
を追加
Decision Period
を10
に変更テスト
Behavior Parameters
のBehavior Type
をHeuristic Only
に変更中央上部のプレイボタンを押して、矢印キーで遊べるか確認してみてください
学習
<展開したディレクトリ>/config/rollerball.yaml
を作成して、以下を書き込みますrollerball.yamlbehaviors: RollerBall: trainer_type: ppo hyperparameters: batch_size: 10 buffer_size: 100 learning_rate: 3.0e-4 beta: 5.0e-4 epsilon: 0.2 lambd: 0.99 num_epoch: 3 learning_rate_schedule: linear network_settings: normalize: false hidden_units: 128 num_layers: 2 reward_signals: extrinsic: gamma: 0.99 strength: 1.0 max_steps: 500000 time_horizon: 64 summary_freq: 1000
Behavior Parameters
のBehavior Type
をDefault
に変更展開したディレクトリに移動して、以下を実行
mlagents-learn config/rollerball.yaml --run-id=RollerBallListening on port 5004. Start training by pressing the Play button in the Unity Editor.この文言がでたら、Unityに戻って、プレイボタンを押すと、学習が始まります。
最初のうちはボールがオロオロ動くのがなかなかに可愛いです笑学習並列化
物理エンジンのいいところは、モデルを複製して同時に学習を進められるところです。
先ほどグループ化したものをプレハブ化し、複製してみましょう。(特にコード等変える必要はありません)
ログはこのようになります。
Mean Reward
が1に近づけばうまく学習が行われています。
終了したいときは、CTRL+C
で止められます。その時のモデルデータを自動的に保存してくれます。また、バックボーンはTensorFlowなので、別ターミナルでコマンドを実行することで、TensorBoardも確認することができます。
tensorboard --logdir results --port 6006学習モデルでテスト
<展開したディレクトリ>/results/RollerBall/RollerBall.onnx
を、Unityプロジェクト内のAssets
にドロップしたのちに、RollerAgentのInspector
内ののBehavior Parameters>Model
にドロップ。プレイボタンを押すと、学習されたモデルでボールが動く様子を見ることができます。
床から落ちることなく箱を一生懸命追っている姿が確認できますね。可愛い。最後に
たびたびバージョンの変わってしまうMLAgentsですが、使い方によっては機械学習の世界が広がると思います。
僕も早くコマンドラインで学習できるようにならないかなぁ
- 投稿日:2021-02-25T15:07:56+09:00
Live2DモデルをAnimatorを使わずにスクリプトでアニメーションさせる
Animatorを使わずにスクリプトでアニメーションさせる方法です。
まずLive2D.Cubism.Coreをインポートします追加インポートusing Live2D.Cubism.Core;次にアニメーションを管理するClassを宣言します。
actionclippublic class actionclip { string ease = "Linear"; public float startValue = 0; public float endValue = 0; public float duration = 0; public float delay = 0; private float currentDurationTime = 0; private float currentDelayTime = 0; public actionclip(float StartValue, float EndValue, float Duration, float NextDelay = 0, string Ease = "Linear") { startValue = StartValue; endValue = EndValue; duration = Duration < 0 ? 0 : Duration; delay = NextDelay < 0 ? 0 : NextDelay; ease = Ease; currentDurationTime = currentDelayTime = 0; } public void Reflesh() { currentDurationTime = currentDelayTime = 0; } public float Value { get { float p = endValue; if (currentDurationTime <= duration) { currentDurationTime += Time.deltaTime; float t = 1.0f; if (duration > 0) { t = currentDurationTime / duration; } else { t = 1.0f; } t = t > 1.0f ? 1.0f : t; switch (ease.ToUpper()) { case "LINEAR": break; case "EASEIN": t = Calc.EasyEaseIn(t); break; case "EASEOUT": t = Calc.EasyEaseOut(t); break; case "EASEINOUT": t = Calc.EasyEaseInOut(t); break; } p = Mathf.Lerp(startValue, endValue, t); } else { currentDelayTime += Time.deltaTime; } return p; } } public bool isAnimationEnd { get { return (Value == endValue && currentDelayTime > delay); } } }Valueを呼び出すごとに開始値から終了値までの遷移値を取り出し、終了値になったらDelay時間待つだけのスクリプトです。前回の簡単イージングはここで使っています。
https://qiita.com/RYUMAGE/items/0351af94eff77dc32187そしてLive2Dのパラメータを管理するクラスを宣言します
parampublic class param { public CubismParameter dat; //Animation private actionclip[] animations; public bool isRepeat = false; private int currentAnimCnt = 0; private float startValue = 0; public string ID { get { return dat.Id; } } public void SetAnimation(actionclip[] Animations, bool Repeat = false) { currentAnimCnt = 0; animations = Animations; isRepeat = Repeat; startValue = dat.Value; } public void StopAnimation() { isRepeat = false; animations = null; } public bool isRunning { get { return animations != null; } } public void Update() { if (animations != null) { dat.Value = animations[currentAnimCnt].Value; if (animations[currentAnimCnt].isAnimationEnd) { animations[currentAnimCnt].Reflesh(); currentAnimCnt++; if (currentAnimCnt >= animations.Length) { if (isRepeat) { currentAnimCnt = 0; } else { StopAnimation(); } } } } } }最後にLive2Dモデルデータのパラメーターをこのクラス内のdatに参照させます。
さらにLateUpdate()内でparamをUpdateさせます。LateUpdateprivate param[] Live2DParams; private CubismModel Model; public void InitializeParameters(CubismModel model) { Model = model; Live2DParams = new param[model.Parameters.Length]; for (int i = 0; i < model.Parameters.Length; i++) { Live2DParams[i] = new param(); Live2DParams[i].dat = model.Parameters[i]; } } void LateUpdate() { for (int i = 0; i < Live2DParams.Length; i++) { Live2DParams[i].Update(); } }こうやってMistyLink TouchableではAnimatorに寄らないスクリプトによるリアルタイムアニメーションをやっていました。
- 投稿日:2021-02-25T10:45:38+09:00
簡単イージング
なぜかUnityにはイージングを制御する関数がありません。
なので自分用にイージングを極力簡略化したスクリプトを組みました。本来イージングには開始値、終了値、経過時間、トータル時間の4つが必要ですが、実際イージングは比率しか求めないので開始位置と終了位置は無視して経過時間/トータル時間のみを渡して計算してます。
このスクリプトは0~1までの値を渡して、その値にイージング処理した値を返すものです。
たとえばLerpのt値にこれらの処理を加えることでイージング効果がでます。
Calc.cspublic static float EasyEaseIn(float t) { return t * t; } public static float EasyEaseOut(float t) { return -1 * t * (t - 2.0f); } public static float EasyEaseInOut(float t) { t = t *0.5f; if (t < 1) { return 0.5f * t * t; } t = t - 1; return -0.5f * (t * (t - 2.0f) - 1) ; }
- 投稿日:2021-02-25T03:42:56+09:00
突然Unity Recorderで録画できなくなった時の対処法
設定を変更していないのに録画できなくなった
Unity Recorderの設定をまったく変えてないのに、
突然録画されなくなったのです。
※もしかしたら何か手が触れて設定が変わっちゃったのかもしれない出力されたアニメーションGIFが真っ白の状態。
タイミング的な何かが重なって奇跡的に
今まで動いていたという可能性もあります。複数カメラを使った録画
?このようにUIと3Dオブジェクトを同時に表示させる必要がありましたので、
UIと3Dの2つのカメラを使った録画でした。
?上の通りCamera設定をActiveCameraに設定しています。結論: Include UIのチェックを外す
今回の解決方法は「Include UIのチェックを外す」でした。Include UIとは?
Include UI
について調査できておりません。To include UI GameObjects in the recording.
録画時にuGUIを表示する際は必須なのかと
思っていましたがそうでもなさそうです。
(チェックしなくても録画できるため)最後に
現在執筆中の「DOTweenの教科書」が佳境で時間がなく
本質的な原因は突き止めておりません。
Include UI
については後日調査予定。開発環境
- Unity2019.4.15f1
- Unity Recorder 2.5.2