* 運用 - Employment [#g9284ebe] #contents Last-modified: &lastmod(); ** このページについて [#ie2b0c1a] AngelScript(以下AS)を実際にチームで運用するときの使用例について書きます。 ** コーディング規約・運用ルールの例 [#fc028079] チームでASを使う場合,コーディング規約をある程度決めておいた方がよいでしょう。 ここでは決めておくべき項目とその例を紹介します。 ちなみに,ここで使用している例は筆者が使っているルールです。 実際にコーディング規約を作る場合はここの例にこだわらず, コミュニティやチームごとに親しみやすいルールで運用することをオススメします。 *** キャメルとは [#g9268c4f] 例の説明中に「キャメル」という言葉を使っています。 キャメルとはAbstractSceneやPlayerInformationのように 単語の頭が大文字,それ以外を小文字とした表記の方法のことを指します。 *** スクリプトファイル [#f847673c] - 原則,1スクリプトファイルにつき1つのクラスもしくは列挙型シンボルを記述するようにしてます。 - クラス名とファイル名は同じにします。拡張子は''.as''を使用します。 - Hogeというクラスのファイル名はHoge.asになります。 *** 列挙型・列挙型の要素 [#d6181359] - 列挙型の名前はクラス同様キャメルを使います。 - 列挙型の各要素は,列挙型の名前 + ''_'' + キャメルで表現します。 - 終端は''TERMINATE''を使います。 - 最も小さい値は''MIN'',最も大きい値は''MAX''を使います。(この名前の付け方はD言語に合わせています) #code(c,){{ enum ColorKind { ColorKind_Red , ColorKind_Blue // term , ColorKind_TERMINATE , ColorKind_MIN = 0 , ColorKind_MAX = ColorKind_TERMINATE-1 }; }} *** クラス [#k376e857] - 名前はキャメルを使います。 - ASではC++のようにクラス内クラス宣言ができません。 - これの代用として所属するクラス名 + ''_'' + 内部クラス名でそれを表現するようにしています。 #code(c,){{ // C++での記述 class Obj { class Inner { }; }; // ASで書くとこう class Obj { }; class Obj_Inner { }; }} *** インターフェース [#u376978d] - 名前は''I'' + キャメルを使います。 #code(c,){{ interface IHuman {}; class Taro : IHuman {}; }} *** メンバ変数・メンバ関数 [#h36e4118] - AS自体にpublicやprivateといった公開修飾子は存在しませんが,命名規則を使うことでメンバがpublicなものかprivateなものか判別つくようにしています。 - publicなメンバ変数・メンバ関数は小文字始まりのキャメルを使っています。 - privateなメンバ変数は''mv'' + キャメルを使っています。 - privateなメンバ関数は''mf'' + キャメルを使っています。 #code(c,){{ class Hoge { int mvPrivateValue; // privateなメンバ変数 int publicValue; // publicなメンバ変数 int publicFunction()const { return 0; } // publicなメンバ関数 int mfPrivateFunction()const { return 0; } // privateなメンバ関数 }; }} *** グローバル変数・関数 [#x82046c8] - ASコードはC++コードと違い,staticメンバ変数を持つことができません。 - それの代わりとして筆者はグローバル変数・関数を使用しているようにしてます。 - グローバル変数・関数をそれ以外の使い方としては使いません。 - 全てのグローバル変数・関数はC++コードでのstaticメンバ変数・関数に該当するという考え方です。 - publicなstaticメンバ変数・関数の名前は所属するクラス名 + ''_'' + キャメルを使います。 - privateなstaticメンバ変数の名前は所属するクラス名 + ''_'' + ''sv'' + キャメルを使います。 - privateなstaticメンバ関数の名前は所属するクラス名 + ''_'' + ''sf'' + キャメルを使います。 - 先頭に所属するクラス名をつける理由として,他のグローバル関数・変数の名前の衝突を回避する狙いがあります。 #code(C,){{ // C++のコード class Hoge { public: static const int StaticFunction(); static const int StaticValue; private: static const int PrivateStaticFunction(); static const int PrivateStaticValue; }; // ASコードで書くとこう。 const int Hoge_StaticFunction(); const int Hoge_StaticValue; const int Hoge_sfPrivateStaticFunction(); const int Hoge_svPrivateStaticValue; }} *** 関数の引数の修飾子 [#r2417574] - ASコードではin,inout,outなどが使えますがこれらは使わないようにしています。 - C++コードと同じようにObject&およびconst Object&のみ使うようにしています。 #code(c,){{ class Foo { int value; }; class Hoge { int mvValue; void readFoo( const Foo& aFoo ) { mvValue = aFoo.value; } void writeFoo( Foo& aFoo ) { aFoo.value = mvValue; } }; }} *** 名前空間を示す接頭辞 [#o58a9d77] ASコードではC++コードの名前空間(namespace)に該当する機能がありません。(*moduleは別の機能です) 筆者は名前空間の機能を補うために,クラス名の頭に名前空間の略称を示す接頭辞をつけるようにしています。 #code(c,){{ // C++コード namespace enemy { class StateAttack {}; } // ASコード class EnStateAttack // enemyの略称EnをStateAttackの前につけている { }; }} また,C++バインドしたクラス名は頭に''App''という接頭辞をつけています。 こういった接頭辞をつける理由は,名前の衝突をさけることが1番の目的にあります。 *** publicメンバ変数およびプロパティアクセサを積極的に使用 [#i451bb45] ASコードではC#やD言語でおなじみのプロパティアクセサを使用できます。 プロパティアクセサがあると + 読み書き可能なメンバ変数をpublicメンバ変数として宣言する。 + フックが必要になったタイミングでプロパティアクセサを記述する。 という流れが可能になります。 そのため,C++コードでは手間がかかるアクセサ記述を最小限に留めることができます。 この流れの例を示します。 Moveクラスはspeedというメンバ変数を持っているとします。 このspeedは読み書き可能なメンバ変数なのでpublicなメンバ変数として宣言します。 #code(c){{ // Moveクラス class Move { float speed; // publicなメンバ変数として宣言 }; // Moveの動作をテストするコード void Move_UnitTest() { Move move; move.speed = 3; // write const float moveSpeed = move.speed; // read assert( moveSpeed == 3 ); } }} ある日,Moveクラスのspeedに0以上の値しか設定できないように仕様を変更することになりました。 この仕様をプロパティアクセサを使用して実装します。 #code(c){{ // Moveクラス class Move { float mvSpeed; // privateなメンバ変数として宣言 float get_speed()const { return mvSpeed; } void set_speed(const float aSpeed) { assert( 0 <= aSpeed ); mvSpeed = aSpeed; } }; // Moveの動作をテストするコード // 前のバージョンから1行も変更していないが同じ動作をする void Move_UnitTest() { Move move; move.speed = 3; // write const float moveSpeed = move.speed; // read assert( moveSpeed == 3 ); } }} Moveクラスの仕様変更をしましたが,プロパティアクセサを使ったため既存のコード(Move_UnitTest)を変更せずにすみました。 ** IDisposableの導入 [#t67d80b9] ASのクラスのインスタンスのデストラクタが走るタイミングは ガベージコレクションの都合で変わるため不確定と考えた方がいいでしょう。 そのため,メモリリソースを破棄するなど,タイミングを狙って後始末をしたい処理をデストラクタに書くべきではありません。 「タイミングを狙って後始末」という仕組みを実現するため C#でおなじみのIDisposableインターフェースを用意し, 後始末が必要なクラスはこれを実装するようにしました。 #code(c,){{ interface IDisposable { void dispose(); } }} |