- 投稿日:2020-02-24T23:38:04+09:00
『Unity C# ガチ初心者用』 for文
自分やガチ始めたばかりの初心者用
テンプレ
//iが5未満の時に起動し、iに1を足す for (int i = 0; i < 5; i++) { //iの値をコンソールに表示する Debug.Log(i); }
int i = 0;
int型のiを作り0を代入
i < 5;
iの値が5より未満の時に中身を起動する
i++
iに1を足すint型のiが5未満の時に中身を起動し、その後iに1を足す
という処理になる
この時iが0の時も処理が行われ、5未満なので4になるまで繰り替えされるので
0,1,2,3,4の5回処理が行われる
数値の種類は変えられる
//INTで使用 //iが5未満の時に起動し、iに1を足す for (int i = 0; i < 5; i++) { //iの値をコンソールに表示する Debug.Log(i); } //floatで使用 //iが10未満の時に起動し、iに2.5fを足す for (float i = 0; i < 10; i += 2.5f) { //iの値をコンソールに表示する Debug.Log(i); }一定の回数などならint型を使用し、
座標などならfloat型を使用する2重で使用
//xが5以下の時に起動し、xに1を足す for (int x = 0; x <= 5; x++) { //yが10以下の時に起動し、yに2.5fを足す for (float y = 0; y <= 10; y += 2.5f) { //iの値をコンソールに表示する Debug.Log(x); //iの値をコンソールに表示する Debug.Log(y); } }処理の流れは
1.一つ目のfor文のxの値が0の時
2.二つ目のfor文の処理がすべて行われる
3.その後一つ目のfor文のxにx++で1が足され
4.再び二つ目のfor文の処理がすべて行われる
5.その後一つ目の(以下略このような流れになる
- 投稿日:2020-02-24T23:38:04+09:00
『Unity C# 個人用メモ』 for文
自分やガチ始めたばかりの初心者用
テンプレ
//iが5未満の時に起動し、iに1を足す for (int i = 0; i < 5; i++) { //iの値をコンソールに表示する Debug.Log(i); }
int i = 0;
int型のiを作り0を代入
i < 5;
iの値が5より未満の時に中身を起動する
i++
iに1を足すint型のiが5未満の時に中身を起動し、その後iに1を足す
という処理になる
この時iが0の時も処理が行われ、5未満なので4になるまで繰り替えされるので
0,1,2,3,4の5回処理が行われる
数値の種類は変えられる
//INTで使用 //iが5未満の時に起動し、iに1を足す for (int i = 0; i < 5; i++) { //iの値をコンソールに表示する Debug.Log(i); } //floatで使用 //iが10未満の時に起動し、iに2.5fを足す for (float i = 0; i < 10; i += 2.5f) { //iの値をコンソールに表示する Debug.Log(i); }一定の回数などならint型を使用し、
座標などならfloat型を使用する2重で使用
//xが5以下の時に起動し、xに1を足す for (int x = 0; x <= 5; x++) { //yが10以下の時に起動し、yに2.5fを足す for (float y = 0; y <= 10; y += 2.5f) { //iの値をコンソールに表示する Debug.Log(x); //iの値をコンソールに表示する Debug.Log(y); } }処理の流れは
1.一つ目のfor文のxの値が0の時
2.二つ目のfor文の処理がすべて行われる
3.その後一つ目のfor文のxにx++で1が足され
4.再び二つ目のfor文の処理がすべて行われる
5.その後一つ目の(以下略このような流れになる
- 投稿日:2020-02-24T21:33:13+09:00
Service Bus triggerでキューの情報をDBに非同期で書き込む(SQL Database)
Azure Storage Tableへの非同期書き込みがうまくいったので、いよいよ本命のSQL Databaseへの非同期書き込みにチャレンジ!
2020/02/24 追記
KeyVaultシークレット情報(接続文字列)を管理させるようにできました。後半に記載。
いつも通り情報があまりないです…C#の知識があればさくっと書けるのかもしれないですが、私にはないので手探りで進めます。
VisualStudioを利用してDB参照する方法は以下のサイトが非常に参考になります!
参考サイト:Azure FunctionsとSQLデータベースを連携する1.Azure Storage Tableへの非同期書き込みと同様にテンプレから作成できるかと思いきや、ない。
ブレードから統合を選択し、出力から新規追加(+新しい出力)しても見当たらない。
2.コーディングの前の下準備(DB作成、テーブル作成)
参考サイトを元にDB作成
クエリエディタを利用create table zodiac_type( id INT IDENTITY(1,1), name nvarchar(20) ) insert into zodiac_type(name) values ('a'); insert into zodiac_type(name) values ('b'); insert into zodiac_type(name) values ('c'); insert into zodiac_type(name) values ('d'); insert into zodiac_type(name) values ('e'); insert into zodiac_type(name) values ('f'); insert into zodiac_type(name) values ('g'); insert into zodiac_type(name) values ('h'); insert into zodiac_type(name) values ('i'); insert into zodiac_type(name) values ('j'); insert into zodiac_type(name) values ('k'); insert into zodiac_type(name) values ('l');3.DBへの書き込み処理は手で書く
1.アプリケーション設定にSQL Databaseの接続文字列を追加
2.コード内で先ほど設定した接続文字列が参照できることを確認
run.csx(一部)var str = Environment.GetEnvironmentVariable("SQLDATABASE_CONNECT"); log.LogInformation($"{str}");3.「Service Busトリガーで起動し、メッセージ内容をDBにINSERTする」処理を加える
Tips. Functionsの.Net Core3.1だとSystem.Data.SqlClientの一部の機能がコンパイルエラーになりました。~2.xに変更することで解消。(それでいいのか・・・)
run.csx(全量)using System; using System.Data.SqlClient; using System.Threading.Tasks; public static void Run(string myQueueItem, ILogger log) { log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem}"); // Get the connection string from app settings and use it to create a connection. var str = Environment.GetEnvironmentVariable("SQLDATABASE_CONNECT"); using (SqlConnection conn = new SqlConnection(str)) { try{ // 接続文字列を利用してSQLDatabaseに接続 conn.Open(); }catch (Exception exc) { log.LogInformation($"connect Error!"); } var text = "select count(*) from [dbo].[zodiac_type];"; using (SqlCommand cmd = new SqlCommand(text, conn)) { // SQLの実行はココ selectなのでExecuteScalarクラスを利用 var rows = cmd.ExecuteScalar(); log.LogInformation($"{rows} rows were updated"); } var queryCommand = "insert into [dbo].[zodiac_type] (name) values ('" + ($"{myQueueItem}") +"')"; log.LogInformation($"{queryCommand}"); using (SqlCommand cmdcmd = new SqlCommand(queryCommand, conn)) { // SQLの実行はココ insertなのでExecuteNonQueryクラスを利用 var rowsrows = cmdcmd.ExecuteNonQuery(); } } }4.Service Busに書き込まれた情報が無事SQL Databaseのnameテーブルに追加されたことを確認
Azureポータル(コンソールログ)2020-02-24T12:27:32.182 [Information] Executing 'Functions.ServiceBusQueueTrigger1' (Reason='New ServiceBus message detected on 'queue01'.', Id=********-e58a-477e-a9e9-1fc248e8f359) 2020-02-24T12:27:32.183 [Information] Trigger Details: MessageId: 589b9068e2a7440886d03930a2ecd770, DeliveryCount: 1, EnqueuedTime: 2/24/2020 12:27:32 PM, LockedUntil: 2/24/2020 12:28:02 PM, SessionId: (null) 2020-02-24T12:27:32.191 [Information] C# ServiceBus queue trigger function processed message: W 2020-02-24T12:27:32.193 [Information] 28 rows were updated 2020-02-24T12:27:32.194 [Information] insert into [dbo].[zodiac_type] (name) values ('W') 2020-02-24T12:27:32.201 [Information] Executed 'Functions.ServiceBusQueueTrigger1' (Succeeded, Id=********-e58a-477e-a9e9-1fc248e8f359)
2020/02/24 追記
接続文字列情報をアプリケーション設定として持っておくとセキュリティ管理責任をアプリケーションも負わなければいけないので、極力KeyVaultに集約管理したい。では、KeyVaultに保管したシークレット情報を読み取る実装に変更してみよう!というお話です。
1.ServiceBusのキューにデータを書き込む関数(HTTPトリガー)の改修
できていないこと できていること FunctionsがKeyVaultから接続文字列を取得して、 ServiceBusのキューにデータを書き込む 現状は、Functionsがアプリケーション設定内にある接続文字列を取得するように統合時に設定
でもこの変数名はコード内に出てこないし、接続処理はどうやってるんだ?と思っていろいろあさっているとfunction.jsonに登録されていた模様
1.マネージドIDの作成と関連付け
ここまでわかればKeyVaultからデータをとってくるのは容易いです。まずはFunctionsでマネージドIDを作成して、KeyVaultのシークレットに一覧・取得を権限付け。詳細は過去記事参照(KeyVaultのシークレット情報をマネージドIDを利用して取得する)
2.シークレット識別子の把握
この時のシークレット情報のURL(シークレット識別子)をメモ
例)ttps://KeyVault名.vault.azure.net/secrets/シークレット名/**********************
3.Key Vault 参照形式に書き換えてメモ帳に用意
参考:App Service と Azure Functions の Key Vault 参照を使用する
参照構文
Key Vault 参照の形式は @Microsoft.KeyVault({referenceString}) です。{referenceString} は次のいずれかのオプションで置換されます。4.一番最初のアプリケーション設定の値欄にKey Vault 参照形式で貼り付け
緑チェックでキーコンテナの参照 になったらOK
例)@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931)
2.ServiceBusのキューからデータを取得する関数(Service Bus trigger)の改修
こちらの関数はServiceBusの接続情報とSQLDatabaseの接続情報を2つ持っているので、変更箇所が2箇所になるだけ。
1.シークレット情報を作成して、シークレット識別子を入手。
2.Key Vault 参照形式に変更して、アプリケーション設定の値を変更。
3.以下のようになればOK
4.最後にもう一回打鍵して、想定した挙動で動くことを確認して終了
- 投稿日:2020-02-24T17:12:58+09:00
SeleniumのChromeDriverで起動済みのブラウザを操作する
はじめに
ブラウザ自動テストのデファクトスタンダードである
Selenium
基本的にSelenium
を使用して組んだプログラムは、自分自身でブラウザを起動して自動操作を開始しますしかし、起動済みのブラウザを自動操作したい!というケースもありますよね?
はたしてChromeDriver
を使って起動済みのブラウザを操作することができるのか?動かせた時の感動は省略
やったこと
ブラウザの起動
今回の手法は、Google Chrome を起動するところにポイントがあります。
コマンドライン引数で-remote-debugging-port=9222
を指定してあげるのです!
DevTools Protocol が有効な状態でブラウザが起動するので、Selenium
がアタッチできるようになるんですね。"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" -remote-debugging-port=9222 --user-data-dir=C:\Temp_ForChrome
--user-data-dir
の指定も忘れずに。
操作対象のブラウザ以外に Google Chrome を起動している場合、
ユーザプロファイルがあるフォルダとして同一パスをSeleniumで指定することができないようです。
私の環境ではタイムアウトエラーになってしまいました。OpenQA.Selenium.WebDriverException: The HTTP request to the remote WebDriver server for URL http://localhost:61065/session timed out after 60 seconds. ---> System.Net.WebException: 要求は中止されました: 操作はタイムアウトになりました。
参考:SeleniumでChromeのユーザープロファイルを指定しつつ同時に自分もChromeを使う方法
ソース
ChromeOptions.DebuggerAddress
にブラウザ起動時に指定したものと同じポート番号を指定します。
ブラウザ起動済みの状態で当プログラムを実行すると、見事に動き始めます。var options = new ChromeOptions { DebuggerAddress = "127.0.0.1:9222" }; using (var driver = new ChromeDriver(options)) { var wait = new WebDriverWait(driver, new TimeSpan(0, 0, 5)); // 任意のブラウザ操作処理 ↓↓↓ driver.Url = "https://www.google.com"; var q = driver.FindElementByName("q"); q.SendKeys("Chromium"); q.Submit(); wait.Until(ExpectedConditions.TitleIs("Chromium - Google 検索")); ((ITakesScreenshot)driver).GetScreenshot().SaveAsFile($"{DateTime.Now.ToString("yyyyMMddHHmmss")}.png"); // 任意のブラウザ操作処理 ↑↑↑ }おまけ
Google Chrome を起動する部分がポイントだったわけです。
ここで、手動ではなく別プログラム経由で起動されたブラウザを自動操作したい場合を考えてみます。
うまいこと-remote-debugging-port
を指定してあげる必要がありますよね?やるとすればこんな感じか。。?
using System.Diagnostics; /// <summary> /// chrome.exe という名前のアセンブリとしてビルドする /// </summary> namespace DummyChrome { class Program { static void Main(string[] args) { var proc = new Process(); proc.StartInfo.FileName = @"C:\Program Files (x86)\Google\Chrome\Application\_chrome.exe"; proc.StartInfo.Arguments = @"-remote-debugging-port=9222 --user-data-dir=C:\Temp_ForChrome"; proc.Start(); } } }オリジナルの
chrome.exe
は_chrome.exe
にリネームしてしまいます。
Google Chrome の通常利用に支障が出るのはご愛敬。
おわりに
参考にしたページ:
Selenium: Attach to an existing Chrome browser with C#参考になりそうでしなかったページ:
Re-using existing browser session in Selenium using C#
Reflection
をつかってprivate
メンバーにアクセスしてるので黒魔術感が否めない。
そもそもやりたいことが違う雰囲気もある。
- 投稿日:2020-02-24T16:13:31+09:00
【C#備忘録】ランダムな値を出力する
※この備忘録は初学者の私が学習した内容を忘れないために書き起こしたものとなります。
①Randomクラスのインスタンスを生成する
using System; public class Program{ public static void Main(){ var random = new Random(); } }上記のように、変数randomにRandomクラスのインスタンスを生成し代入する。
②変数randomを、Nextメソッド、WriteLineメソッドを用いて表示させる
using System; public class Program{ public static void Main(){ var random = new Random(); var number = random.Next(1,25); //1以上25未満の数値を指定 Console.WriteLine("C#を1日に" + number + "時間勉強した"); } } ※「C#を1日に【1~24の中のランダム値】時間勉強した」と出力される注意点
Nextメソッドの引数を指定しないととんでもない桁数の数値からランダムで出力されてしまう。
そのため、Next(〇〇,△△)のように引数を指定するとよい。
また、引数は、〇〇以上、△△未満を意味している。
- 投稿日:2020-02-24T15:58:22+09:00
【Unity】RenderTextureを使ってオブジェクトにアウトラインを付ける
こんにちはっ?八ツ橋まろんです
今回はUnityのアウトラインシェーダー&マテリアルの話です。
Unityで3Dモデルにアウトラインを付ける方法はいくつかありますが、以下の条件をお求めの人におすすめです。
・アウトラインを付けたい対象のオブジェクトのシェーダーは何でもOK
(アウトライン機能のないスタンダードシェーダーとかでもOK)
・アウトラインを極太にしてもヘンな描画にならない(ただし描画コストが上がる)アウトライン描画の概要
こんな感じで、破綻しづらく太いアウトラインができました。
RenderTextureに対してシェーダーで処理をするので、対象の3Dモデルはどんなシェーダーでも良いというのがgoodです✨
(よくあるアウトライン描画の方法では、Stencilを仕込んだり、メッシュを拡張するためににシェーダーをいじる必要があるのですよね)また、Render Textureを後ろに重ねるだけなので、二重や三重にすることもできるので、上の画像の一番右のように3Dモデル→白枠→細い灰色枠→影と言う風に3枚のRender Textureでより豪華な表現ができます。
シェーダーコード
以下、シェーダーコードです。
MaronOutlineShader.csShader "Custom/MaronOutlineShader" { Properties { [NoScaleOffset]_MainTex ("Render Texture", 2D) = "white" {} _SubTex ("Pattern Texture", 2D) = "white" {} _Color ("Main Color", Color) = (1,1,1,1) _Outline ("Outline thickness", Range (0,1)) = 0.1 _DrawNum ("Drawing times", Range (2,32)) = 2 } SubShader { Tags { "RenderType"="Transparent" "Queue" = "Transparent" } LOD 100 ZWrite off Blend srcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; float4 _MainTex_TexelSize; sampler2D _SubTex; float4 _SubTex_ST; float4 _SubTex_TexelSize; float4 _Color; float _Outline; int _DrawNum; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } float2 PolarToCartesian(float r, float theta) { float x = r * cos(theta); float y = r * sin(theta); return float2(x, y); } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); float2 tiling_offset_uv = float2(i.uv.xy * _SubTex_ST *float2(1, _MainTex_TexelSize.x/_MainTex_TexelSize.y) + _SubTex_ST.zw); fixed4 subcol = tex2D(_SubTex, tiling_offset_uv); float2 outline = _Outline*0.1; float PI = 3.14159265358979323; int n = _DrawNum; float theta; for(int j = 0; j < 2 * n; j++) { theta = PI * j / n; col += tex2D(_MainTex, i.uv + PolarToCartesian(outline, theta)*float2(_MainTex_TexelSize.x/_MainTex_TexelSize.y, 1)); } //境界をなめらかに col = smoothstep(0.5, 0.51, col); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); //一定以下のalphaはゼロにする _Color.a *= step(1-col.a, 0.01); return subcol*_Color; } ENDCG } } }このシェーダーのキモは、以下のfor文の部分と、PolarToCartesianと定義した関数部分です。
for文for(int j = 0; j < 2 * n; j++) { theta = PI * j / n; col += tex2D(_MainTex, i.uv + PolarToCartesian(outline, theta)*float2(_MainTex_TexelSize.x/_MainTex_TexelSize.y, 1)); }PolarToCartesian関数float2 PolarToCartesian(float r, float theta) { float x = r * cos(theta); float y = r * sin(theta); return float2(x, y); }for文は2n回だけループしますが、ループのたびにthetaがπ/nだけ変化し、PolarToCartesian関数に代入されます。
そして、PolarToCartesian関数は極座標→直交座標の変換をする関数です。( (r, θ)表記を(x, y)表記に直しています)
rはアウトラインの太さ(ずれの大きさ)です。
つまり、例えばn = 2のとき、π/2 = 90度なので、90度ずつrだけずれた描画が2n = 4回なされて、RenderTextureと同じものが上下左右に描画されます。n = 2なのでX軸Y軸の2軸分ずらしたものを描画しているわけです(下画像:左)
(下方向の影は見づらいけど、脚が下方向に伸びていますね)
n = 2では、ごく短い距離にしないとアウトラインとして成立しないですが、これを、n = 4で影が8つ、n = 8で影が16という風に増やしていくと、太くてもきれいなアウトラインになります。シェーダの核の部分はこんな感じです。
あとは、シェーダーをTransparentにしたり、模様を付けたいとき用にサブのテクスチャを設定できるようにしてtiling, offsetに対応したり、境界部分をsmoothで多少滑らかにしたりという感じです?以上、アウトラインシェーダーの話でしたっ?
もし良ければイイネ押していってくださいね?八ツ橋まろん
https://twitter.com/Maron_Vtuber
Booth(シェーダーも売ってる)
https://maronvtuber.booth.pm/
- 投稿日:2020-02-24T14:18:06+09:00
FinalIKでキャラクターに思いのままにインタラクションさせる方法
使用アセット
Final IK
Final IK
https://assetstore.unity.com/packages/tools/animation/final-ik-14290背景
Fantasy Forest Environment
https://assetstore.unity.com/packages/3d/environments/fantasy/fantasy-forest-environment-34245モデル
京狐
https://booth.pm/ja/items/1826902アニメーション
Magician-Motion Pack
https://assetstore.unity.com/packages/3d/animations/magician-motion-pack-133515インタラクションの参考
Final IKインタラクションのチュートリアル
https://www.youtube.com/watch?v=r5jiZnsDH3M足を接地
Sceneに配置したモデルにFull Body Biped IKを設定
※基本的には自動で設定されるがモノによっては設定が上手くいかないものがある。
床用のレイヤーを追加し、接地させるようのメッシュにレイヤーを設定する。
今回はGroundレイヤーを追加し、地面に設定した。
Grounder Full Body Bipedをモデルにアタッチする。
Grounder Full Body BipedのIKにFull Body Biped IKをアタッチする。
Solverを開き、Layersに接地用レイヤーを設定する。今回はGroundを追加した。
IKだけでオブジェクトに片手を触れて放す
アイドルモーション用に使っていたAnimatorコンポーネントをオフにする
Weight Curvesを追加しTypeはPosition Weightを選ぶ
Curvesは(Time,Value)でKeyを(0, 0) > (0.5, 1) > (1, 0)と設定した。
※0秒から0.5秒にかけて目標座標に到達し、0.5秒から1秒にかけて元に戻るようなカーブです。
インタラクションさせるモデルにInteractionSystemをアタッチする
Full Bodyにはモデルに設定したFull Body Biped IKを設定する
InteractionSystemTestGUIをアタッチする
※ゲームビューにInteractionSystemを実行するためのボタンが追加されます。FinalIKの動作テスト用に同梱されたスクリプトであり、任意のタイミングでインタラクションするにはスクリプトの修正が必要です。
Interaction ObjectにInteraciton Objectをアタッチしたのを設定する
Effectorsを増やしRight Handを選択する
※これで右手にInteractionを適用することができる
IKだけでオブジェクトに両手を触れて放す
上の章(IKだけでオブジェクトに片手を触れて放す)で紹介した工程をしたあとに
InteractionSystemTestGUIのEffectorsを増やし
RightHandとLeftHandを設定するだけです。
IKだけでオブジェクトに片手を触れ続ける
上の章(IKだけでオブジェクトに片手を触れて放す)で紹介した工程をしたあとに
Interaction ObjectのEventsを追加し
Timeを0.5
Pauseにチェックを入れる
動作としてはWeightCurvに従って0.5秒かけてオブジェクトに手を近づけ、
Evetntsで設定されたPauseが呼ばれる動作です
Curvを伸ばし、Timeを調整することでタイミングを調整できます。IKだけでオブジェクトに両手で触れ続ける
上の章(IKだけでオブジェクトに片手を触れ続ける)で紹介した工程をしたあとに
InteractionSystemTestGUIのEffectorsを増やし
RightHandとLeftHandを設定するだけです。
IKだけでオブジェクトを片手で掴む
上の章(IKだけでオブジェクトに片手を触れて放す)で紹介した工程をしたあとに
動かすとこんな感じ
掴むオブジェクトの回転をモデルのボーンに適用する
Interaction ObjectのMultipliersを追加し以下のように設定する
Curve:Position Weight
Multiplier:1
Result:Rotate Bone Weight
動かすとこんな感じ
MultiPliersのCurveはWeight Curveで指定したCurveから選ぶオブジェクトに手の位置を合わせる
モデルを複製し、複製したモデルの手(Hand)をオブジェクトの置きたい座標に移動させる
※この時手が伸びてるように見えるが問題はない
複製した手を置きたいオブジェクトの子階層に配置する
コピーしたモデルは削除してよい
オブジェクト直下に配置したHandにInteractionTargetをアタッチし以下のように設定
EffectType:RightHand
※合わせるのに使用した部位を選ぶ
動かすとこんな感じ
ポジションはあってるが回転があってないのでWeight Curveを追加し、Rotation Weightを追加する
動かすとこんな感じ
オブジェクトに指の位置を合わせる
モデルの合わせたい指がある手(Hand.R)にHandPoserをアタッチ
調整するときはゲームを再生し、IK適用後にオブジェクト直下に配置してるHandの指を動かす
調整したHandオブジェクトをコピーして再生を終了する
コピーしたオブジェクトに置き換える位置によって手首がねじれるのでオブジェクトのInteractionTargetに
Pivot:インタラクションするオブジェクト
※再生中にTwistAxisを調整してコピペして角度合わせるのも有効
Rotate Once:チェックをつけると常に角度が修正されるようになる
手を動かすときのY座標のオフセットを指定する
オブジェクトのInteractionObjectのWeightCurvを追加する
Y座標のオフセットを修正する際にはPosition OffsetYにする
Curvesは(Time,Value)でKeyを(0, 0) > (0.25, 1) > (0.5, 0)と設定した。
※0秒から0.25秒にかけてオフセットのYに到達し、0.25秒から0.5秒にかけて目的座標に戻るようなカーブです。
手を動かすときの腕の伸ばし具合のオフセットを指定する
オブジェクトのInteractionObjectのWeightCurvを追加する
腕の伸ばし具合のオフセットを修正する際にはReachにする
沢山伸ばしたい場合はValueを0に近づける
調整するときは再生してコピペでCurveを設定する
Pauseを0.5としてるので
Curvesは(Time,Value)でKeyを(0, 0) (0.5, 0.08)と設定した。
動かすとこんな感じ
- 投稿日:2020-02-24T13:55:55+09:00
ゼルダの伝説BotWのカメラ研究 with Unity 其の一「基本の動き」
はじめに
- ゼルダの伝説BotWはここでは「ゼルダの伝説 ブレスオブザワイルド」を指します。
- 場合によっては誤りがある場合があります。十分に気を付けてはおりますが、筆者は特に数学が苦手ですのでその部分で誤りがあるやしれません。
- 解説を分かりやすくするために一部初級者向けの内容が含まれています。
- Unityで動きを演出する部分があります。
- 何回かに分けて考察を書いていきます。今回は基本的な動きに目を付けたいと思います。
現在、この項に関して考察は完了していますがそれを実現するための処理は完成していません。予めご了承ください。- →(2020年2月25日追記)処理が完成しました。
Unityの動作のテスト環境
Windows10 Home
Unity 2019.3.0f6
VisualStudio 2019 Professional基本の動き
基本の映し方としては一般的な三人称視点のカメラと変わりはありません。
注目していただきたいのはカメラの回転速度(左右の軸周り)です。
動画を確認された方は「何を言ってるんだ、普通の動きじゃないか」などと思われるかもしれませんが、例えばUnityのRotateAround関数の処理をC#で書き表すとこんな感じになります。
ソースコード
RotateAround.csusing UnityEngine; public class RotateAround : MonoBehaviour { [SerializeField] GameObject parent; float speed; // Start is called before the first frame update void Awake() { speed = 50.0f; } // Update is called once per frame void Update() { float x = Input.GetAxis("RightHorizontal"); float y = Input.GetAxis("RightVirtical"); //初期値 float rad = 2 * Mathf.PI; //RotateAroundと同等の動きをする Vector3 radSpeed = x * transform.right * rad; transform.position += radSpeed * Time.deltaTime; //振動を防ぐためにMathf.Clampは利用していない //振動を防ぐ方法として一定以上もしくは以下になったらそれ以上動きを足さない処理を利用する float eulerAnglesX = transform.eulerAngles.x; if (!((eulerAnglesX < 180 && eulerAnglesX > 89.0f && y > 0) || (eulerAnglesX > 180 && eulerAnglesX < 271.0f && y < 0))) transform.RotateAround(parent.transform.position, transform.right, y * speed * Time.deltaTime); transform.LookAt(parent.transform); } }簡単なコード解説
- 使っているのはXboxController for Windows
- 上下の軸周りはそのままUnityのRotateAround関数を利用しています。
- 動きを分かりやすくするためにLookAt関数を利用しています。
- カメラの角度制限を設けた際の振動を抑える処理を追加しています。
動かすとこうなる
y軸と、基準点とカメラ位置のなす直線がなす角が狭まれば狭まるほど高速に回転を始めることがわかると思います。
(下記の画像と上記の動画で言いたいことがわかっていただけると幸いです。)
なお、下から覗くような形でも同様の現象が確認できます。
考察
まず必要な条件として、カメラが基準となる位置を周回するときに回転数が半径に関わらず常に一定であるということが挙げられるかと思います。
周回する際の半径に関わらず回転数が一定である条件
まず、Unityで動かした方は半径に関わらず速度が一定という条件での動作でした。
これでは半径が短くなることで円周の長さも短くなった場合に一周する速さが変わり、半径によって回転数が変化してしまします。そこで必要になるのが角速度を利用するという考え方です。
そもそも角速度とは
角速度は回転運動の速度を単位時間当たりの角度で表したものになります。
回転運動(円運動)をしている物体の角速度が一定である場合、その物体は等速円運動を行っていると定義できます。結論:ゼルダBotWのカメラを再現するための条件
角速度が一定である運動 = 等速円運動を行わせることでゼルダのカメラの基礎的な部分は再現できる!!
と、条件を定めました。結論が出たうえでの再現処理
RotateAround.cs[SerializeField] GameObject parent; float speed; float distance; //追加 基準オブジェクトとの距離。半径の役割を持つ Vector3 distancePosition; //追加 半径設定用のベクトル // Start is called before the first frame update void Awake() { speed = 50.0f; //半径設定 高さをカメラと同一にすることで距離をそのまま半径として利用できる distancePosition = new Vector3(parent.transform.position.x, transform.position.y, parent.transform.position.z); distance = Vector3.Distance(distancePosition, transform.position); } // Update is called once per frame void Update() { float x = Input.GetAxis("RightHorizontal"); float y = Input.GetAxis("RightVirtical"); //等速円運動をする物体の角速度(rad/s)の公式は 角速度(ベクトル) = 速度 / 半径 もしくは 角速度(ベクトル) = 角度 / 時間 (2π * 半径 / 時間) //今回は角速度(ベクトル) = 2π * 半径 / 時間 を利用する //今回の場合、秒間72°移動する処理である float radSpeedValue = 2 * Mathf.PI * distance / 5.0f; //変更 //RotateAroundと同等の動きをする Vector3 radSpeed = x * transform.right * radSpeedValue; transform.position += radSpeed * Time.deltaTime; //振動を防ぐためにMathf.Clampは利用していない //振動を防ぐ方法として一定以上もしくは以下になったらそれ以上動きを足さない処理を利用する float eulerAnglesX = transform.eulerAngles.x; if (!((eulerAnglesX < 180 && eulerAnglesX > 89.0f && y > 0) || (eulerAnglesX > 180 && eulerAnglesX < 271.0f && y < 0))) transform.RotateAround(parent.transform.position, transform.right, y * speed * Time.deltaTime); transform.LookAt(parent.transform); //半径設定 高さをカメラと同一にすることで距離をそのまま半径として利用できる distancePosition = new Vector3(parent.transform.position.x, transform.position.y, parent.transform.position.z); //y軸となす角度によって距離が変動するため、毎フレーム更新 distance = Vector3.Distance(distancePosition, transform.position); }簡単なコード解説
- Update関数の一部を変更し、半径を設定することで角速度の計算を行えるように変更しました。
- 距離を取得するための変数を追加しました。
関連する記事
まだ書いていません。
- 投稿日:2020-02-24T12:56:27+09:00
Azure Queue Storage triggerでキューの情報をDBに非同期で書き込む(Azure Strage テーブル)
以下のサイトを参考(そのままコピペ)にして、Functionsで受け付けた情報をAzure Storage キューに書き込み出来るようになりました。
Functions を使用して Azure Storage キューにメッセージを追加するちょっとステップアップして、非同期テーブル書き込み処理を実現してみました。
1.HTTPのGETリクエストのパラメータをAzure Storage キューに書き込み
2.Azure Storage キューをトリガーにAzure Storage テーブルに書き込み1.HTTPのGETリクエストのパラメータをAzure Storage キューに書き込み
<参考にした手順はこちら>
Functions を使用して Azure Storage キューにメッセージを追加する1. Azure ポータルから関数アプリ(Functions)を作成し、HTTPトリガーのテンプレートを利用すると自動でコードが作成される
3. 出力から「+新しい出力」をAzure Queue Storageを選択
4. 便利なのでStorage Explorerをインストール(接続するときは接続文字列を使うと便利、ストレージ表示名は接続文字列を入力すると自動補完してくれた
5. HTTP Triger1のコードに以下を追加実行するとHTTPのGETリクエストのクエリパラメータをキューに保存される。
- ブラウザを打鍵した結果
- Storage Explorerの内容
<一部抜粋>
public static async Task Run(HttpRequest req, ICollector outputQueueItem, ILogger log)
outputQueueItem.Add(name);<全量>
run.csx#r "Newtonsoft.Json" using System.Net; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Primitives; using Newtonsoft.Json; public static async Task<IActionResult> Run(HttpRequest req, ICollector<string> outputQueueItem, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); string name = req.Query["name"]; string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); dynamic data = JsonConvert.DeserializeObject(requestBody); name = name ?? data?.name; // パラメーターを使用してキュー メッセージを作成するコードを追加 outputQueueItem.Add(name); return name != null ? (ActionResult)new OkObjectResult($"Hello, {name}") : new BadRequestObjectResult("Please pass a name on the query string or in the request body"); }2.Azure Storage キューをトリガーにAzure Storage テーブルに書き込み
1. 関数(Azure Queue Storage trigger)の追加を行う
※キュー名はINPUTとするトリガーキューの名前なので、先ほどHTTP Trigger1で出力したキュー名を指定する必要有り
2. 統合>出力からAzure Table Storageを選択
3. Storage Explorerからテーブル「outTable」を作成run.csxusing System; public static void Run(string myQueueItem, ICollector<Person> outputTable, ILogger log) { log.LogInformation($"C# Queue trigger function processed: {myQueueItem}"); log.LogInformation($"Adding Person entity {myQueueItem}"); outputTable.Add( new Person() { PartitionKey = "Test", RowKey = $"{myQueueItem}"} ); } public class Person { public string PartitionKey { get; set; } public string RowKey { get; set; } }5. 実行するとHTTPのGETリクエストのクエリパラメータがキューに保存されて、DBに格納される。キューの情報は取り出したら自動で削除してくれるっぽい
- ブラウザを打鍵した結果
- Storage Explorerの内容
- 投稿日:2020-02-24T12:56:27+09:00
Azure Queue Storage triggerで非同期テーブル書き込み
以下のサイトを参考(そのままコピペ)にして、Functionsで受け付けた情報をAzure Storage キューに書き込み出来るようになりました。
Functions を使用して Azure Storage キューにメッセージを追加するちょっとステップアップして、非同期テーブル書き込み処理を実現してみました。
1.HTTPのGETリクエストのパラメータをAzure Storage キューに書き込み
2.Azure Storage キューをトリガーにAzure Storage テーブルに書き込み1.HTTPのGETリクエストのパラメータをAzure Storage キューに書き込み
<参考にした手順はこちら>
Functions を使用して Azure Storage キューにメッセージを追加する1. Azure ポータルから関数アプリ(Functions)を作成し、HTTPトリガーのテンプレートを利用すると自動でコードが作成される
3. 出力から「+新しい出力」をAzure Queue Storageを選択
4. 便利なのでStorage Explorerをインストール(接続するときは接続文字列を使うと便利、ストレージ表示名は接続文字列を入力すると自動補完してくれた
5. HTTP Triger1のコードに以下を追加実行するとHTTPのGETリクエストのクエリパラメータをキューに保存される。
- ブラウザを打鍵した結果
- Storage Explorerの内容
<一部抜粋>
public static async Task Run(HttpRequest req, ICollector outputQueueItem, ILogger log)
outputQueueItem.Add(name);<全量>
run.csx#r "Newtonsoft.Json" using System.Net; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Primitives; using Newtonsoft.Json; public static async Task<IActionResult> Run(HttpRequest req, ICollector<string> outputQueueItem, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); string name = req.Query["name"]; string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); dynamic data = JsonConvert.DeserializeObject(requestBody); name = name ?? data?.name; // パラメーターを使用してキュー メッセージを作成するコードを追加 outputQueueItem.Add(name); return name != null ? (ActionResult)new OkObjectResult($"Hello, {name}") : new BadRequestObjectResult("Please pass a name on the query string or in the request body"); }2.Azure Storage キューをトリガーにAzure Storage テーブルに書き込み
1. 関数(Azure Queue Storage trigger)の追加を行う
※キュー名はINPUTとするトリガーキューの名前なので、先ほどHTTP Trigger1で出力したキュー名を指定する必要有り
2. 統合>出力からAzure Table Storageを選択
3. Storage Explorerからテーブル「outTable」を作成run.csxusing System; public static void Run(string myQueueItem, ICollector<Person> outputTable, ILogger log) { log.LogInformation($"C# Queue trigger function processed: {myQueueItem}"); log.LogInformation($"Adding Person entity {myQueueItem}"); outputTable.Add( new Person() { PartitionKey = "Test", RowKey = $"{myQueueItem}"} ); } public class Person { public string PartitionKey { get; set; } public string RowKey { get; set; } }5. 実行するとHTTPのGETリクエストのクエリパラメータがキューに保存されて、DBに格納される。キューの情報は取り出したら自動で削除してくれるっぽい
- ブラウザを打鍵した結果
- Storage Explorerの内容