ぷろみん

プログラミング的な内容を扱ってます

オイラー角と空間座標系

概要

複数の環境間での回転のインポート、エクスポートについて調べていたのですが、全然進まないので最小限のアウトプットを目指したメモです。

オイラー

説明はWikipediaに投げます。

重要なのは単にオイラー角といっても12通りの表現方法があることです。

今回の対象環境の1つであるUnityはz-x-y系を採用しています。

Z 座標を軸に z 度、X 座標を軸に x 度、Y 座標を軸に y 度 (順番はこのとおり) 回転する回転

オイラー角については上記座標系とCW(clockwise, 時計回り)とCCW(counterclockwise, 反時計回り)を合わせる事で他空間に同じ回転を適用することができます。
90度回転させると時計回りに回転するので、UnityではCWを使っているようです。

Tait–Bryan angles

航空力学で採用されている形式、 正面にX軸、右側にY軸、下方向にZ軸にそれぞれの正の値が割り当てられます。回転方向はCW。

オイラー角と違って軸の定義まで含まれています。
そして、ここまで厳密ではないと現実世界を定義できないのでジャイロセンサ等の出力はこの形式であることが多いです。

Quaternion

Quaternion Conventions: Hamilton and JPL

QuaternionにはHamiltonとJPL形式があるけど混同したり意識せずに混ぜて使ってしまっていることが多いよって話です。

Convert from JPL to Hamilton

Quaternionからオイラー角への変換

Sensor Fusion

訳語が何になるかは分かりませんが、Sensor Fusionで検索すると参考になるものが色々出てきます。
Sensor Fusion · jherico/OculusRiftHacking Wiki · GitHub

参考

Conversion between quaternions and Euler angles - Wikipedia

中途半端なbit幅の符号付整数を扱い易くするSign extending trick

charじゃ大きすぎる

charは何気に256もの数字を記録できます。
しかし、組み込みのような制限の厳しい環境では1bitでも送受信のデータは節約したいものです。
そんな時はbit演算を駆使してデータをやりとりすることになるのですが、困った状況というものがあります。

例えば、あるセンサーが-10~+10の値を返す場合どうやって値を管理すれば良いのでしょうか?
負の値を表現するには通常2の補数表現が使われますが、それを自分でやるしかないのでしょうか?
実はbit fieldを使えばそんな問題も簡単に解決できるようになります。

bit field

知らない人も多そうですが、データのやり取りの際にUnion等と一緒に使われることが多い機能です。 bit fieldでは指定した変数に割り当てるbit数が指定できます。
極々ローレベルな問題を扱うことが多いCならではの機能といえるでしょう。
書き方としては単純で、クラスや構造体のメンバ変数の最後にコロンと割り当てるbit数を書くだけです。

struct BitField
{
    int a: 3;
};

int main()
{
    BitField bitField = {};
    bitField.a = 0b0000; // 0
    bitField.a = 0b0001; // 1
    bitField.a = 0b0010; // 2
    bitField.a = 0b0011; // 3
    bitField.a = 0b0100; // -4
    bitField.a = 0b0101; // -3
    bitField.a = 0b0110; // -2
    bitField.a = 0b0111; // -1
    bitField.a = 0b1000; // 0
    bitField.a = 0b1001; // 1
    bitField.a = 0b1010; // 2
    bitField.a = 0b1011; // 3
    bitField.a = 0b1100; // -4
    bitField.a = 0b1101; // -3
    bitField.a = 0b1110; // -2
    bitField.a = 0b1111; // -1

これでaには3bit以上の値が入らないようになります。
4bit目以降の数値は無視されていることが分かると思います。
そして、3bit目が補数表現のための負値の役割を果たしていることも分かります。

Sign extending trick

template <typename T, unsigned B>
inline T signextend(const T x)
{
  struct {T x:B;} s;
  return s.x = x;
}

int r = signextend<signed int,5>(x);  // 5bitの符号付整数をintに変換する

まとめ

これで1byteに符号付整数を2つ入れてくるようなセンサーを相手にしても頭を抱えなくて済みますね。
template引数をbit fieldにも使う事ができるって面白かったので紹介してみました。
頭の片隅に置いておくといつか役に立つ日がくるかもしれません。

参考

http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend

C++では典型的なif文は使わない方が良い

よくある間違い

あなたはコンテナから指定のデータを探す時に以下のようなコードを書いていませんか?

for(int i = 0; i < 10; i++)
{
    if(list[i] == 5)
    {
        // do something
        break;
    }
}

正しい書き方

// #include <algorithm>が必要
auto result = std::find(list.cbegin(), list.cend(), 5);
if(result != list.cend())
{
    // do something
}

理由

一見するとC++の知識の必要のない最初のコードの方が読みやすく感じるかもしれません。
しかし、熟練のプログラマでも最初のコードを見ると一瞬これは何のコードだろうか?となります。
これは基本的な構文のみで書かれたコードにありがちな分かり辛さです。
一方、正しい書き方の方はすぐに何をしているか分かります。std::findって書いてあるのですから。

間違った書き方の方では更に悪い事にイテレータ破壊の危険性が高いです。
イテレータ破壊についての詳細は以前書いた記事を参照してください。

std::vectorの正しい使い方 - ぷろみん

しかし、algorithmを使おうとするとまずfindでは機能が足らなくなります。
find_ifになると大体の用途では問題なくなるのですが、今度はラムダを理解する必要が出てきます。
最近のコンパイラならラムダにautoを使えるので、

[](const auto& item){ return false; }

みたいに書いたら大体そのままいけるので大分楽にはなっているんで頑張ってください。スコープだけは意識して欲しいところですが。

キャプチャ内での変数宣言はもう入ったんでしたっけ。アレも出来たら便利ですね。

Unityの新入力システム

Unityの入力システムは汎用性が低い

Unityはマルチプラットホームという利点がありますが、入力システムの汎用性は低いです。
当然同一で処理したいタッチ処理とマウスクリック処理を別々に書かなければいけなかったり、マルチプレイヤーゲームで1プレイヤーはキーボード操作、1プレイヤーはコントローラーといったことをするのに複雑な機構が必要になってしまいます。
また、プレイヤーの状態に合わせて操作を変更したいというのは自然な要望ですが、それにも対応していませんでした。 そこでUnityでは新しい入力システムとしてPlayerInputという機能をβ版で発表しています。

新しい入力システム

新しいインプットシステム、一緒に開発しませんか? – Unity Blog

また、こちらのコードがgithubにも上がっています。

https://github.com/Unity-Technologies/input-prototype

どういった機能なのかは動画を見てもらうのが一番早いかと思います。

youtu.be

今までとは何が違うのか

今まではProjectSettings/InputManager.asset等に保存していたため、プロジェクト全体でグローバルに入力システムを運用する必要がありスコープが広い上に指定も文字列でやらなければいけない等、とても積極的に使いたいシステムではありませんでした。

この新しいシステムではプレイヤーのある状態でのキーマップをActionMapとして作成し、そのアクションマップを状態に合わせて切り替えることでキーコンフィグを動的に切り替えることができるようになります。

これによってコード上では

if(firstPersonControls.fire.isHeld){ ... }

という風に入力システムを意識しない統一的なアクションでコーディングできるようになります。

未来と生きるために

Unityでは難しいことを簡単にするという明確なミッションがあります。なので、VR等の最先端の技術や新しいデバイスの対応をどんどんしていくことになるでしょう。
例えば、このゲームをViveコントローラとOculus Touchに対応して欲しいと言われた際にそれぞれがインハウスで開発するのはいかにも時間の無駄です。
この機能が入れば、そのDeviceProfile機能で入力とアクションをマッピングできるようになるかもしれません。
汎用的な機能が入ればAssetStoreに各デバイスのプロファイラーやマッパーがリリース会社から提供されることが期待できるので信頼性もあがります。

良きデベロッパーのために

Unityはスコープがゆるかったりパブリックにするように設計されていたりで悪いデベロッパーを育てる側面が強いです。
しかし、Unityは新しい機能で確かに問題を切り分けて良い設計ができるようにしています。
この思想にのって、ステートマシンという概念、ステート毎の入力(アクションマップ)、どのスクリプトで入力を取得すべきか。を学習することは良いデベロッパーへの近道に思えてなりません。

Unityのフォルダ構成ベストプラクティス

モチベーション

Unityでは多岐にわたるプロジェクトを考慮してフォルダ関係のサポートは薄く、プロジェクト毎に自由に設定しなければならない。
自由は素晴らしいことだが統一規格がないことは他の人のコードを読む時にもんにゃりする。
そこで、私が良いと思っているフォルダ構成を紹介しようかと思う。

オススメする方法

とりあえず結論から紹介する。

  • Assets直下をAsset Storeのインポート先とする。
  • Assets以下に自身のグループや会社名のフォルダを作り、ここを作業フォルダとする。
  • Assets/MyGroup以下にScenesやScripts、Prefabs等のフォルダを作る。ここは複数のシーンで利用するアセットを格納する。
  • Assets/MyGroup/Scenes以下にシーン名フォルダを作る
  • Assets/MyGroup/Scenes/SceneName以下にScripts、Prefabs等のフォルダを作る。ここはこのシーン専用のアセットを格納する。
  • Assets/MyGroup/Scenes/SceneName/SceneName.unityにシーンファイルを作る。

タグを有効活用すれば、もっと良い方法もありそう。
けど、いちいちアセットを作る度にタグ打つのが私には無理だった。

ダメだった方法

Assets/Pluginsフォルダに全てのAsset Store等の外部アセットを入れる。

上記をすると誤作動するアセットが度々存在したためダメだった。
スクリプトに自身のパスを指定しており、デフォルトインポートパス以外では動作しないアセットが存在する以上デフォルトインポート以外は信用し辛い。
そして多くのデフォルトインポート先はAssets/GroupNameである。これはもう諦めてそれに従った方が良い。
たまにAssets直下に展開しているアセットもあるが、その場合は適当なフォルダを作って詰める。
それで動かなかった場合はスクリプト修正か利用しないかを選択した方が良いと思う。プラグインを簡単に外せる環境構築は大事。
というか、有料アセットが間違ってgithubに上がってたりするのでプラグインマネージャ欲しい。いろんな人が困っているはず。

ダメだった方法2

Assets/Scenesに全てのシーンファイルを詰める。
Assets/Scriptsに全てのスクリプトファイルを詰める。
Assets/Prefabsに全てのPrefabファイルを詰める。

シーンが増えてくるとアセットのスコープが見えなくなってくる。
スコープが見えないということは変更に対する影響範囲が見えなくなるということだ。
つまり、ちょっとシェーダを書き換えたり、ちょっとPrefabを更新したりするだけで他のシーンでは動作しなくなっている可能性がある。
スクリプトのnamespaceもシーン毎に変える。

ちょっと横にそれるが、Unityではスコープの概念がゆるゆるなので色んな所で自ら制限をかけた方が良いかと思う。
例えば私はtransform.parentは使わないようにしている。こうすることで、末端のgameObjectはアクセスできる範囲が狭まり必要以上に多機能になることはないはず。

そもそもがそもそもだった

概要

何故またこっちで書こうと思ったのか。

そもそもの動機

将来性を考えると独自ドメインでブログは運用すべき。
なぜなら資産であるブログ記事を検索サイトに模倣と思われず引っ越す方法がないからだ。

ブログを書くモチベーション

結局は多くの人に見てもらいからだろう。
そうなった時に新しいブログではこのサイトの数%のPVしか付かない。
やる気が湧かないから記事の数も増えないのでPVが増えない。
うーん、バニティメトリクス。

敷居の高さ

記事のクオリティを一定水準まで上げようと思うと、どうしても下書きが増えてしまう。
Qiitaの下書きが10記事までしか保存できないシステムはぐだぐだ言ってねーでさっさと投稿しろ!ってことなんだろうけど、どうも苦手で常に下書きが埋まってしまう。
これは良くないなー、せめて書き切らないとブログを書くモチベーションが保てない。
ということで、こんな感じの頭の中整理みたいな記事も投稿するようにしてみようというチャレンジ。

問題

せっかくなので、現状抱えている問題を列挙しておこう。

こっちは改善案がコメントに色々出ているので対応したいところだが、いかんせんアップデートしようがしまいが改善しようがしまいがアクティブやプレイ、インストール数が変わらないので改善する意欲がわかない。

  • Ghostに付けたい機能が付けられない

twitterのカードサムネに対応したかったけど、やり方が分からなかった。頻繁にアップデートが来るのでもう対応しているのかもしれないが、リリースノートをチェックするのも手間。オープンソースなのでコードを読めば自力で実装もできたかもしれないが、私がブログフレームワークに望んでいることではなかった。
無難にWordPressにした方が良いと思い直したのは良いもののデータ移行のことを考えると面倒になって後回しにしてしまっている。
機能的にははてなブログで十分なんだけど独自ドメイン使おうと思うと月600円。サーバが月500円で借りれることを考えると中々使う気にはなれない。

  • Unityは記事書くの面倒

私は分かりやすく記事を書くのをモットーにしている。何故なら未来の自分へのリファレンスとして書いているからだ。
人は昔のことは忘れるものだ。3年前にやったことを平気で忘れる。そんな時に見返せば当時と同じことが可能になったら素晴らしい。

しかし、Unityの記事を分かりやすく書くのは難しくないが面倒だ。分かりやすくするためには必然的にスクリーンショットを多用する必要があるからだ。

今後の方針

もういっそ、自分のサーバにWordPress上書きインストールで良いかもしれない。せっかくPVが集まってないのだから。

C++で無理矢理クロージャを使ってみる

クロージャ

現在クロージャという表現を使う場合状態を持ったラムダを指す場合が多い気がします。 つまり、ワンライナーで書けるクラスといった感じです。これはC++では関数オブジェクト的な手法で似たようなことができますが、javascript程普通なものではありません。

// javascript
const twice = (action) => { action(); action(); };

const counter = () => {
  let count = 0;
  return () => {
    count++;
    console.log(i);
    return i;
  };  
};

const a = counter();
twice(a); // 1, 2

const b = counter();
twice(b); // 1, 2

C++だと外部ストレージを用いなければできないと思っていましたが、内部ローカル変数を参照でバインドし延命させることでできました。

// C++
auto twice = [](auto action) { action(); action(); };
    
auto counter = [] {
    int count = 0;
    auto increment = [](int& count) { count++; std::cout << count; };
    return std::bind(increment, count);
};
    
auto a = counter();
twice(a); // 1, 2
    
auto b = counter();
twice(b); // 1, 2

応用

これを利用するとfor_each中にコンテキストを保存することができます。
今回は前回のループの値を利用してみました。
素直にローカル変数をキャプチャした方が良いような気もしますが、内部で完結しているのも1つのメリットではないでしょうか。

#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>

namespace {

auto context = [](auto function) {
    auto context = std::make_pair(false, 0);
    auto action = [](decltype(context)& context, auto& function, auto& value) {
        function(value, context);
    };
    return std::bind(action, context, function, std::placeholders::_1);
};

}

int main() {
    std::vector<int> vec = { 5, 10, 15, 20, 25 };
    std::for_each(vec.cbegin(), vec.cend(), context([](auto& value, auto& context) {
        if(context.first) {
            std::cout << value - context.second << std::endl;
        }
        context = std::make_pair(true, value);
    }));
}

参考

クロージャとラムダ式をザックリ解説してみる | No Creativity, No Life!