20210505のUnityに関する記事は8件です。

[Unity] Inspector上でVector3の値を(0,1)に制限する

Unityでプロパティの範囲を(0,1)に制限しておきたいとき、floatまたはint値であれば [SerializeField, Range(0f,1f)] float hoge; のようにすることで値を0から1の間に制限することができます。 Vector2/3で同様に [SerializeField, Range(0f,1f)] Vector3 hoge; とすると、Inspector上で Use Range with float or int. と怒られてしまいます。 そこで、下記のスクリプトを用意し、 UserAttribute.cs using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif public class Clamp01VectorAttribute : PropertyAttribute { } #if UNITY_EDITOR [CustomPropertyDrawer(typeof(Clamp01VectorAttribute))] public class Clamp01VectorDrawer : PropertyDrawer { // Necessary since some properties tend to collapse smaller than their content public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { return EditorGUI.GetPropertyHeight(property, label, true); } // Draw a disabled property field public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { switch (property.type) { case "Vector2": property.vector2Value = new Vector2( Mathf.Clamp01(property.vector2Value.x), Mathf.Clamp01(property.vector2Value.y) ); break; case "Vector3": property.vector3Value = new Vector3( Mathf.Clamp01(property.vector3Value.x), Mathf.Clamp01(property.vector3Value.y), Mathf.Clamp01(property.vector3Value.z) ); break; } EditorGUI.PropertyField(position, property, label, true); } } #endif これを適当な場所に保存してから [SerializeField, Clamp01Vector] Vector2 hogeVec = new Vector2(0.5f,1f); のように記述すると、インスペクター上からは(0,1)の範囲しか入力できなくなります。 余談ですが、エディター拡張をEditorフォルダ外に置く今回のような方法、いろんな記事でちょこちょこ見ていたので今回自分もやってみたのですが、普通にビルドも通りますね。いまさら?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Unity] Inspector上でVector3の各成分を(0,1)に制限する

Unityでプロパティの範囲を(0,1)に制限しておきたいとき、floatまたはint値であれば [SerializeField, Range(0f,1f)] float hoge; のようにすることで値を0から1の間に制限することができます。 Vector2/3で同様に [SerializeField, Range(0f,1f)] Vector3 hoge; とすると、Inspector上で Use Range with float or int. と怒られてしまいます。 そこで、下記のスクリプトを用意し、 UserAttribute.cs using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif public class Clamp01VectorAttribute : PropertyAttribute { } #if UNITY_EDITOR [CustomPropertyDrawer(typeof(Clamp01VectorAttribute))] public class Clamp01VectorDrawer : PropertyDrawer { // Necessary since some properties tend to collapse smaller than their content public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { return EditorGUI.GetPropertyHeight(property, label, true); } // Draw a disabled property field public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { switch (property.type) { case "Vector2": property.vector2Value = new Vector2( Mathf.Clamp01(property.vector2Value.x), Mathf.Clamp01(property.vector2Value.y) ); break; case "Vector3": property.vector3Value = new Vector3( Mathf.Clamp01(property.vector3Value.x), Mathf.Clamp01(property.vector3Value.y), Mathf.Clamp01(property.vector3Value.z) ); break; } EditorGUI.PropertyField(position, property, label, true); } } #endif これを適当な場所に保存してから [SerializeField, Clamp01Vector] Vector2 hogeVec = new Vector2(0.5f,1f); のように記述すると、インスペクター上からは(0,1)の範囲しか入力できなくなります。 (0,1)ではなく(min,max)にするにはこちら 余談ですが、エディター拡張をEditorフォルダ外に置く今回のような方法、いろんな記事でちょこちょこ見ていたので今回自分もやってみたのですが、普通にビルドも通りますね。いまさら?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】NCMBのAndroid通知アイコンを設定する(四角に表示される問題)

今回はNCMBを用いたプッシュ通知にアイコンを設定する方法を紹介したいと思います。 前回はすんなり設定できなかったために先送りになっていましたが対応してみました。 同じようなことが原因でお悩みの方の参考になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】敵の視野を疑似的に表示するギズモをつくった

よくある扇状の視野範囲をギズモで擬似的に表示できます。 検知の方法は偉大なるこちらの方の記事を参考にさせていただくと良いと思います。 : Unityで敵が主人公を検知する範囲を視覚内に限定する Enemy_DetectionAI.cs using UnityEngine; public class Enemy_DetectionAI : MonoBehaviour { [SerializeField] private float searchRadius; [SerializeField] private float searchAngle = 130f; private void OnDrawGizmos() { Vector3 trans = transform.position; Gizmos.DrawWireSphere(trans, searchRadius); Gizmos.color = Color.red; float x, y, r, a; r = searchRadius; a = transform.eulerAngles.y + 90; x = r * Mathf.Cos(a * Mathf.PI / 180f); y = r * Mathf.Sin(a * Mathf.PI / 180f); Vector3 forwardPos = new Vector3(-x, 0, y); Gizmos.DrawSphere(trans + forwardPos, .1f); Gizmos.color = Color.green; float xr, yr; xr = r * Mathf.Cos((a + searchAngle) * Mathf.PI / 180f); yr = r * Mathf.Sin((a + searchAngle) * Mathf.PI / 180f); Vector3 rightPos = new Vector3(-xr, 0, yr); Gizmos.DrawSphere(trans + rightPos, .1f); Gizmos.color = Color.blue; float xl, yl; xl = r * Mathf.Cos((a - searchAngle) * Mathf.PI / 180f); yl = r * Mathf.Sin((a - searchAngle) * Mathf.PI / 180f); Vector3 leftPos = new Vector3(-xl, 0, yl); Gizmos.DrawSphere(trans + leftPos, .1f); Gizmos.color = Color.red; Gizmos.DrawLine(trans, trans + forwardPos); Gizmos.DrawLine(trans, trans + rightPos); Gizmos.DrawLine(trans, trans + leftPos); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】①アイテム、アイテムボックスの実装【3D脱出ゲーム制作】

【内容】 ・オブジェクトのクリック判定実装 ・アイテムの実装 ・アイテムボックスの実装 【オブジェクトのクリック判定実装】 今回、クリックしたらアイテムボックスへ取得されるアイテムの実装を行う。 上記アイテムを実装するには、以下のものが必要 ①Event Trigger ・ヒエラルキーに追加したアイテム内にコンポーネントとして追加するもの ・追加したアイテムに対して、どのような動作をするか設定できる ②Physics Raycaster ・メインカメラにコンポーネントとして追加 ・アイテムに対してレーザー的なものを当てて、判定するもの ③Event System ・ヒエラルキーにUIから追加するもの ・とりあえず必要なものと認識 要調査 ④クリック判定のスクリプト(ヒエラルキーのアイテムにそれぞれD&Dで追加) ・今回は、クリックをしたらアイテムボックスへ格納されるというスクリプトを作成(以下) ・下記スクリプトにより、クリックしたら後述する「Item.cs」と「ItemBox.cs」を動作させる形で記載 PickUpObj.cs public class PickUpObj : MonoBehaviour { [SerializeField] Item.Type item; //クリックしたら消す public void OnClickObj() { /*クリックされたらItemBox.csが動作、       アイテムが消え、ItemBoxへ格納される動きになる*/ ItemBox.instance.SetItem(item); gameObject.SetActive(false); } } 【アイテムの実装】 ①アイテムの列挙スクリプト ・アイテム自体は、あくまでプレイヤーが手に入れた「情報」として保持すればよいので、   MonoBehaviourは不要 Item.cs public class Item { //列挙型:種類を列挙 public enum Type { Cube, Ball, } } 【アイテムボックスの実装】 ①パネル ・ヒエラルキーにUIから追加 ・コンポーネントHorizontal Layout Groupを追加(のちに追加する枠の位置関係をこれで調整) ②アイテムが表示される枠 ・ヒエラルキーにUIからイメージを追加 ③ItemBoxのスクリプト ItemBox.cs public class ItemBox : MonoBehaviour { //ItemBoxをOnClickObjで実行するためにstaticなインスタンス public static ItemBox instance; private void Awake() { if (instance == null) { instance = this; } } /*PickUpObjがクリックされたら、スロットにアイテムを入れる   public static ItemBox instanceでどこからでも引っ張れる PickUpObj.csへ持っていける*/ public void SetItem(Item.Type type) { Debug.Log(type); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

強化学習でAIを避難させる #1 -Unity ML-Agents

0. はじめに 初めまして、Yuulisです。今年の3月ぐらいから、Unityの強化学習ライブラリである、ML-Agentsを使って強化学習をして遊んでいる高校生です。 Qiita初投稿ではありますが、ML-Agentsの日本語解説記事を増やすという目的も兼ねて、今回から強化学習でAIに避難行動を学習させていこうと思います。 1. ML-Agents環境を用意する ML-Agentsを使う上で環境構築は重要です。しかし、うまく書ける自信がないので、自分の中で分かりやすかったサイトを貼っておきます。 Unity ML-Agentsのインストールから学習まで また、以下の書籍もかなり参考になりました。誤植が一部ありますが、気づける範囲です。これからML-Agentsを始めたい、という方には是非おすすめです。 Unity ML-Agents 実践ゲームプログラミング v1.1対応版 (Unityではじめる機械学習・強化学習) ※ML-Agentsの最新バージョンは、2021/05/04時点でRelease 17となっていますが、私の環境はUnity 2019.4.23f1, ML-Agents Release 12です。 2. 今回作るもの 初回の今回は、4つの非常口の中から一番近いものに避難するAIを作ることを目標とします。 2-1. 学習環境全般 長方形のエリアの四隅に、非常口を設置します。なお、エリアには壁は存在せず、下に落ちると失敗です。 2-2. エージェントの設定 環境の観察 環境の観察は、Raycast Observationという方法を使います。エージェントの周囲からレーザーを発射し、そのレーザーに当たったものを検知します。 行動 行動を記録するデータ型は、Discreteというものを使います。これは、離散値(0, 1, 2...)の要素を持つ整数配列です。 今回は、1回の行動の配列の長さは1で、配列1つの要素に入り得る要素の数は次の5つです。 数値 行動 0 移動しない 1 前 2 後ろ 3 右回転 4 左回転 ※例えば、[1],[3],[2]というデータがあれば、「前、右回転、後ろ」を意味します。 報酬 生成時に最も近かった非常口に触れれば+1.0。それ以外の場合は+0.3。 エリアから落下したら-0.2。 1行動ごとに-1.0 / MaxStep ※MaxStepというのは、1回の学習で行動できる回数のことです。例えばMaxStep = 5000ならば、エージェントは5000回行動できます。 3. 実装 3-1. ML-AgentsライブラリをUnityに追加 では、いよいよ実装していきます。まずは、ML-AgentsライブラリをUnityに追加します。 Unityの新しいプロジェクト「EvacuationLearning」を作成したら、「ウィンドウ」から「Package Maneger」を選択してください。 Package Manegerの読み込みが終わったら(少し時間がかかる)、左上のボタンから「Add package from disk...」を選択してください。 1. ML-Agents環境を用意する でクローン(ダウンロード)したML-Agents toolkitのディレクトリに移動し、com.unity.ml-agents/package.jsonとcom.unity.ml-agents.extensions/package.jsonを追加します。 パッケージを追加したら、今度は「ファイル」から「ビルド設定」を選択し、左下の「プレイヤー設定」を選択します。 下にスクロールして、「API 互換性レベル」を「.NET 4.x」に変更します。 以上でML-AgentsライブラリをUnityに追加することができました。 3-2. 学習環境の作成 次に、エージェントが学習する環境を作成します。 以下のオブジェクトを追加し、ヒエラルキーの「Transform」タブを以下のように設定してください。 Areaは「Plane」、その他は「Cube」のオブジェクトを使います。 オブジェクト名 座標 角度 サイズ Area (0, 0, 0) (0, 0, 0) (1.5, 1, 3) Agent (0, 0.5, 0) (0, 0, 0) (1, 1, 1) Target1 (-6.5, 0.5, -12.5) (0, 0, 0) (2, 1, 5) Target2 (6.5, 0.5, -12.5) (0, 0, 0) (2, 1, 5) Target3 (6.5, 0.5, 12.5) (0, 0, 0) (2, 1, 5) Target4 (-6.5, 0.5, 12.5) (0, 0, 0) (2, 1, 5) 「Agent」には「Rigidbody」を追加します。以下のように設定してください。 さらに、Agentの向いている方向が分かるよう、顔のパーツも追加します。この辺はお好みです。私は以下のように設定しました。 目は「Cylinder」、口は「Cube」です。 オブジェクト名 座標 回転 サイズ Eye (0.25, 0.2, 0.35) (90, 0, 0) (0.2, 0.2, 0.2) Eye(1) (-0.25, 0.2, 0.35) (90, 0, 0) (0.2, 0.2, 0.2) Mouth (0, -0.2, 0.5) (0, 0, 90) (0.1, 0.5, 0.1) これらのオブジェクトは「Agent」の子オブジェクトにしてください。 次はそれぞれにMaterialを付けます。この辺もお好みです。別にMaterial用のディレクトリを作っておくとよいでしょう。 ちなみに私はBlockoutという無料アセットを使っています。アセットストアからダウンロードできます。 エージェント本体には赤色のマテリアル、エージェントの顔には黒色のマテリアルを適用しました。 それ以外のオブジェクトにはBlockoutのマテリアルを適用。 最後に、「TrainingArea」という空のオブジェクトを追加し、すべてのオブジェクトを子オブジェクト化します。 これで、「TrainingArea」を複製することで並行学習が簡単になりました。 以上で学習環境の作成が完了です。 3-3. 学習のためのスクリプトを作成 今度は、「Agent」に強化学習のスクリプトを適用します。 その前に、今後のために、「Target」にtagを付けておきます。 インスペクターウィンドウの「タグ」を選択し、「タグを追加...」をクリック(画像ではすでに「target」と「obstacle」というタグができていますが無視して結構です)。 以下のような画面になるので、「タグ」の右下の+ボタンをクリックし、新しいタグの名前「target」を入力して「Save」。 Target1~4のそれぞれに「target」タグをつけます。 では、スクリプトを書いていきましょう。 エージェントのインスペクターウィンドウの「コンポーネントを追加」から、新しいスクリプト「AgentControl.cs」を作成。 以下のように書き換えます。 AgentControl.cs using System.Collections; using System.Collections.Generic; using UnityEngine; // ML-Agentsパッケージの追加 using Unity.MLAgents; using Unity.MLAgents.Sensors; using Unity.MLAgents.Actuators; public class AgentControl : Agent // 基底クラスをAgentに変更 { public Transform target1; public Transform target2; public Transform target3; public Transform target4; Rigidbody rBody; private bool onEpisode = false; // エピソード実行中かどうか private string closestTarget; // 生成時に最も近かったターゲット名を格納 // 初期化時 public override void Initialize() { this.rBody = GetComponent<Rigidbody>(); onEpisode = false; } // エピソード開始時 public override void OnEpisodeBegin() { // エージェントの状態をリセット this.rBody.angularVelocity = Vector3.zero; this.rBody.velocity = Vector3.zero; this.transform.rotation = Quaternion.identity; // エリアのランダムな位置に生成 this.transform.localPosition = new Vector3(Random.Range(-6.5f, 6.5f), 0.5f, Random.Range(-14.0f, 14.0f)); // エピソード開始 onEpisode = true; // スポーン時の最近のターゲットを取得 float dis1 = Vector3.Distance(this.transform.localPosition, target1.localPosition); float dis2 = Vector3.Distance(this.transform.localPosition, target2.localPosition); float dis3 = Vector3.Distance(this.transform.localPosition, target3.localPosition); float dis4 = Vector3.Distance(this.transform.localPosition, target4.localPosition); float leastDis = Mathf.Min(dis1, dis2, dis3, dis4); if (leastDis == dis1) closestTarget = "Target1"; if (leastDis == dis2) closestTarget = "Target2"; if (leastDis == dis3) closestTarget = "Target3"; if (leastDis == dis4) closestTarget = "Target4"; } // 行動実行時 size = 5 public override void OnActionReceived(float[] vectorAction) { Vector3 dirToGo = Vector3.zero; Vector3 rotateDir = Vector3.zero; // 離散値ごとの行動設定 int action = (int)vectorAction[0]; if (action == 1) dirToGo = transform.forward; // 前 if (action == 2) dirToGo = transform.forward * -1.0f; // 後ろ if (action == 3) rotateDir = transform.up; // 右 if (action == 4) rotateDir = transform.up * -1.0f; // 左 this.transform.Rotate(rotateDir, Time.deltaTime * 200f); this.rBody.AddForce(dirToGo * 0.6f, ForceMode.VelocityChange); // エリアから落下したとき if (this.transform.localPosition.y < 0) { AddReward(-0.3f); // 罰 EndEpisode(); } // ステップごとの罰 AddReward(-1.0f / MaxStep); } // 目標にぶつかったとき public void OnCollisionEnter(Collision collision) { if (collision.gameObject.tag == "target") { // エピソード中なら if (onEpisode) { // 生成時に最も近かったターゲットなら if (collision.gameObject.name == closestTarget) { AddReward(1.0f); // 最大報酬 EndEpisode(); } // それ以外なら else { AddReward(0.3f); // 小さな報酬 EndEpisode(); } } // エピソード中でなければ else { // ランダムな位置に生成のやり直し this.transform.localPosition = new Vector3(Random.Range(-6.5f, 6.5f), 0.5f, Random.Range(-14.0f, 14.0f)); } } } // 人間が操作するとき public override void Heuristic(float[] actionsOut) { actionsOut[0] = 0; if (Input.GetKey(KeyCode.UpArrow)) actionsOut[0] = 1; if (Input.GetKey(KeyCode.DownArrow)) actionsOut[0] = 2; if (Input.GetKey(KeyCode.RightArrow)) actionsOut[0] = 3; if (Input.GetKey(KeyCode.LeftArrow)) actionsOut[0] = 4; } } 大まかな説明はスクリプト内にコメントしておきました。 では、Agentのインスペクターウィンドウに戻り、設定を行っていきます。 まずは先ほど書いたスクリプトの設定。 Target1~4には、3-2. 学習環境の作成 で作ったTargetをドラッグアンドドロップ。 次に、「Behavior Parameters」という、エージェントの行動にかかわるスクリプトを追加。すでにある場合もありますが、追加されてなければ「コンポーネントを追加」から追加できます。 以下のように設定。 今回はRayCast Observationを使うので、「Vector Observation」の「Space Size」は0にセット。 また、「Discrete Branches」は2-2. エージェントの設定 の行動の欄を参考に。 最後に、エージェントの観察をするためのセンサーを追加します。 「コンポーネントを追加」から、「Ray Perception Sensor 3D」を追加し、以下のように設定します。 今回は、エージェントの周囲360°から16方向にセンサーを発射します。 そして、学習を効率化させるために、並行学習をします。「TrainingArea」を9個ほど複製し、互いの学習環境に影響を与えないよう、四方50ぐらい間をあけます。 こんな感じ。 以上で学習環境の実装は終了です。 4. 強化学習をやってみる 強化学習を始める前に、学習用パラメータの設定をします。 EvacuationLearning\config\base.yamlを新しく作成します。 以下のようなコードを書きます。 base.yaml behaviors: AgentManeger: trainer_type: ppo hyperparameters: batch_size: 128 buffer_size: 2048 learning_rate: 0.0003 beta: 0.005 epsilon: 0.2 lambd: 0.95 num_epoch: 3 learning_rate_schedule: linear network_settings: normalize: true hidden_units: 256 num_layers: 3 vis_encode_type: simple reward_signals: extrinsic: gamma: 0.95 strength: 1.0 max_steps: 1000000 keep_checkpoints: 5 checkpoint_interval: 200000 time_horizon: 64 summary_freq: 10000 threaded: true ここの詳しい説明は書籍にも載っていますが、次回説明しようと思います。 いよいよ学習です! Anaconda Promptを起動し、以下のコマンドを順に実行します。 仮想環境の起動 conda activate (作成した仮想環境名) ※(仮想環境名) >>> となれば成功。 プロジェクトのディレクトリに移動 cd (プロジェクトのディレクトリのパス) 学習開始コマンド mlagents-learn .\config\base.yaml --run-id=1st 1stの部分は重複は許されません。学習ごとに変える必要があります。 ※こんな感じのアスキートアートと、Listening on port (ランダムな値). Start training by pressing the Play button in the Unity Editor. というコードが出たら成功。 ▄▄▄▓▓▓▓ ╓▓▓▓▓▓▓█▓▓▓▓▓ ,▄▄▄m▀▀▀' ,▓▓▓▀▓▓▄ ▓▓▓ ▓▓▌ ▄▓▓▓▀' ▄▓▓▀ ▓▓▓ ▄▄ ▄▄ ,▄▄ ▄▄▄▄ ,▄▄ ▄▓▓▌▄ ▄▄▄ ,▄▄ ▄▓▓▓▀ ▄▓▓▀ ▐▓▓▌ ▓▓▌ ▐▓▓ ▐▓▓▓▀▀▀▓▓▌ ▓▓▓ ▀▓▓▌▀ ^▓▓▌ ╒▓▓▌ ▄▓▓▓▓▓▄▄▄▄▄▄▄▄▓▓▓ ▓▀ ▓▓▌ ▐▓▓ ▐▓▓ ▓▓▓ ▓▓▓ ▓▓▌ ▐▓▓▄ ▓▓▌ ▀▓▓▓▓▀▀▀▀▀▀▀▀▀▀▓▓▄ ▓▓ ▓▓▌ ▐▓▓ ▐▓▓ ▓▓▓ ▓▓▓ ▓▓▌ ▐▓▓▐▓▓ ^█▓▓▓ ▀▓▓▄ ▐▓▓▌ ▓▓▓▓▄▓▓▓▓ ▐▓▓ ▓▓▓ ▓▓▓ ▓▓▓▄ ▓▓▓▓` '▀▓▓▓▄ ^▓▓▓ ▓▓▓ └▀▀▀▀ ▀▀ ^▀▀ `▀▀ `▀▀ '▀▀ ▐▓▓▌ ▀▀▀▀▓▄▄▄ ▓▓▓▓▓▓, ▓▓▓▓▀ `▀█▓▓▓▓▓▓▓▓▓▌ ¬`▀▀▀█▓ Version information: ml-agents: 0.23.0, ml-agents-envs: 0.23.0, Communicator API: 1.3.0, PyTorch: 1.7.0+cu110 2021-05-05 12:47:13 INFO [learn.py:275] run_seed set to 7390 2021-05-05 12:47:14 INFO [environment.py:205] Listening on port 5004. Start training by pressing the Play button in the Unity Editor. このコードが出たら、すぐにUnityの画面に戻って「Play」ボタンを押してください。ある程度時間が経過すると、エラーになって学習開始コマンドの打ち直しになります。 学習中は2021-05-05 12:47:48 INFO [stats.py:139] AgentManeger. Step: 10000. Time Elapsed: 35.025 s. Mean Reward: -0.017. Std of Reward: 0.632. Training. のようなログがプロンプトに表示されます。 今回は100万Step学習することになっているので、1時間程度かかります。 ちなみに、Mean RewardはStepごとの報酬平均、Std of Rewardは標準偏差を表しています。 今回の場合では、前者が1に近づき、後者の値が0.4以下ぐらいになれば成功だといえるでしょう。 私のケースでは、70万Stepあたりで変化がほぼなくなったので、学習を終了しました。 5. 学習結果の確認 学習が終わったら、結果を確認しましょう。 EvacuationLearing\results\1stに、AgentManeger.onnxというファイルができているはずです。 それをUnityのプロジェクトウィンドウにドラッグアンドドロップ。 さらにそれを、「Agent」のヒエラルキーウィンドウの「Behavior Parameters」の「モデル」の部分にアタッチします。 「Play」ボタンを押してみましょう。こんな感じに動きましたか?(さらっとTwitterの宣伝) 報酬グラフや詳しい情報は、以下のコマンドをAnaconda Promptで実行すると得られます。 tensorboard --logdir results すると、localhostのリンクが出てくるので、コピペしてアクセス。 うん、うまい具合に学習できました。 6. 終わりに いかがでしたでしょうか。 強化学習に興味を持っていろいろ調べた結果、UnityのML-Agentsを発見したのですが、やはり解説が少ないです(あったとしてもほぼ同じ内容)。これからこの分野を開拓していきたいと思っています。 初投稿ということもあり、執筆に時間がかかってしまいました。誤字や不明な点があれば、ご指摘ください。 また、この記事を読んで、少しでも強化学習に興味を持っていただけたら幸いです。 次回は、今回のエリアに障害物を置いてみようと思っています。 ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cascadeurで作成したモーションをUnityちゃんで動かしてみた

Cascadeurで作成したアニメーションをUnityChanで動かしてみたので共有です。 結論から言えば、頭と腕の動きが作成したものと違う状態になりましたが、大まかな動きはよさそうです。 Cascadeurとは物理ベースのキャラクターアニメーション用の3Dアニメーションソフトウェアで、 複雑な人の動きを破綻せずに作りやすくしてくれるツールです https://cascadeur.com/ 基本的な使い方はチュートリアルがあるので実例を踏まえながら学ぶことが出来ます。 https://cascadeur.com/learn/how_to_begin 今回は指のモーションも動かしたかったので5本の指がリギングされているUE4MANNEQUINを使用しました。 今回はこちらの作成したアニメーションをUnityに持っていきます 公式が薦めている方法を参考にします。 https://cascadeur.com/help/export_to_unity まずシーンごとエクスポートします 出力されたfbxからアニメーションを取り出します。 アニメーションファイルを選択しEditを押します ModelタブのPreserve Hierarchyを有効に RigタブからAnimationTypeをHumanoid型に変更します。 そしてApplyを押します。 先ほど変更をしたアニメーションファイルを複製します(モーションを編集できるようになります) Windowsだとctrl+Dで複製できます 複製したアニメーションファイルを開き、各項目のBake Into Poseを有効に、加えて各Base UponをOriginalに変更します ここから、モーションを使用するための各設定をします。 Animator Controllerを作成し、モーションを挿入します このままアニメーションを再生すると足が地面についていなかったり、滑るようになってしまうので設定をします。 Animator Controllerに挿入したアニメーションを選択し、InspectorからFoot IKを有効にします 動かしたいモデルを選択し、InspectorのAnimatorからAppy Root Motionを有効にします。 Unityを実行します。 実際に動かすと、Unityちゃんの手の向き(縦拳が横拳になってしまっている)と、首の向きが作成したアニメーションと違いますが、それ以外の動きはよさそうです。 作成したモーション通りに反映ができた方は教えていただけると幸いです。 アニメーションの設定関係で参考にさせてもらった動画 https://youtu.be/rkCYVfA4yaU?t=197 モデルのモーションと位置について参考にさせてもらった動画 https://youtu.be/SGO9MMFHI54?t=319
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】DeepLで文書翻訳してみた

1. はじめに ノベルワークスR&Dチームのユウガです。現在は, バーチャルオフィスの開発を行っています。 バーチャルオフィスの機能の1つとして, DeepL文書翻訳をやってみたので紹介します。 主に新しい技術のリサーチをしているため, 最新のドキュメントはだいたい英語だったりします。「このドキュメント日本語にして読みたいなぁ」と思った時に, すぐに翻訳できると便利です。また, 外国人とチャットでやりとりしていて, ファイルを共有された時に, すぐに母国語に翻訳できると便利ですよね。 DeepLを使えば日本語で作った製品案内とかを瞬時に翻訳できるので, 海外への営業・販売など比較的容易に展開できるようになるかもしれませんね。 所見としては, 文書翻訳はベータ版ではありますが, かなり精度も良くて, 全然使えると思いました。また, 画像が添付されてる場合も, レイアウトが崩れる事なく, 翻訳できた印象です。 環境 ・windows10 ・Unity2020.3.2f.1 2. DeepL文書翻訳 翻訳できるファイル形式は, Word(.docx), PowerPoint(.pptx), Text(.txt), HTML(.html) です。 ファイルサイズ&文字数の制限は, Word(.docx), PowerPoint(.pptx)が, 10MBまたは100万文字。Text(.txt)が, 1MBまたは100万文字。 HTML(.html)が, 5MBまたは100万文字です。 DeepL API プランの場合は, 好きなだけ文書翻訳できるそうです。 このプランは従量課金制で, 1か月の基本料金630円に加え, 1文字あたり0.0025円として料金が請求されます。 注意点としては, 文字数が少ない場合も, 1ファイルにつき最低5万文字分が請求されます。5万文字以上の場合は, 文字数分の料金が請求されます。 なので, 1ファイルにつき最低でも125円かかります. テストしてる時, これに気が付かなくて焦りました(-_-;) 3. 実装 DeeplFileTranslator.cs using UnityEngine; using UnityEngine.Networking; using System; using System.IO; using System.Collections.Generic; using System.Threading.Tasks; public static class DeeplFileTranslator { [Serializable] public class ResponseForUpload { public string document_id; public string document_key; } [Serializable] public class ResponseForCheck { public string document_id; public string status; public string seconds_remaining; public string billed_characters; } private static readonly string auth_key = "XXX"; public static async Task<string> Upload(string path, string filename, string target_lang, string content_type) { // pathにあるファイルを読み込む byte[] rawdata = File.ReadAllBytes(path); List<IMultipartFormSection> requestData = new List<IMultipartFormSection>(); requestData.Add(new MultipartFormDataSection("auth_key",auth_key)); requestData.Add(new MultipartFormDataSection("target_lang",target_lang)); requestData.Add(new MultipartFormFileSection("file", rawdata, filename, content_type)); UnityWebRequest request = UnityWebRequest.Post("https://api.deepl.com/v2/document", requestData); await request.SendWebRequest(); if (request.result == UnityWebRequest.Result.ProtocolError || request.result == UnityWebRequest.Result.ConnectionError) { Debug.LogError(request.error); return "error"; } return request.downloadHandler.text; } public static async Task<string> CheckStatus(string document_id, string document_key) { List<IMultipartFormSection> requestData = new List<IMultipartFormSection>(); requestData.Add(new MultipartFormDataSection("auth_key",auth_key)); requestData.Add(new MultipartFormDataSection("document_key",document_key)); UnityWebRequest request = UnityWebRequest.Post("https://api.deepl.com/v2/document/"+document_id, requestData); await request.SendWebRequest(); if (request.result == UnityWebRequest.Result.ProtocolError || request.result == UnityWebRequest.Result.ConnectionError) { Debug.LogError(request.error); return "error"; } return request.downloadHandler.text; } public static async Task<string> Download(string document_id, string document_key, string output_path) { List<IMultipartFormSection> requestData = new List<IMultipartFormSection>(); requestData.Add(new MultipartFormDataSection("auth_key",auth_key)); requestData.Add(new MultipartFormDataSection("document_key",document_key)); UnityWebRequest request = UnityWebRequest.Post("https://api.deepl.com/v2/document/"+document_id+"/result", requestData); await request.SendWebRequest(); if (request.result == UnityWebRequest.Result.ProtocolError || request.result == UnityWebRequest.Result.ConnectionError) { Debug.LogError(request.error); return "error"; } // output_pathにデータを書き込む byte[] rawdata = request.downloadHandler.data; File.WriteAllBytes(output_path, rawdata); return "success"; } } このコードは見てもらえば分かると思いますが, UnityWebRequestを使って, DeepLとやりとりしてます。 UnityWebRequestでasync/awaitを使いたかったので, こちらのコードも使いました。 auth_keyの所は, ダッシュボードにある Authentication Key を入力してください。 FileTranlateManager.cs using UnityEngine; using UnityEngine.UI; using System; using System.IO; using System.Threading.Tasks; public class FileTranslateManager : MonoBehaviour { [SerializeField] Button fileTranslateButton; private string document_id; private string document_key; void Start() { fileTranslateButton.onClick.AddListener(async () => { string path = @"C:\Users\username\Documetns\test.docx"; string target_lang = "EN-US"; await DeeplFileTranslate(path, target_lang); }); } private async Task DeeplFileTranslate(string path, string target_lang) { string ext = Path.GetExtension(path); string filenameWithoutExt = Path.GetFileNameWithoutExtension(path); string output_path = "C:\\Users\\" + Environment.UserName + "\\Downloads\\" + filenameWithoutExt + "_" + target_lang + ext; string jsonString = null; int millisecond = null; // Deeplにアップロード jsonString = await DeeplFileTranslator.Upload(path, filenameWithoutExt+ext, target_lang, GetContentType(ext)); if (jsonString == "error") return; var responsForUpload = JsonUtility.FromJson<DeeplFileTranslator.ResponseForUpload>(jsonString); document_id = responsForUpload.document_id; document_key = responsForUpload.document_key; // 翻訳状況をチェック while (true) { jsonString = await DeeplFileTranslator.CheckStatus(document_id, document_key); if (jsonString == "error") return; // 翻訳完了なら, ループを抜ける var responseForCheck = JsonUtility.FromJson<DeeplFileTranslator.ResponseForCheck>(jsonString); if (responseForCheck.status == "done") break; if (!String.IsNullOrEmpty(responseForCheck.seconds_remaining)) { millisecond = int.Parse(responseForCheck.seconds_remaining) * 1000; Debug.Log($"{millisecond / 1000}s remaining."); } else { millisecond = 1000; } await Task.Delay(millisecond); // ミリ秒待つ } // Deeplからダウンロード jsonString = await DeeplFileTranslator.Download(document_id, document_key, output_path); if (jsonString == "error") return; // 翻訳完了を知らせるために, ダウンロードフォルダを開く Application.OpenURL("file:C:\\Users\\" + Environment.UserName + "\\Downloads"); document_id = null; document_key = null; return; } private static string GetContentType(string ext) { switch(ext) { case ".docx": return "application/vnd.openxmlformats-officedocument.wordprocessingm"; case ".pptx": return "application/vnd.openxmlformats-officedocument.presentationml.presentation"; case ".txt": return "text/plain"; case ".html": return "text/html"; default: return null; } } } このコードは, DeeplFileTranslatorのメソッドを呼び出して, 処理を実行してくれます。 翻訳状況をチェックの所で, 翻訳完了したらループを抜けるようにしてますが, あんまり良くないなぁと思ってるので, こう書いたら良いよって方がいたら教えてください!! 4. おわりに 個人的には, PDFも翻訳してくれたらありがたいのですが, DeeplAPIでは無理です。 なので, PDFを翻訳したければ, CloudConvertAPIを使って, word(.docx)に変換してから, DeepL翻訳すると良いかもしれません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む