2018年11月4日日曜日

cubic bezier における2分法実装の妥当性

本稿は2次元平面における3次ベジェ曲線の特殊なもの「cubic-bezier(キュービック・ベジェ曲線)」につく数値解析手法の妥当性を説明します。

cubic-bezierは以下の通常の3次ベジェ曲線に対して、以下の制約を加えたものです。

  • 始点と終点を(0,0) (1,1)に固定
  •  各制御点のX値の範囲は0~1

詳しくは以下
https://developer.mozilla.org/ja/docs/Web/CSS/timing-function


上記性質によって、X,Yの媒介変数表示は以下の通りとなります。
 X(t) = t^3 + 3 t^2 (1−t) x_3 + 3 t^(1−t) 2 x_2
 Y(t) = t^3 + 3 t^2 (1−t) y_3 + 3 t^(1−t) 2 y_2

特に、x_2 , x_3 は0~1の範囲に限定されます。
また、始点と終点が0~1の範囲に収まっている為、
媒介変数tも0~1の範囲に限定されます。

ここから、X(t)の適当なグラフをみると、単調増加関数の性質が示されています。
もしX(t)がt=0~1において単調増加関数であるならば、2分法を用いて数値解析が可能となります。

X(t)が全域で単調増加関数であることを示すには、
X(t)の導関数の値が、t=0~1で0以上であることを示せればよいです。

めんどくさいので、以下の通りに制御点のx値を置き換えます。
 x_2 = a , x_3 = b

導関数から、判別式を算出します。
X'(t) = 9 a t^2 - 12 a t + 3 a - 9 b t^2 + 6 b t + 3 t^2
(3で割って整理) → ( (3 t^2 + 1) a + 2 t b + t^2 ) - (  4 t a + 3 t^2 b )

b側の値は以下の通り。
 (2 t)b - (3 t^2)b
  • b = 0
    • t = 0~1 : 0
  • 0 < b
    • t = 0 : 0
    • t > 2/3 : プラス
    • t >= 1 : マイナス


a側の値は以下の通り。
 (3 t^2 +1)a - (4 t)a
  • a = 0
    • t = 0~1 : 0
  • 0 < a
    • t = 0 : 0
    • t > 1/3 : プラス
    • t > 1 : マイナス
    • t = 1 : 0


a.b の係数は共に、最終的な係数に掛けられる為、マイナス側が一番大きくなるのは、b=1 , a=1 となる事が分かります。
よって、 a=1 , b=1 として計算した結果が常にプラス側であれば単調増加関数であることが示せます。

判別式 = t^2 - 2 t + 1 ≧ 0
⇒ (t-1)^2 ≧ 0

これは下に凸の放物線で、t=1の時、0となる所が最下点となります。
よって、X(t)の導関数は、t=0~1において常に+側が支配的であることが分かりました。
これによりX(t)はt=0~1内に置いて、単調増加関数であることが示されました。

よって、cubic-bezierにおいて、X(t)は単調増加関数の為、2分法を用いた導出が可能となります。
また、この方法は、X(t)の逆関数を求める方法より計算コストが低くなります。
X(t)の逆関数は指数関数が複数入り、コンピューターにおける指数関数の計算コストが、2分法の探索より高コストとなります。

# 結論
性能面、実装の容易性の観点から、cubic-bezierは2分法を用いて実装するのが合理的と分かりました。

2017年6月10日土曜日

UnityでNPC作りたい_前編

Unityで敵さんのNPCを作るとき、色々方法はあるらしいのですが、今回はQiitaからビヘイビアツリーなる物をEditor上で作れるシステムがあり、それを弄って使おうと思ったらサンプルの構成や各ファイルの意味が理解できなかったので、自分なりにまとめた。
Editor拡張のFrameworkを使ってBehaviour Designer のようなAIシステムを作る。



Unityちゃんのプレハブについて
最初からあるようなやつ(=AIに関係ないやつ)
Transform
位置(X,Y,Z)と姿勢(ピッチ、ヨー、ロール)の情報。

Animator
アクション時のアニメの設定。
(これないと動けなくなる)

Rigidbody
物理エンジンに関わる各係数の情報。

CapsuleCollider
当たり判定の設定。
(これないと床をすり抜けて落ちる)

AIに必要なやつ
ThirdPersonCharacter
動く際のパラメーター。要はステータスみたいなモノ。
スタンダードアセットについてくるスクリプト。
(これないとスポーンした直後にアクションできないので宙に浮いたまま動かなくなる)

ActionController
ハイキックの状態保持やアニメーションを行うために有る。
(これないと走り続けて落下する)

Pown
スポーン地点に戻る為にある?(正直わからない)
(これないと回復しようとする際、相手に向かって走り続ける)

BTreeAction
AIの行動処理があるスクリプト。
(これないとそもそもスポーンした直後にアクションできないので宙に浮いたまま動かなくなる)

BTreeDecoratorFunc
AIの条件式があるスクリプト。
(これないとスポーンした直後にアクションできないので宙に浮いたまま動かなくなる)

BTreeManager
Btreeの操作を行うスクリプト。
(これないとスポーンした直後にアクションできないので宙に浮いたまま動かなくなる)

RunTimeNodeEditor
画面上にNodeEditorを表示するスクリプト。
(これないとゲーム開始時にNodeEditorの動きが見れない)

設定されている各スクリプトの説明
BTreeManager
AIの一番最初に動くやつ。最初はこれを見てね。
こいつはAIの読み込み、構築が主な仕事内容。
構築が終わったらメソッド「Update」で
延々とノードの実行、非実行を行い、AIを動かす。

Start
AIのスタート地点です。
別途指定されているAIファイルへのパスを基に、AIファイルをロードする。
開始ノードを探し、そこからノードの作成を行い、BTreeを構築する。
ちなみに指定されているファイルは「BTreeSample」なので、
そのファイルを消したり書き換えたりすると動きが変わるよ。

CreateNodes
ノードの作成、登録を行う。
「Start」から呼ばれる。
具体的なノード作成は「CreateBehaviorTreeBase」に投げる。
作成は投げ、変数への登録はこいつがする。

ちなみに、登録前に構築したBtreeの全ノードが「Ready」状態か確認しています。
Startノードはまあ開始してるわけですが、それ以外にも「Ready」状態じゃない場合、
「ResetCoroutineStart」が2回呼ばれて、再初期化を試みます。
普通はロード時、全てReady状態なので、たぶんリスポン用ですねこれ。

CreateBehaviorTreeBase
ノードの具体的な作成を行う。
「CreateNodes」から呼ばれる。
Btree特有のプロパティやアクションの設定をしてくれる。

ResetCoroutineStart:Btreeの再構築を行う。
具体的な処理は「ResetCoroutine」に投げる。


ResetCoroutine
最初に停止場所がある。※コルーチン:処理の中断、再開を行えるプログラム構造。
ノードの全削除、トップノードからの再登録を行う。

ChangeColorTask
ノードの実行と非実行を切り替える処理。
なんにしてもノードの実行はこいつが握ってます。
そしてこいつは「Update」で常に呼ばれ、ノードを動かします。

BTreeDecoratorFunc
Btreeのデコレーターノードに適用されるメソッド群がここにあります。
要はAIの行動の条件となる処理が、ここに全てあるわけです。

ここに定義されているメソッドは、エディター上で使用可能で、
逆にここで定義しないと、それはエディター上で使用できません。
つまりここで条件を書き、エディター上で条件分岐ノード(デコレーターノード)として
始めて使えるようになるわけです。

BTreeAction
Btreeのアクションノードに適用されるメソッド群です。
要はAIの行動処理が、全てここにあるわけです。

つまりここで条件を書き、エディター上で行動ノード(アクションノード)として
始めて使えるようになるわけです。

ちなみにダメージを受ける処理もここに有ります。

ActionController
「BTreeAction」で行われるアクション時、
アニメーションを動かしたり、ログを出力しているのがここです。
それ以外のことはしてません。

これを自前のゲームキャラクターに持たせるとなると、これらのスクリプトにあるステータス参照、アニメーション参照、各ロジックを色々弄くりまわす必要が出てくる。

次はパッドコントローラーを通した入力を前提に、NPCを動かすとかしてみる予定。


2017年4月23日日曜日

CentOS7でgmailのメール送信しようとしたら二段階認証でつまづいた

※CentOS7でgmailのメール送信の設定自体はすでにゴロゴロあるので書いてないよ。
※要は二段階認証で引っかかった所以外は見所がないよ。

まとめるとタイトルの通りで、googleアカウントに二段階認証がついてると、postfixでの「sasl_passwd」のパスワードが「アカウントのパスワード」ではダメになります。

詳しくは以下の通りです。ちなみに以下のURLで色々辿るとアプリのパスワードが作れます。それで通せます。
二段階認証の場合に必要な追加の作業

下はいつもの通りのブログです。

前回までのあらすじ
引越し先の借屋でインターネット環境がゴミクズそのものだった。
なのでSoftbank Airでネットに接続だ。
・・・・・・・・・・・・・・・・・・・・・・・・
・・・・・・・・・・・・
・・・・・・
・・・
ソフバンエアはポート転送ができない。
つまり自宅のサーバーが外部ネットワークに公開できない。
マイクラサーバーが立てられない。色々できない。
一応解決方法はあるみたいだけどめんどいし、
結局VPSは借りないといけないので・・・・・・
・・・・・・
・・・
ConoHaのVPSを借りた。

理由は
最重要:VPSである。
重要:初期費用無し。
重要:最安プランで千円切る。
おまけ:ConoHachanKAWAII

ていうかVPSでさくらに対抗できるのこの子以外見当たらなかった・・・
初期費用0円はすごいし、最安プランでも十分に良い。

とゆーわけで
Redmine立てた。最近のRedmineはコマンド5つ書くだけでいいんだね。
でもRedmineからメール飛ばないと不便だにゃあ。
CentOS7系で、postfixのみでメール送信したーい!
なんか大前提となる知識が圧倒的不足していたのでメモ。

知識
■SMTP
メール送信のプロトコル名です。
要は「こう言う方式で信号が送られてきたらメールと解釈して処理してね」
と言うメール送信時の規約です。
メールを送信・転送するためのプロトコル。受信とかできない。
※メール受信したいなら、POP3とか言うメール受信のプロトコルで動いているソフトウェアを別途で入れてね

■postfix(ポストフィックス)
簡単にいうと:
 メールを送信するためのソフトウェア。受信とかできない。
 本当に送信だけ。でも転送もできるよ。
難しくいうと:
 SMTPサーバーとなるソフトウェア。

■どうやってメールが送られる?
①SMTPに則った信号(要はメール)をネットに垂れ流そうとします。
②メールサーバ(postfix)がメールに乗っている宛先のドメインをDNSに聞きます。
③DNSから帰ってきたIPアドレスの場所に送られます。
要はIPアドレスさえ分かればメール送れる。
一応"@"以降に"[999.999.999.999]"って感じで指定すれば届く。
でもIPアドレスを特定するのは果てしなくめんどいのでやめとこう。


やりたいこと
メールを送信したい。

嫌なことが起きたこと
でもpostfixでテキトーに設定し、
"root@asanes.jp"とかいう感じの架空のアドレスを送信元アドレスにして
gmailに送信すると、1回は受け取ってくれるが
「おめーのメール、送信元のドメインがDNSに無いからスパムな」
と言われ、一定時間gmail側で完全に拒否られた。
1回は届くけど、スパムあんど一定時間完全拒否とかいうコンボ。
まあ当たり前よな。

多分gmail以外でもそんな感じだろうし何よりRedmineで受け取る先は
gmailの予定だからスパム扱いは困る。

要はメールのドメインを取得すればいいんだけど、月100円も嫌です。
ConoHachanは月630円でにゃんにゃんできるからいいけど。

じゃあ、gmail名義で出せばいいじゃない
とゆーわけで自分の持ってるgmailのアドレスでメールを送信します。
送信元のアドレスがDNSにあればいいんだよ!

とゆーわけでgmailのアドレスを送信元のアドレスにするのですが
「承認してねーよ、詐称してんじゃねーよ」
怒られる。そりゃそうだ。

仕方ないので承認しまーす。
sasl_passwdを設定してー。
承認しましたー。

「ユーザーとパスワードがあってねえよ」
はぁ?
いや正しいんですけど間違いなく。どういうことなの。
そういやなんかURLあるなこのログ。

二段階認証の場合に必要な追加の作業

アプリ用のパスワード登録して、それを書いたら通った。
二段階認証おそるべし。

2017年3月16日木曜日

コマンドプロンプトでgroongaのまとめ


groongaさん日本生まれの英国仕様なのでドキュメントが日本人向けでないです。でもgroongaさんはDBだそうなので、DB的に使ってみようとした際のTipsです。ちなみにコマンドプロンプトです。以下cmdです。

OS:Win7
Ver:groonga7.0.0_x64[zip版]
前提:コマンドプロンプトでgroonga.exeがあるフォルダにcdコマンドで移動済み。

DB作成、削除
DB作成
めんどくさいのでgroongaと同じフォルダにDBを作ります。注意点として、groonga.exeを実行したcmdでやってはいけません。通常のcmdで、groonga.exeのフォルダまで移動して打ってね。
groonga -n --encoding sjis ./[テーブル名]
これを打つとDB作成と同時にDBにアクセスします。
ちなみにsjisでエンコードしてないと(cmdのエンコがsjisの拡張版cp932だから)cmdで日本語が扱えません。英国生まれなのでそんなことドキュメントに体系的に書いて無いので最初から日本語使えなくて即死します。日本生まれのメリットを初手から潰してくるgroongaさんまじぱねっす。
DB削除
消し方はあれです。なんかファイル作られてるでしょ。それをゴミ箱にぽいして。更新日付順に並び替えば上から4個くらいがなんか作られてるのでそれを捨てて。

DBへのアクセス
注意点として、groongaexeを実行したcmdでやってはいけません。通常のcmdで、groonga.exeのフォルダまで移動して打ってね。
groonga ./【テーブル名】
はい、DBにアクセスします。以降のTipsはDBにアクセスした状態で打ってね!!
テーブルの作成、削除
テーブルの作成
はい、普通のテーブルの作り方です。
table_create --name 【テーブル名】 --flags TABLE_HASH_KEY --key_type 【型】
型ってなんですか?って感じだけど、groongaのテーブル作成は非常に特異です。テーブルを作成する際、主キーとなるカラムと一緒に作ります。それ以降のカラムは別のコマンドで随時追加していく感じになります。SQL的に言うと以下の感じを1行でぶち込みます。
Create Table 【テーブル名】 {
 _id int64 autoincrement
,_key 【型】 not null primary_key
}
ちなみに_idとか_keyとか言うカラム名は固定でしかも付いてきます。くそですね。一応_idとか取れるらしいが通常はあっても無くても極限性能でなければ人間の感じ取れる性能差は変わらないので気にしなくていいらしいです。

あと型は色々あります。以下を参照。
データの種類
テータの型
カラムの追加
下の感じです。
column_create --table 【テーブル名】 --name 【カラム名】 --flags COLUMN_SCALAR --type 【型】
ちなみにカラムに配列を指定することもできます
column_create --table 【テーブル名】 --name 【カラム名】 --flags COLUMN_VECTOR --type 【型】
完全にAlterTableのColumnAddのノリですね。

あと型は色々あります。以下を参照。(2度目)
データの種類
テータの型

テーブルの削除
削除するテーブルを参照してる他のテーブルが無ければこれで消せます。
table_remove 【テーブル名】
消せない場合は消しちゃうと色々影響でかいという事なのでがんばって影響されるテーブルを探して順次依存を解消していこうね。

レコードの登録、更新、削除
いよいよ更新系クエリですね。

登録と更新は以下のとおり。ん?更新も兼ねているのかって?そうだけど。
load --table [テーブル名]
[
{【カラム名】:【値】}
]
SQL的に言うとINSERT … ON DUPLICATE KEY UPDATEですね。ちなみに更新は主キー、つまり_keyの値で判断してるよ。ただのInsertは無いよ。

複数のカラムに値を入れるとか、複数のレコードを一気に登録したい場合
load --table [テーブル名]
[
 {【カラム名】:【値】,【カラム名】:【値】}
,{【カラム名】:【値】,【カラム名】:【値】}
,{【カラム名】:【値】,【カラム名】:【値】}
]
主キーに適当な文字列を入れたい場合は以下のような感じ
load --table 【テーブル名】
[
 {_key:"いろは"}
,{_key:"にーほ"}
,{_key:"へいと"}
]

削除は以下のとおり
delete --table 【テーブル名】 --filter "【Where句】"
ちなみに主キー直指定で1個ずつ消したりもできる。

消してぇ・・・・全消ししてえよ・・・・!!
って方は以下のトランケートで
truncate --table 【テーブル名】

レコード検索
ただWhere句がまたしても癖の塊なのでドキュメントを参考にしながら組み立てるといいです。
select --table 【テーブル名】 --filter "【Where句】"
ちなみにLimitOffset句もある。
select --table 【テーブル名】 --offset 【数値】 --limit 【数値】


おまけ
エスケープとか、Windowsのパスとか、日本のお値段としてよく使う「\」なんだけど、バグでエスケープがドキュメント通りにいきません。
・「"」でクオート時、「\"」「\\」を中に入れると「\"」「\\」が出る⇒エスケープもそのまま出す。
・「\'」「\(」を中に入れると「'」「(」が出る⇒エスケープされた文字が出る。
ちなみに以下の文字列をloadコマンドで登録すると
load --table DataObj
[
{_key:"\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z\\1\2\3\4\5\6\7\8\9\0"}
]
"a\bcde\fghijklm\nopq\rs\t\u0000xyz\\1234567890"
エスケープ込みの特殊文字入れすぎでひどい。