投げる!変化球
どうも、もにょさんです。今年度のアドベントカレンダーはお初。23日分の記事のつもりですが投稿が24日になってしまいましたね悲しい。
ゲーム創作研究部のアドベントカレンダーなのにゲーム創作を研究してない!との声を部の内部からよく聞くので、それっぽいことでも書いておきましょう。
今年度の高専祭では狐っ娘がわちゃわちゃする某方Projectっぽい弾幕STGを展示したのでそれに纏わるアレコレを少々。そんな専門的なことはやりません。数学徒とか物理学徒にマサカリ投げられちゃうヤバイヤバイ。
投げる!
さてさて弾幕STGなんで自機も敵も弾を撃たないとお話にならないねぇというわけです。変化球を投げる前には直球を知らなくてはいけません。だったらどうやって弾を動かしたらいいんでしょ。
今回は無能で怠惰で役立たずな白狐の深草すすきさんにお越しいただいて、色々と見てみましょう。
とりあえず飛ぶ弾
彼女の居る位置、すなわち弾の発射される位置をここでは仮に点(0,0)としておきます*1。
まず画面に弾を表示してあげるには、その弾がどこにあるかが分からないといけません。
弾には「位置」の情報を持たせてあげます。2Dゲームなんで難いこと考えずにX座標とY座標があればいいですね。2次元ベクトルで表して、まだ弾は動いていないので点(0,0)に居ます。
次はその座標を動かします。図中の矢印のような、「速度」の情報が要りますね。
1フレーム(60fpsなら1/60秒)あたりに動かしたいX軸方向の距離が2ドット、Y軸方向の距離が3ドットだったら、速度ベクトルは(2,3)です。あとは弾の座標に対して、この速度を毎フレーム足していけばいい話です。
移動をさせていくと(0,0)に居た弾は、1フレーム目は(2,3)、2フレーム目は(4,6)と徐々に動いて、1秒後である60フレーム目には(120,180)に居るはずです。
撃ちたい方向に撃ちたい速度で
でも「X軸方向とY軸方向の速さ」とか言われても分かりづらくはありませんか?
人がボールを投げる時に投げたい方向を向くように、「角度*2」と「速さ*3」で弾を撃たせるほうがいいんじゃなくて?
というわけで「角度」と「速さ」でもって弾を撃たせます。
三角関数のsinとcosを使えば、角度と速さからX軸方向とY軸方向の速さにすることができます。人が弾の動きを設定するような箇所では角度と速さを入力させ、実際に弾を動かす時に速度ベクトルに変換して、弾を動かせばいいわけです。
速くなる弾
発射されてから徐々に速くなっていく、もしくは発射時は速くても徐々に遅くなる弾です。
どれだけずつ速くなっていくか、もしくは遅くなっていくかの加速度*4というものを設定します。
毎フレーム、今の速さに加速度の値を足していく処理をさせれば速さの変化する弾の出来上がりです。発射時の速さを1、加速度を0.1とすると、1秒後には速さが7になりますね。
さてここで問題になるのが、このままでは際限なく速くなっていくということです。ゲームは楽しむものであるので弾速の速い弾を撃ちまくればいいというものではありません*5。速さに上限下限を設けておきましょう。発射から速くなるor遅くなって一定の速さになります。
曲がる弾
いくら速度変化があっても、これでは真っ直ぐにしか進みません。弾が曲がることによって見た目のバリエーションも増えるほか、難易度も上げることが出来ます。出た!初心者キラーだ!
どれだけずつ角度が変化するかの角速度*6というものを設定します。毎フレーム、今の角度に角速度の値を足していく処理をさせれば角度の変化する弾の出来上がりです。
加速度とは違ってどれだけ角度が増えても360°ぐるっと回るだけなのですが、その変化量に気をつけなくてはなりません。例えば角速度を1°毎フレームとした場合、360°回るのに6秒しかかからないので弾速が遅い場合、ずっと画面内を回り続けるなんて状況になります。画面内に弾が溜まり過ぎる(大爆笑)と処理が重くなるので、そうなる時は角速度を小さくする、速さを上げる、もしくは加速度をつけて徐々に速さを上げましょう。
画面端で跳ね返る弾
出た!初心者キラーその2だ!
上下の壁に当たった場合(弾の位置が画面上端よりも上に行ったor下端より下に行った)や、左右の壁に当たった場合(弾の位置が画面左端よりも左に行ったor右端より右に行った)に、ちょうど反射するような軌道を描かせます。
上下壁なら速度のY成分を、左右壁ならX成分を逆符号にしてやれば跳ね返りますね。角度でいえば上下壁なら角度を逆符号に、左右壁なら逆符号に180°足したら跳ね返ります。
自機は画面下側をうろちょろしていることが多く、下を除いた3つの壁でのみ跳ね返るようにしている場合もあります。また、そのままでは跳ね返り続けて画面外へ出て行かず、処理が重くなるばかりか難易度が跳ね上がってしまうので、反射する回数に制限を設けてあげると良いでしょう。
おわり
他にも「自機方向に向かって撃たれる弾」「重力加速度のかかる弾」「指定した速さと角速度で極座標系における動径と偏角が変化する弾」「反射した後に自機方向に向かうよう反射する弾」なんていうのもゲーム内には登場しますが記事が長くなるのでこのへんにしておきます。ここでは弾幕STGでしたが別なジャンルのゲームにも流用できるでしょう多分。
実際このゲーム、高専祭で展示しててまともにクリアしてる人を見たことがないので難易度むずかしいでノーコンクリアくらいしたらご一報ください*7。部公式サイトでゲームは配布中です♥
Modern C++ Programming
こんばんは.誰に投げるか決めてなかったために2日連続です,N@Nです.つらいです.また,C++の話です.
アブストラクト
一般に用いられるC++コンパイラ(gcc及びClang)は当然のことながら最新のC++にどんどんと対応していく.
最新のC++コンパイラのC++11,C++14対応に関しては以下を参照すると良いだろう.
コンパイラの実装状況 - cpprefjp - C++ Library Reference
C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)
Clang - C++1z, C++14, C++11 and C++98 Status
これを見る限りgccについてもClangについてもC++11の対応は完了したと言って良いだろう.但し,C++14対応はgccの方がClangに後れを取っている,と言った感じだ.
C++11によってC++は大幅に進化した.コンパイラの対応状況から言ってもC++11の機能は可能な限り使えるべきだろう.
C++11の全ての機能については書籍であるC++ポケットリファレンスや,Webサイトでもリファレンスがいくらでも転がっているため,ここでは特に主要な機能に焦点を当てたいと思う.
また,各機能においても詳細な議論をすると,それだけで一記事が埋まってしまうので詳細は他の記事を参照されたい.
ちなみに2014年12月19日現在コンパイラの最新バージョンは
- gcc 4.9.2 (2014/10/30 Released)
- Clang 3.5 (2014/09/04 Released)
だ.ところでMSVCは2015でかなりの対応を見せるとのことだったが果たしてどうなるのであろうか.
C++11
前述の通り,C++11の機能は最早完全に実装されたと言っても良いほどだ.
C++11の中でも特に用いることが多いであろう機能を以下に記そう.
型推論
いわゆる型推論がC++11より導入された(C++03以前にもautoというキーワードはあったが最早過去のものだ).
auto a = 0; // a => int auto b = 4.0; // b => double auto c; // ill-formed
また,gcc 4.9.0-において-std=gnu++1y
オプションを指定することにより,関数の引数にも型推論を使用することができる.
ヌルポインタ
これまでC++におけるNULLとは単なるint型の0であるマクロに過ぎなかった.
#define NULL 0
これがC++03までのNULLだ.
しかし,C++11で導入されたnullptrはnullptr_t型の実装となった.これによってオーバーロード時の不具合を避けることができる.
ところで/usr/local/include/c++/4.9.0/i686-pc-linux-gnu/bits/c++config.h
を見てみよう.
#if __cplusplus >= 201103L typedef decltype(nullptr) nullptr_t; #endif
とある.また,/usr/local/include/c++/4.9.0/ext/type_traits.h
には
#if __cplusplus >= 201103L inline bool __is_null_pointer(std::nullptr_t) { return true; } #endif
とある.
配列
実装に関しては昨日の記事でなんとか読もうとしたものがあるので,大した参考にならないとは思うが参照する方は参照されると良い.
C++03までの配列と違い,C++11のstd::arrayはstd::vectorなどのコンテナと同様のイテレータを持つ配列だ.
range based for
いわゆるforeachだ.std::vectorなどのコンテナに対し,煩わしいコードを書かずに済む.ループカウンタのような本来不要な変数をなくすことにもなる.
std::vector<int> vec = {0, 1, 2}; for(auto const& v : vec) { std::cout << v << " "; } // => 0 1 2
スマートポインタ
C++03までは,動的なメモリの確保にnew
,delete
を用いていた.うっかりdelete
し忘れることによってメモリリークを起こした方も多いのではないか.
また,new
をしていないポインタに対するdelete
や,delete
したポインタに対するdelete
は未定義であった.
スマートポインタはそれを解決する.
shared_ptr<type>
とunique_ptr<type>
についてのおおまかな違いは
shared_ptr<type>
はメモリの所有権を共有できるが,unique_ptr<type>
は唯一であるshared_ptr<type>
はコピーが可能であるが,unique_ptr<type>
は所有権の移動に限られる.
他,deleterの指定ができるなどの機能がある.
std::shared_ptr<int> sptr1; std::shared_ptr<int> sptr2; sptr2 = sptr1; // OK std::unique_ptr<int> uptr1; std::unique_ptr<int> uptr2; uptr2 = uptr1; // ill-formed
コンパイル時定数
constexpr宣言された変数はコンパイル時変数となる.
constは単なる定数であるオブジェクトであり,初期化子が定数式でなければ定数式にはならなかった.
しかし,constexprは宣言した変数を明示的にコンパイル時定数としてくれる.可能な限り定数にはconstexpr宣言をするべきだ.
// C++03 #define INF 10e5 // bad const int INF = 10e5; // good // C++11 constexpr int INF = 10e5;
ちなみにだが,gcc 4.8.2(恐らくはそれ以前もだ)において
struct X { int n; }; int main() { const X x = {10}; int a[x.n] = {1}; }
はx.nがコンパイル時定数でないためill-formedだった(当然constexpr宣言すれば問題はない).しかし,同様のコードをgcc 4.9.0-で実行するとconstであってもコンパイルは通っていた.
この辺り仕様変更があったのだろうが,詳細はネットの海にあるだろう.
ところで,C++11のconstexprには大幅な制限があった.詳細はボレロ村上氏の資料が詳しいので参照されたい.
ラムダ式
ラムダについての詳細な議論は計算機科学の分野なのでここでは一部言及するに止めよう.
ラムダ式はいわゆる無名関数というやつだ.ラムダ計算において,
f(x) = y // 有名(f)関数 λx.y // 無名関数
となる.
C++においてラムダ式は関数オブジェクトであり,それをローカルに定義することができる.
与えられたint型の引数の2乗を返すプログラムをラムダ式を用いたものとそうでないもので記述してみよう.
constexpr int square(int n) { return n * n; } int n = 3; std::cout << square(n) << std::endl; // => 9
auto square = [](int n){ return n * n; }; std::cout << square(3);
短く記述できる関数オブジェクトであり,また,使用する場所と定義する場所が近くなるのでコードの可読性も上昇する.ラムダ式の記述は極めて容易で
[] // キャプチャ () // 引数 {} // ステートメント () // 関数呼び出し式(定義だけなら不要) ;
となる.また,引数は省略可能である.そのため例えばラムダ式を用いたHello, World!は,
[]{ std::cout << "Hello, World!" << std::endl; }();
と記述できる.
初期化子リスト
ユーザー定義クラスで一度に初期化することができる.例えば,
std::vector<int> v = {1, 2, 3, 4};
だ.
右辺値参照
右辺値参照はサイズの大きなオブジェクトのコピーに役立つ.右辺値とは無名の一時オブジェクトだ.
右辺値参照は&&
を用いて参照する.
// C++03 type a; type& aRef = a; // C++11 type a; type&& aRef = a;
enum class
enum classは強い型付けとスコープを持ったenumだ.
暗黙の型変換や名前の衝突を避ける事に役立つ.
enum class name : type { enumrator1, enumrator2 = value, enumrator3, ... , }
C++14
ここで,その機能の一部を提示しよう.
戻り値の型推論
return文から戻り値の型を推論するようになった.但し,複数のreturn文がある場合,型が一致している必要がある.
auto f(auto n) { return n * n; } std::cout << f(3) << " " << f(2.2); // => 9 4.84
ジェネリックラムダ式
C++14において,ラムダ式の引数の型を明確にする必要がなくなった.
auto add = [](auto x, auto y) { return x + y; }; std::cout << add(3, 2.2); // => 5.2
コンパイル時定数の制限緩和
などの制限があったがこれらが撤廃された.但し,Clangしか対応していないのが現状である.
C++14におけるconstexprはより柔軟な表現を手に入れた.
C++14についてもC++11のもの同様ボレロ村上氏の資料が詳しいので参照されたい.
C++14 時代の constexpr プログラミング作法 - ボレロ村上 - ENiyGmaA Code
2進数リテラル
2進数だ.但し浮動小数点数は使えないので注意が必要である.
int x = 0b100; // 4 int y = 0B0011; // 3
終わりに
C++はC++11で大幅な進化を遂げ,C++14ではそれを更に強化する形となった.
C++11,C++14ではC++03では煩雑にならざるを得なかったコードも簡潔に書くことができることが多くなった.
C++11,C++14の機能を用いないレガシーなコードは可読性も低く,実装の手間も大きい.積極的にC++11,C++14の機能を用いるべきである.
明日……と言いたいが,日付を超えてしまったので,今日の分をクック君にお願いします.
std::arrayを頑張って読もうとした.
おはようございます.N@Nです.スペード君がstd::array諸々の話をしていたので今回はstd::arrayについて少し話してみようかと思います.
僕のC++力ではどうなるのかわからない部分もあったため,ご教授くださる方が入ればぜひ…….また,予め断っておきますが,本記事は誤りが含まれている可能性が非常に高いのでお気をつけ下さい.
では早速,STLの1つであるstd::arrayのコードの一部を以下に記そう(インデントを揃えるため,一部半角スペースのみを追加した.また,これより上にも意味のある(__array_traits
)部分があるが後述する).
template<typename _Tp, std::size_t _Nm> struct array { typedef _Tp value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* iterator; typedef const value_type* const_iterator; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; typedef std::reverse_iterator<iterator> reverse_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
「ウゥーッ!」(突如泡を吹き倒れる僕)
実装がそんなに膨大でない(コメントを除くと300行に満たない)std::arrayですら全てを読もうとすると,残念ながらかなり時間が掛かってしまうので軽く概略だけ触れるとしよう.
以下の文において,std::arrayはstd::arrayの実装そのもの(コード自身)を指し,arrayは配列(boost::arrayなどではなくstd::array)を指すとする.
また,必要ならば適宜std::array - cppreference.comを参照されたい.
参照しているstd::arrayはgcc 4.9.1のものである.
C++ template
ところでこのコードを読むためにはC++のtemplateの知識が必要だ. そのため,templateに関する知識が一切無い方のためtemplateに関して少し記述するとしよう. templateとは大雑把に言えば「型に依らず」「コンパイル時処理」を行う機能である.
ここでtemplateを用いず,2つの引数の値を加算する関数を見てみよう.
double add(double a, double b) { return a + b; }
特に何の変哲もないコードだ(gcc 4.9.0-の場合,関数の引数にautoが使用できるがここではそれに触れない). しかしこれはdoubleという型に関数が束縛されている.これをtemplateを用いて記述してみよう.すると
template<typename T> T add(T a, T b) { return a + b; }
このようになる.比較してみればtemplate未経験者でも読むことができると思う.引数の型が両方共intならばint型の関数となるし,doubleならばdouble型の関数だ.
typename T
とあるようにT
が型となる.
std::array
さて,先ほどのstd::arrayのコードを見てみよう.
template<typename _Tp, std::size_t _Nm> struct array {
型が_Tp
で,サイズが_Nm
のarrayというtemplateクラスを生成していることが分かる(templateの記述の後にstructとあるためクラスである).続きの
typedef _Tp value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* iterator; typedef const value_type* const_iterator; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; typedef std::reverse_iterator<iterator> reverse_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
はtypedefを用いて再定義しているだけなのでこの部分についての説明は省略する.更に下にいくと,
// Support for zero-sized arrays mandatory. typedef _GLIBCXX_STD_C::__array_traits<_Tp, _Nm> _AT_Type; typename _AT_Type::_Type _M_elems;
コメントを読む限り,サイズが0のarrayに対してのサポートだと思うが,よく分からなかった.
ここで__array_traits
というものが現れている.__array_traits
は最初に記した意味のある部分に相当するものだったので以下に記す.
namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_CONTAINER template<typename _Tp, std::size_t _Nm> struct __array_traits { typedef _Tp _Type[_Nm]; static constexpr _Tp& _S_ref(const _Type& __t, std::size_t __n) noexcept { return const_cast<_Tp&>(__t[__n]); } }; template<typename _Tp> struct __array_traits<_Tp, 0> { struct _Type { }; static constexpr _Tp& _S_ref(const _Type&, std::size_t) noexcept { return *static_cast<_Tp*>(nullptr); } };
このコードとコメントを挟んで,先程のstruct array
が書かれていた.
traitとはa particular quality in your personality*1であり,即ち宣言したarrayの特徴である.
struct __array_traits
はtypedefにより型[サイズ]の形に定義している.そしてその下の関数_S_ref
で(constexpr宣言されているためコンパイル時定数である)型とサイズを,返す(と思う).
struct__array_traits<_Tp, 0>
は宣言されたarrayのサイズが0だった場合で,これはnullptrを返すのだろうか(arrayのサイズが0でもwell-formedだ).
ともかく,一度struct __array_traits
を見たおかげで,本記事でその前に提示した_AT_Type
の意味はarrayの型とサイズであると推測できる.というわけで次を見ていこう.
関数
// No explicit construct/copy/destroy for aggregate type. // DR 776. void fill(const value_type& __u) { std::fill_n(begin(), size(), __u); } void swap(array& __other) noexcept(noexcept(swap(std::declval<_Tp&>(), std::declval<_Tp&>()))) { std::swap_ranges(begin(), end(), __other.begin()); }
fill(const value_type& __u)
は引数の値でarrayを満たす関数だ.
swap(array& __other)
は引数のarrayと値を全て交換する関数だ.これは型とサイズが一致している必要がある.
ここを見る限りやはり_Tp&
は型とサイズで良さそうだ.
また,begin()
,size()
,end()
などはイテレータであり,それはstd::arrayの続きに記してあった.
イテレータ
// Iterators. iterator begin() noexcept { return iterator(data()); } const_iterator begin() const noexcept { return const_iterator(data()); } iterator end() noexcept { return iterator(data() + _Nm); } const_iterator end() const noexcept { return const_iterator(data() + _Nm); } reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } reverse_iterator rend() noexcept { return reverse_iterator(begin()); } const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } const_iterator cbegin() const noexcept { return const_iterator(data()); } const_iterator cend() const noexcept { return const_iterator(data() + _Nm); } const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); }
まあ妥当といったところだ.普通の関数,const修飾した関数と並んでいるが,これに関しては数も多く単純であるため特に解説する必要も無いと思うので続けていこう.
サイズに関するもの
// Capacity. constexpr size_type size() const noexcept { return _Nm; } constexpr size_type max_size() const noexcept { return _Nm; } constexpr bool empty() const noexcept { return size() == 0; }
イテレータの次はサイズに関するものが記されてあった.
arrayのサイズを返すsize()
arrayの実装上の最大のサイズを返すmax_size()
arrayが空かどうかを判定するempty()
全てサイズに関するものだ.この部分は全てconstexpr宣言されておりコンパイル時定数となっている他,普通の関数と何ら変わりない.では次を見ていこう.以下からは要素へのアクセスだ.そろそろ半分だろうか.
アクセッサ
// Element access. reference operator[](size_type __n) noexcept { return _AT_Type::_S_ref(_M_elems, __n); } constexpr const_reference operator[](size_type __n) const noexcept { return _AT_Type::_S_ref(_M_elems, __n); }
array[n]
のようにアクセスする単純なアクセッサだ.const修飾してようと大して差はない,コンパイル時定数であるだけだ.そういえば結構前に_AT_Type
や_M_elems
が現れていたので続きに進む前にもう1度戻ってみよう.
template<typename _Tp, std::size_t _Nm> struct __array_traits { typedef _Tp _Type[_Nm]; static constexpr _Tp& _S_ref(const _Type& __t, std::size_t __n) noexcept { return const_cast<_Tp&>(__t[__n]); } };
typedef _GLIBCXX_STD_C::__array_traits<_Tp, _Nm> _AT_Type; typename _AT_Type::_Type _M_elems;
array[n]
のようにアクセスした場合返ってくるのはarrayの型でもサイズでもなくarrayのn-1番目に格納されている値である.サイズnに対し,n(以降)番目の要素にアクセスしようとするとそのアドレスの値を返す.
つまり,_AT_Type::_S_ref(_M_elems, __n)
はarrayの__n
番目の要素ということになる.
となると,_M_elems
はarrayの要素だろうか.array自身と言うべきなのだろうか.次に進もう.
reference at(size_type __n) { if (__n >= _Nm) std::__throw_out_of_range_fmt(__N("array::at: __n (which is %zu) " ">= _Nm (which is %zu)"), __n, _Nm); return _AT_Type::_S_ref(_M_elems, __n); } constexpr const_reference at(size_type __n) const { // Result of conditional expression must be an lvalue so use // boolean ? lvalue : (throw-expr, lvalue) return __n < _Nm ? _AT_Type::_S_ref(_M_elems, __n) : (std::__throw_out_of_range_fmt(__N("array::at: __n (which is %zu) " ">= _Nm (which is %zu)"), __n, _Nm), _AT_Type::_S_ref(_M_elems, 0)); }
arrayはn-1番目の要素にアクセスする方法として,array[n]
とarray.at(n)
がある.これは後者だ.サイズnに対し,n(以降)番目の要素にアクセスしようとするとプログラムが中断する.
constexpr宣言された方はif文ではなく,三項演算子を用いている.C++14においてはconstexpr宣言した関数内での条件分岐の使用が可能となっているが,C++11ではそうではない.そのため,gcc 4.9.2(未確認)やそれ以降のコンパイラにおいてはif文が用いられているのかもしれない.
さて,参考程度に次の
std::array<int, 3> a = {0, 1, 2}; std::cout << a[10] << std::endl;
を実行した場合,684321288が出力された.また,以下の
std::array<int, 3> a = {0, 1, 2}; std::cout << a.at(10) << std::endl;
を実行した場合,
terminate called after throwing an instance of 'std::out_of_range' what(): array::at: __n (which is 10) >= _Nm (which is 3)
となった.実装と照らし合わせればこの辺りはよく分かるだろう.さあ,続きだ.
reference front() noexcept { return *begin(); } constexpr const_reference front() const noexcept { return _AT_Type::_S_ref(_M_elems, 0); } reference back() noexcept { return _Nm ? *(end() - 1) : *end(); } constexpr const_reference back() const noexcept { return _Nm ? _AT_Type::_S_ref(_M_elems, _Nm - 1) : _AT_Type::_S_ref(_M_elems, 0); } pointer data() noexcept { return std::__addressof(_AT_Type::_S_ref(_M_elems, 0)); } const_pointer data() const noexcept { return std::__addressof(_AT_Type::_S_ref(_M_elems, 0)); } };
begin()
やend()
はイテレータであって,普段プログラムを書くとき,要素にアクセスするために使う関数ではない.
そのような場合は,front()
やback()
を使う.
data()
はC++11で追加された機能だ.この辺りはC++のリファレンスが詳しいのでそちらに譲る,こっちは次へと進もう.
演算子
// Array comparisons. template<typename _Tp, std::size_t _Nm> inline bool operator==(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two) { return std::equal(__one.begin(), __one.end(), __two.begin()); } template<typename _Tp, std::size_t _Nm> inline bool operator!=(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two) { return !(__one == __two); } template<typename _Tp, std::size_t _Nm> inline bool operator<(const array<_Tp, _Nm>& __a, const array<_Tp, _Nm>& __b) { return std::lexicographical_compare(__a.begin(), __a.end(), __b.begin(), __b.end()); } template<typename _Tp, std::size_t _Nm> inline bool operator>(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two) { return __two < __one; } template<typename _Tp, std::size_t _Nm> inline bool operator<=(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two) { return !(__one > __two); } template<typename _Tp, std::size_t _Nm> inline bool operator>=(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two) { return !(__one < __two); }
見て分かる通り,演算子の定義だ.定義されている演算子は==
,!=
,<
,>
,<=
,>=
であるがそれぞれ比較するarrayの型とサイズが等しい必要がある.
そういえばいつだったか<=
,>=
は小さい(resp. 大きい)の否定(大きい(resp. 小さい)か等しい)であると聞いた記憶があるがその通りの実装である.
ところで<
の実装だけstd::lexicographical_compareを用いた比較となっているが、これは<
の実装を用いて>
,<=
,>=
を実装しているためである.
とりあえず次に進もう.ゴールはもう近い.
特殊な関数
// Specialized algorithms. template<typename _Tp, std::size_t _Nm> inline void swap(array<_Tp, _Nm>& __one, array<_Tp, _Nm>& __two) noexcept(noexcept(__one.swap(__two))) { __one.swap(__two); } template<std::size_t _Int, typename _Tp, std::size_t _Nm> constexpr _Tp& get(array<_Tp, _Nm>& __arr) noexcept { static_assert(_Int < _Nm, "index is out of bounds"); return _GLIBCXX_STD_C::__array_traits<_Tp, _Nm>:: _S_ref(__arr._M_elems, _Int); } template<std::size_t _Int, typename _Tp, std::size_t _Nm> constexpr _Tp&& get(array<_Tp, _Nm>&& __arr) noexcept { static_assert(_Int < _Nm, "index is out of bounds"); return std::move(get<_Int>(__arr)); } template<std::size_t _Int, typename _Tp, std::size_t _Nm> constexpr const _Tp& get(const array<_Tp, _Nm>& __arr) noexcept { static_assert(_Int < _Nm, "index is out of bounds"); return _GLIBCXX_STD_C::__array_traits<_Tp, _Nm>:: _S_ref(__arr._M_elems, _Int); } _GLIBCXX_END_NAMESPACE_CONTAINER } // namespace std
以上でようやく長かったstdに属する部分が終了だ.最後は特殊な関数だ.
さて,またswap()
が出てきた.とりあえず2つを並べてみよう.
void
swap(array& __other)
noexcept(noexcept(swap(std::declval<_Tp&>(), std::declval<_Tp&>())))
{ std::swap_ranges(begin(), end(), __other.begin()); }
template<typename _Tp, std::size_t _Nm> inline void swap(array<_Tp, _Nm>& __one, array<_Tp, _Nm>& __two) noexcept(noexcept(__one.swap(__two))) { __one.swap(__two); }
何の事はない,唯のstd::swapだ.つまり,
std::array<int, 3> a = {0, 1, 2}; std::array<int, 3> b = {3, 4, 5}; a.swap(b); std::swap(a, b);
こうして2つの値たちは元通りだ.
さてget()
に関しても述べたいところではあるがそろそろ文書が長くなりダレてくるので省略しよう.閲覧者にはリファレンスがあるんだ.さあ,もうすぐ終わりだ.
namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION // Tuple interface to class template array. /// tuple_size template<typename _Tp> class tuple_size; template<typename _Tp, std::size_t _Nm> struct tuple_size<_GLIBCXX_STD_C::array<_Tp, _Nm>> : public integral_constant<std::size_t, _Nm> { }; /// tuple_element template<std::size_t _Int, typename _Tp> class tuple_element; template<std::size_t _Int, typename _Tp, std::size_t _Nm> struct tuple_element<_Int, _GLIBCXX_STD_C::array<_Tp, _Nm>> { static_assert(_Int < _Nm, "index is out of bounds"); typedef _Tp type; }; _GLIBCXX_END_NAMESPACE_VERSION } // namespace std
arrayのtemplateクラスのためのタプルインターフェースだ.std::tupleはstd::arrayをincludeしているため,どちらかと言うと,arrayのtemplateクラス「による」タプルインターフェースというべきな気もするが……どうなのだろうか…….
残念ながらこれに関して私ははっきり言えるほどの理解力も知識もない.
これ以外の部分はファイルのincludeや定義なのでこれでstd::arrayの実装を見終えた,とする.
終わりに
長い.思ったより長かった.疲れました.
実際,複数のヘッダファイルをincludeしてるからまだ続きはあるけど……キリがないので終わりとします.
実装を見ることで自身のtemplate力,C++力の低さを実感できたしいい機会だったと思います.
SAIの領域選択の話するよ
どうもごめんなさい。1日遅れの雷軌です。 最近あんまりペンタブに触ってないなりにもSAIの話をしたいと思います。 具体的に言えば、領域選択の使い方の話。 実は某視覚的な媒体を研究する部で、部長の絵の修正とかをさせられた事が何度かあるんですが…… ベタ塗りが線画からはみ出てたり、影の塗りだけはみ出てたりとまあ散々だったわけですよ。 マジックワンド使えよ。 でも、SAIにはマジックワンド以外にも便利な選択の仕方はあるんですよって話です。 (矩形選択、投げ縄選択の話はありません)
マジックワンド
と言いつつマジックワンドの話しちゃう。 SAIを使ってたら多分一番使う選択ツール。特定レイヤーや、キャンパスにある、囲まれた領域内を選択するツールです。 領域の検出の方法が何種類かあるけど、「透明部分(精密)」がオススメ。「色差が範囲内の部分」は、微妙な濃さの部分を調整して選択できる。でもそれより特定の色が塗られている部分だけを選択できるのが売り。グラデーションの一部分だけを選択する事も出来ちゃいます。(いつ使うのかは疑問だけど)
左上半分が透明部分選択、右下半分が色差が範囲内の部分選択。画像サイズにもよりますが、色差の範囲が±100以上あれば大体の塗りはごまかせます。
選択ペン/選択消しゴム
マジックワンドと合わせて使う事が多いツール。「マジックワンドで選択したいのに線が繋がってない!」って時に使う事が多いと思います。
しかしその本領は、他の選択ツール方法ではできない、濃度のある選択ができる事。 普通のペンツールと同じように、ブラシ濃度を下げれば透明度を付けることが出来るし、ある程度までならグラデーションだって作れちゃいます。 この濃度のある選択を 使えば、画像の一部だけをぼんやりと抜き取ったり、ちょっと変わった塗りかたが出来るようになります。
主に光の表現などで広く色んな使い道ができるので試してみましょう。(服の皺や、ハイライトなど)
不透明部分を選択
そしてもう1つ、とっても大事な選択の仕方があるんです。それが、レイヤーの不透明部分を選択する方法。
この選択の仕方はとっても簡単。Ctrlキーを押しながらレイヤーパネルのサムネイル部分をクリックするだけ!
なんとこれだけで、元のレイヤーの不透明度と同じ不透明度の選択領域を作ってくれます。 (これを知らかった当時は元になるレイヤーをコピーして不透明度保護して使ってましたね……) 複数のレイヤーの不透明部分を選択したい場合は、1つのレイヤーセットに入れて、レイヤーセットのアイコン部分をクリックすれば選択できます。 (これを知らなかった当時はいくつものレイヤーをコピーして以下略)
例えば、線画を描いた後、塗る前に仮色を塗っておけば、そのレイヤーを使って選択すればレイヤーを切り替えたりせずに、すぐに選択領域を変えることが出来ます。 他にも、一度塗ったものと同じ不透明度で別の色を塗りたい!ってときにも役に立ちますね。もちろん、選択ペンや消しゴムで微調整するのも良いでしょう。
と、いうわけで、SAIを使うなら覚えておいて損はない領域選択のお話でした。他の有料ペイントツールは使っていませんが、おそらくSAI特有のものだと思いますので、これを機にSAIの使い方をじっくり調べてみるのも良いかもしれませんね。
TRPG雑記
おはこんばんちは。ねぎとろです。今回はアナログなゲームのお話です。今まで私がTRPGで遊んできた感想などを徒然なるままに書き連ねていきたいと思います。ゆるーい企画だなぁ・・・。
一応TRPGとは何ぞやということについて説明しておきます。一言で言うと、「紙やサイコロなどを用い、プレイヤー同士が会話しつつ進めていく卓上ゲーム」というゲームジャンルです。「Call of Cthulhu」、「汝は人狼なりや」、「パラノイア」等々、無数のタイトルがあり、それぞれルールが異なっています。TRPGは役割演技(Role-Playing)と試合(game)の2つの側面があり、作品によって役割演技の側面が強いものと試合の面が強いものに分かれています。例えば、「汝は人狼なりや」では勝敗がはっきりと決められており、プレイヤーも積極的に勝利を求めていかなければならないので、試合の側面が強いゲームです。一方では「Call of Cthulhu」のように勝敗の定義が無く、役割演技の側面が強いものもあります。まあGCCの面子なら大体知ってますよね。
以下に僕がこれまでプレイしたTRPGを紹介していきます。詳しいルールは載せてないので興味があれば調べてみてね。
このゲームは、1900代年前半に活躍したアメリカの作家、Howard Phillips Lovecraft氏の著作およびその2次創作から構成される「クトゥルフ神話体系」の世界観から生み出されたゲームです。参加者はキーパー(以下KP)とプレイヤー(PL)に分かれ、KPが用意したシナリオに沿ってPLがロールプレイする形でゲームを進行させます。分かりやすく、アレンジもしやすいルールなのでTRPGの中でも人気のタイトルです。邪神のもたらす災いに巻き込まれたPLは、物理的・精神的ダメージを極力回避しつつ無事にお家まで帰ることがゲームの目的です。
このタイトルの最大の強みは、アレンジのしやすさです。
もともとこのゲームは1920年代アメリカを舞台に恐怖映画的な雰囲気の世界を体験するという内容でした。しかし多少のアレンジでSF的な要素の強いシナリオを作ることもできますし、和風な妖怪譚の世界や、明るくファンシーな雰囲気を表現することもできます。つまり、クトゥルフの基本的なルールさえ知っていれば自分の好きな世界観でシナリオを作り、ロールプレイを楽しむことが可能なのです。明石高専を舞台に高専生を探索者としたシナリオを作ることもできますし、某狐っ娘STGの世界観も表現できてしまえるような許容力がクトゥルフにはあります。妄想夢が広がりますね!
動画サイトにもクトゥルフTRPGのリプレイ(風)動画が多く挙げられています。少し紹介します。
http://www.nicovideo.jp/watch/sm18402821
↑オカルト警察官とミリタリー探偵が事件を解決するお話。絵がきれいで丁寧に分かりやすく作られているので、クトゥルフをやったことのない人でも楽しめる動画だと思います。
http://www.nicovideo.jp/watch/sm20390167
↑通称喀血卓。話のテンポがめっちゃ良いです。間隙なく挟まれる小ネタ、ボケとツッコミの応酬、荒れるダイスなど、終始ふざけているようで話の本筋はブレないのがこの動画の凄いところ。
http://www.nicovideo.jp/watch/sm22829080
↑忙しい人に。
・キルデスビジネス
河嶋陶一朗氏が開発した汎用RPGルール、「サイコロ・フィクション」を使用したTRPGです。この「サイコロ・フィクション」が優れもので、この汎用ルールでは6面サイコロ2つのみでゲームをプレイすることができます。その上「キルデスビジネス」のルールブックがとっても安価(\1620)なので、初期投資2000円未満ですぐにプレイすることができます。
ゲームの設定は、地獄の殺し合いTV番組に招待されたプレイヤー達が、何でも願い事を叶えてもらうことを条件にターゲットの抹殺を試みるというお話です。戦闘は運の要素が強いものの、簡単な駆け引きを気軽に楽しめます。サイコロを振って決められたシチュエーションに対して大喜利的にロールプレイをしなければならないので、発想の瞬発力が問われます。その大喜利の積み重ねでキャラクターが出来上がっていくのも魅力の一つです。ルールは割合キッチリ決まっていてKPの裁量に頼る部分が少ないため、ルールをよく理解した者同士ならKP抜きでプレイすることも可能です。
何気にキャラメイクの自由度が高く、聞いた話によると「産卵をしたい鮭」などでもプレイできます。趣味全開のキャラを作って大暴れさせてやりましょう。
・シノビガミ こちらも「サイコロ・フィクション」を使用したTRPG。プレイヤーは現代に生きる忍者の末裔となって、秘密の任務を遂行します。この「秘密」がこのゲームのミソで、積極的にロールプレイをし、他のプレイヤーの秘密を知ることでゲームを有利に進めていくことができます。すごろく感覚でさらっとプレイできるところが魅力です。
パラノイアは完璧なゲームであり、幸せと楽しさは市民としての当然の義務です。
このゲームの目的は、上司から与えられた任務を達成するために仲間を騙し賺し、収賄や靴をなめることによって上司に媚を売り、仲間の言動の揚げ足を取って言いがかりをつけ殺害し、最終的には出来るだけ自分に責任が降りかからないような形で任務を失敗することです。
そのあまりにも奇抜なゲーム性からTRPG界隈ではとてもよくネタにされて愛されています。
ちなみにパラノイアのルールは公式でネット上に公開されています。↓
http://paranoia.newgamesorder.jp/misc/101
http://ch.nicovideo.jp/yanwalee/blomaga/ar130282
・ソードワールド2.0 クトゥルフと並んで日本では人気の高いTRPGです。中世ヨーロッパ風ファンタジーな世界観が魅力で、「剣と魔法の世界」のお約束が詰まっています。これもKPとPLに分かれてシナリオをプレイするゲームです。戦闘に関するルールが細かく決まっており、より戦略的なロールプレイが求められます。私は一回しかプレイしたことが無いので、あまり詳しいことは知りません・・・。
長々と書きましたが、ざっとした紹介はこんなものです。ロールプレイは、自分のやりたいことを相手に伝える、つまり、「日本語を話すトレーニング」にもなるので、おすすめです。GCCでセッションしたいなぁ・・・。
明日は雷軌に書いてもらおうかな?Slackのグループに居ないけど。
シングルボードPCのススメ
どうも皆さん、おはこんばんちは。クモハです。前Raspberry Piの紹介をしましたが、今回はその他のシングルボードPCで気になったものの紹介もしたいと思います。流石に「調べて下さい(丸投げ)」はひどいと思ったので戻ってきました。あと僕も興味があるので、調べたことを出来る限り吐き出そうかと思います。
pcDuino
※筆者の使用経験はありません
pcDuinoが目を引くポイントは以下の3点です。
- メモリ
- CPU
- 搭載しているOS
まずこのpcDuinoがRaspberry Piとどう違うかという点ですが、なんといってもメモリの大きさです。1GBです。Raspberry Piの2倍です、すごい!
そしてCPU。こちらもグレードアップが図られています。AllWinner A20というチップで、このメーカーのチップは安価なAndroidタブレットでも使われていたりします。
この2点だけで欲しくなってしまいます。僕も欲しいです。
最後に搭載OSについて。こいつはUbuntuとAndroid、Androidを搭載しています。Androidです。pcDuinoの対応言語はC、C++、AndroidSDKを使ってのJava、Pythonで、公式にAndroidアプリ開発がサポートされています。つまり、作ったアプリをその場でデバッグすることが出来るのです。しかも今流行りの「USB OTG(USBホスト機能)」に使えるUSB-microBコネクタを搭載しているので、これを利用したアプリのデバッグなんかもサポートしてくれます、もはや神ですね。
まあこいつのスペック自体が、Android搭載であるという点から見れば2年前の性能であり、最新の端末でシステムリソースをゴリゴリ食って動かすようなアプリを作ってデバッグするのは少し厳しいかもしれません。それを考えても、Raspberry Piの性能からは大きな進歩です。あとRaspberry Piに同じく低消費電力なので、サーバー用途にも使えますね。
でも、お高いんでしょ?
前「Raspberry Piこそコスパ最高」と言い切ってしまいましたが、pcDuinoのコスパの方が優れているかもしれないと今日調べていて気づきました…(震え)。このスペック差で、最安6480円です(秋月電子通商にて)。お金を貯めて買おうかなと迷い始めました。
Raspberry Piとどっちがいい?
ここまでpcDuinoのいいところばかり紹介してきましたが、実際どっちがいいのか。
答えは「やはり利用スタイルによる」です。GPIOの数はRaspberry Piの方が多いので、大量のGPIOを使って電子工作したい方はRaspberry Piを使ってあげてください。また「この世界に入ったばかりでよくわからない」というあなた、Raspberry Piは日本語のドキュメントが多いので始めやすいと思います。Raspberry Piでこの世界に飛び込みましょう。
「スペック高いほうがいいに決まってるだろ」というスペック厨のあなた、pcDuinoを使ってあげてください。また、pcDuinoのGPIOピンはかの有名なマイコンボード「Arduino」のピン配置に似せてあります。Arduinoシールドを使った開発をしたい方、興味がある方は是非pcDuinoを使ってあげてください。そして日本語のドキュメントを増やして下さい!!
こんなのもあるよという紹介
Intel系のSBC
実はIntelもこの界隈に参入していて、Galileoという名前でボードを売っています。特徴はx86アーキテクチャを採用していることです。Intel以外はほとんどARMアーキテクチャという後発のアーキテクチャを採用しています(スマートフォンなどもこれです)。x86アーキテクチャを採用する利点は、普通のPC用OSをインストールすることができることです。もっとも、スペックはそんなに高くないのでちゃんと最適化されたOSを選ぶ必要がありますが、ほとんどのLinuxをインストールすることができるので選択肢が広がります。
最近は「Edison」という新たな切手サイズ(!)のSBCもあります。これはまだ情報が少ないので僕はよくわかりません。
BeagleBone
LinuxとIDEが最初から搭載されているSBCです。こちらはスペック十分ですが、その割には高いなぁという印象を受けるので興味があまり湧いていません。しかし大量のピンソケットが生えているのでトップクラスの拡張性を誇るかも…?
最後に
やはり紹介記事みたいになっちゃいましたが、SBC界隈はアツいのでこの熱気を他のクラスタの人達にも伝えたい…!という思いでここまで書いてきました。電子ガジェット製作(電気系に限らず情報系も)にはもってこいのツールですし、小規模なサーバー用途にも低消費電力で性能十分なので、皆さんもこの界隈に足を突っ込んでみて良さを味わって欲しいです。またこの記事がそのきっかけになれば幸いです。次書く機会があればGPIOピンの使い方なども紹介しようかな…。
明日はねぎとろさんにお願いしようかと思います。どんな記事を書いていただけるか楽しみです。
便利すぎるC++11
こんばんは、スペードです。
部活の方針がcocos2d-xでの開発になったらしいので、今回はC++11の便利なもののほんの一部を紹介したいと思います。
配列
C++11でソースコードを書くとき、普通の配列を使っていませんか?C++11には std::array という普通の配列のように使えるのはもちろん、それに加えて色々な機能を持っているすばらしいものがあります。
使い方
// 宣言 int a[10] = {5, 4, 3, 2, 1, 0, 9, 8, 7, 6}; // 普通の配列 std::array<int, 10> ar = {9, 8, 1, 2, 3, 7, 6, 5, 4, 0}; // std::array // ソート std::sort(a, a + 10); std::sort(ar.begin(), ar.end()); // また、C++11から使える範囲ベースforというものもある。 for(const auto& v : a) std::cout << v << std::endl; for(const auto& v : ar) std::cout << v << std::endl;
補足しておくと、上記のように、std::arrayはイテレータを使うことができます。
ラムダ式
僕がC++11で実装されたもので特に気に入っているものが、ラムダ式です。
ラムダ式とは
無名関数とも呼ばれ、関数を記述するための記法であり、関数オブジェクトの一種です。
無名関数??関数オブジェクト??と思われて難しく感じるかもしれませんが、使い方を見てもらえば簡単にラムダ式を書けるようになります。
使い方
[]{};
これが(最小の)ラムダ式です。
冗談はさておき(最小のラムダ式というのは本当です)、実際の使い方を見てみましょう。
[]{ std::cout << "Hoge!!" << std::endl; }; // Hoge!!を表示するラムダ式 auto func = []{ std::cout << "Hoge!!" << std::endl; }; // 変数に代入することができ、型推論も利用可能 []{ std::cout << "Hoge!!" << std::endl; }(); // ()で呼び出し可能 [](int x){ std::cout << x << std::endl; }; // 引数も与えられる。 [&](int x){ std:: cout << x << std::endl; }; // [&]で参照渡し、[=]で値渡し(コピー) [ キャプチャ(&, =) ]( 引数 ){ 処理 }() // ラムダ式の文法(二つ目の括弧をつけると呼び出しになる) // STLとラムダ式 std::vector<int> vec{2, 4, 6, 8, 10}; // 5未満の要素を削除 vec.erase(remove_if(vec.begin(), vec.end(), [=](int x){ return x < 5; }), vec.end());
ラムダ式いがかでしたか?最後の例で挙げたように、STLと併用することでき、使いこなせれば強い味方になってくれると思います。
まとめ
C++11の機能はもっとたくさんあります。リファレンスなどを閲覧してみるのもいいかもしれません。
明日(12/13)の担当はクモハ君に任せたいと思います。どんな記事を書いてくれるのでしょうか。