Top > C++ > AngelScript > 構文

* 構文 - Script Language [#g9284ebe]
#contents
Last-modified: &lastmod();

** このページについて [#ie2b0c1a]
AngelScript(AS)のスクリプト構文について書きます。
(このページに書かれているサンプルコードの一部はAngelScript公式のドキュメントから転載しています。)

** 「同等」と「同様」の言葉の使い方ついて [#sa64f03f]
文中で「C++と同等」と「C++と同様」という言葉を使っています。
「C++と同等」は「C++とほぼ同じ」という意味合いで,
「C++と同様」は「C++と全く同じ」という意味合いで使っています。
** 字句 [#i3b49282]
*** コメント [#oda6fcbb]
C++と同様に1行コメント,ブロックコメントの2種類が使えます。
#code(c,){{
// 1行コメント
/*
 ブロックコメント
*/
}}

*** 値 [#z1ceda31]
#code(c,){{
// 10進数表現した整数値
int a = 3; 

// 16進数表現した整数値
int b = 0xFFFF;

// 64bit浮動小数
double ca = 1.2345;
double cb = 1.2345e10;

// 32bit浮動小数
float da = 1.2345f;
float db = 1.2345e10f;

// bool値
bool ea = true;
bool eb = false;
}}
** 型 [#c13aa6fe]
*** 組み込み型 [#pb10e35e]
C++と同等の組み込み型が用意されています。

:void|
C++と同様に関数の戻り値に型に使います。

:bool|
C++と同様のもので,true,falseのどちらかの値を持ちます。

:整数|
下記のタイプが存在します。
|型名|最小値|最大値|h
|int8|-128|127|
|int16|-32768|32767|
|int&br;int32|-2147483648|2147483674|
|int64|-92233772036854775808|9223372036854775807|
|uint8|0|255|
|uint16|0|65535|
|uint&br;uint32|0|4294967295|
|uint64|0|18446744073709551615|
公式のドキュメントによると,パフォーマンスを最適化したいのであれば
32bitより小さい型の値はクラスのメンバ変数など,データを保持するもののみに使用し,
それ以外(ローカル変数,一時変数など)は32bitの型の値を使用したほうがいいようです。

:浮動小数|
下記のタイプが存在します。
|型名|値の範囲|一番小さい正の値|最大桁数|h
|float|+/- 3.402823466e+38|1.175494351e-38|6|
|double|+/- 1.7976931348623158e+308|2.2250738585072014e-308|15|
公式のドキュメントによりますと,ASではNaN(Not-aNumber)を0x7fc00000で表現しているようです。
*** 配列 [#k3346995]
配列の変数は次のように宣言します。
#code(c,){{
// int型の配列を宣言
int[] intArray;

// int型の配列を3つ宣言
int[] intArrA,intArrB,intArrC;

// 何も初期化子を指定しなければ,長さ0の配列を作ります
int[] a; // a.length() == 0

// 初期化子に数を指定すると,その数の長さの配列を作ります
int[] b(3); // b.length() == 3

// 配列初期化子を指定すると,それがコピーされた配列を作ります。
int[] c = {1,2,3}; // 先頭から1,2,3がコピーされた配列

}}

配列はC++と同様にインデクス式を使ってアクセスします。
また,配列の長さはlength()メソッドにより取得できます。
#code(c,){{
int[] a = {0,1,2};
for ( uint i = 0; i < a.length(); ++i )
{
    a[i] += 1;
}
}}
*** ユーザー定義型 [#r0f093c7]
ユーザー定義型(クラスや列挙型)の変数はC++と同様に宣言することができます。

#code(c,){{
// objという型のインスタンスが作られる
// デフォルトコンストラクタがあればそれが呼ばれる
obj a;

// objの一時変数を作成し,aに代入する
a = obj();
}}
*** オブジェクトハンドル [#t39c8cfc]
オブジェクトハンドルとはC++でいう参照・ポインタにあたるものです。
参照先のオブジェクトは参照カウンタで寿命を管理しているため,「既に破棄されたデータにアクセスする」という不正なデータアクセスはおこりません。
もしnullなオブジェクトハンドルにアクセスしようとしたら例外が発生しスクリプトの実行が止まります。

#code(c,){{
obj o;
obj@ a; // a はnullで初期化されます
obj@ b = @o; // oの参照をbに代入します

b.ModifyMe(); // o.ModifyMe()と同様のことがおこります

if ( a is null ) // nullかどうかチェックしています。
{
  @a = @b; // aにbが参照しているオブジェクトを代入します。この場合,結果的にoの参照が渡されています。
}

}}
*** 文字列 [#lec9ebac]
ASでの文字列は8bitもしくは16bitのデータの配列として扱われます。

ASではC++と同等のエスケープシーケンス文字が用意されています。
下記がエスケープシーケンス文字をまとめた表です。

|文字|値|意味|h
|¥0|0|null文字|
|¥¥|92|バックスラッシュ|
|¥'|39|シングルコーテーション|
|¥"|34|ダブルコーテーション|
|¥n|10|改行文字 LF|
|¥r|13|改行文字 CR|
|¥t|9|TAB|
|¥xFFFF|0xFFFF|16bitで表した文字|
|¥uFFFF|0xFFFF|unicode16bitで表した文字|
|¥uFFFFFFFF|¥0xFFFFFFFF|unicode32bitで表した文字|

文字列はダブルコーテーションもしくはシングルコーテーション(要オプション設定)で囲んで表します。
#code(c,){{ 
// ダブルコーテーションで囲む例
// 文字列の中にダブルコーテーションを使いたいときは,エスケープ文字を使うこと。
string str1 = "This is a string with \"escape sequences\".";

// シングルコーテーションで囲む例
// シングルコーテーションで囲むと,エスケープ文字を使わずに文中にダブルコーテーションを使うことができる
string str2 = 'If single quotes are used then double quotes can be included without "escape sequences".';

}}

C++と同様に複数の文字列を1つの文字列として連結することができます。
#code(c,){{
// strには "First Line.¥nSecondLine.¥n"が代入される
string str = "First Line.¥n"
  "Second Line.¥n";
}}

ASでは''"""''を使うことで複数行に渡す文字列を簡単に記述することができます。
#code(c,){{
// """ で始まった文字列は """で閉じる
string str = """
First Line.
Second Line.
""";
}}
** 文 [#teb1f49c]
*** 宣言文 [#ae71baed]
#code(c,){{
// 同じ型の変数はカンマで区切ることで複数宣言することができます。
int var = 0, var2 = 10;
object@ handle, handle2;

// C++と同様にconstをつけることができます
const float pi = 3.141592f;

// 組み込み型の変数の初期化子を省略した場合,値は未初期化となり不定になります
int randaomVar;

// オブジェクトハンドルの場合,nullが代入されます
object@ nullHandle;

// ユーザー定義型の場合,デフォルトコンストラクタが呼ばれます
object defaultCtorCalled;

}}
*** 式文 [#o5c9ee2d]
C++同様の式文が使えます。詳しくは式の項をご覧ください。
#code(c,){{
a = b; // 代入演算式
func(); // 関数呼び出し
}}
*** if文 [#p78a09e7]
C++同等のif文が使えます。
#code(c,){{
if( condition ) 
{
  // condition == trueのときに実行する処理
}

if( value < 10 ) 
{
  // valueが10未満のときに実行する処理
}
else
{
  // valueが10以上のときに実行する処理
}
}}

ただし,C++と異なり,C#のように条件部はbool値である必要があります。
#code(c,){{
// 条件部はbool値である必要があるため,次のコードはエラー
int notZeroValue = 1;
if ( notZeroValue ) // エラー:条件がbool値でない
{
}

// 次のように書くこと
if ( notZeroValue != 0 )
{
}

}}
*** for文 [#baebd491]
C++と同等のfor文が使えます。
ただし,条件部はif文と同じようにbool値である必要があります。

#code(c,){{
for ( int n = 0; n < 10; ++n )
{
    // 10回する処理を書く
    // ...
    

    // continueやbreakも書けます
    if ( n == 5 )
    {
        continue;
    }
    if ( n == 9 )
    {
        break;
    }
}

}}
*** while文 [#j461bb07]
C++と同等のwhile文が使えます。
ただし,条件部はif文と同じようにbool値である必要があります。

#code(c,){{  
int i = 0;
while( i < 10 )
{
    i++;

    // continueやbreakも書けます
    if ( i == 5 )
    {
        continue;
    }
    if ( i == 9 )
    {
        break;
    }
}
}}
*** do-while文 [#p5762b82]
C++と同等のdo-while文が使えます。
ただし,条件部はif文と同じようにbool値である必要があります。

#code(c,){{  
int i = 0;
do
{
    i++;

    // continueやbreakも書けます
    if ( i == 5 )
    {
        continue;
    }
    if ( i == 9 )
    {
        break;
    }
}while( i < 10 );
}}
*** switch文 [#g0b99de9]
C++と同様のswitch文が使えます。
#code(c,){{
  switch( value )
  {
  case 0:
    // value == 0のときにここに飛ぶ
    break;

  case 2:
  case constant_value:
    // value == 2 もしくは value == constant_valueのときにここに飛ぶ
    break;

  default:
    // 全ての条件に該当しない場合,ここに飛ぶ
  }
}}
*** return文 [#g7ecfbf7]
C++と同様のreturn文が使えます。

#code(c,){{
float valueOfPI()
{
    return 3.141592f; // 値を返す
}
void function()
{
    return; // 値を返さずに関数の処理を終了する
}

}}
*** スコープブロック文 [#kc357a28]
C++と同様のスコープブロック文が使えます。
#code(c,){{
  {
    int a; 
    float b;

    {
      float a; // aという同じ名前の変数を宣言したため,int aを隠してしまいます

      // float aの値をbに代入しています
      b = a;
    }

    // ここで再びint aにアクセスできるようになります
  }

}}
** 式 [#nb44a3d6]
*** 代入式 [#yeb8cb21]
C++と同様の代入式が使えます。
#code(c,){{
lvalue = rvalue;
}}

*** 代入演算式 [#r0faab26]
C++と同等の代入式が使えます。
ASのシフト演算はC++のものと異なり,左シフト,符号無し右シフト,符号有り右シフトの3種類のシフトが使えます。

#code(c,){{
lvalue += rvalue;
lvalue = value + rvalue;
}}

使用可能な代入演算記号は下記の通りです。
 += -= *= /= = &= |= ^= <<= >>= >>>=
*** 関数呼び出し [#w8040e1c]
C++と同様の関数呼び出しが使えます。
#code(c,){{
func();
func(arg);
func(arg1, arg2);
lvalue = func();
}}
*** 型変換式 [#u9e1576c]
オブジェクトハンドルの型変換の例を下記に示します。
もし,オブジェクトハンドルの型変換に失敗したらnullのオブジェクトハンドルが返されます。
#code(c,){{
  // オブジェクトハンドルの暗黙の変換
  // classがintfを継承しているときのみ可能
  intf @a = @clss();

  // オブジェクトハンドルの明示的な変換
  // 変換に失敗したらnullが返る
  clss @b = cast<clss>(a);
}}

*** 組み込み型の値の変換式 [#fa97d20e]
組み込み型の値の変換の例を下記に示します。
#code(c,){{
  // float -> intの暗黙的な変換が発生
  int a = 1.0f;

  // 明示的な変換。aをfloatに変換している
  float b = float(a)/2;
}}
*** 算術演算式 [#y7543ed9]
C++と同様の算術演算式が使えます。
 c = -(a + b);

|演算記号|説明|左項|右項|結果|h
|+|正符号演算|-|数値|数値|
|-|負符号演算|-|数値|数値|
|+|加算|数値|数値|数値|
|-|減算|数値|数値|数値|
|*|乗算|数値|数値|数値|
|/|除算|数値|数値|数値|
|%|余算|数値|数値|数値|
*** ビット演算式 [#m1f0b534]
C++と同等のビット演算式が使えます。
ASではD言語のように右シフトが2種類に増えています。

 c = ~(a | b);

|演算記号|説明|左項|右項|結果|h
|~|ビット反転|-|数値|数値|
|&|ビット論理積|数値|数値|数値|
|||ビット論理和|数値|数値|数値|
|^|ビット排他論理和|数値|数値|数値|
|<<|左シフト|数値|数値|数値|
|>>|右シフト|数値|数値|数値|
|>>>|算術右シフト|数値|数値|数値|
*** 論理演算式 [#h7b6dcbb]
C++と同等の論理演算式が使えます。
ASでは,not,and,or,xorが既存の演算記号の代わり使うことができます。
 if ( a and b or not c ) {}

|演算記号|説明|左項|右項|結果|h
|not&br;!|論理否定|-|bool値|bool値|
|and&br;&&|論理積|bool値|bool値|bool値|
|or&br;|||論理和|bool値|bool値|bool値|
|xor|排他論理和|bool値|bool値|bool値|
*** 等値式 [#de0709a3]
C++と同様の等値式が使えます。

#code(c,){{
if ( a == b )
{
}
if ( a != b )
{
}
}}
*** 比較式 [#j1bc3347]
C++と同様の比較式が使えます。
#code(c,){{
if ( a < b ) {}
if ( a <= b ) {}
if ( a > b ) {}
if ( a >= b ) {}
}}

*** 同一性式 [#n6064dbf]
2つのオブジェクトハンドルが示すものが同じかどうかを調べる式です。
#code(c,){{
object@ a,b;
if ( a is null )
{
    // aがnullのときに実行される処理
}
if ( a !is null )
{
    // aがnullでないときに実行される処理
}
if ( a is b )
{
    // aとbが同じオブジェクトを指している,もしくは両方ともnull
}
}}


*** インクリメンタル式・デクリメンタル式 [#l7f3b00c]
C++と同様のインクリメンタル式,デクリメンタル式が使えます。
#code(c,){{
// a = i; i += 1; と同じ意味
a = i++;

// i += 1; b = i; と同じ意味
b = ++i;

// c = i; c -=1; と同じ意味
c = i--;

// i -= 1; d = i; と同じ意味
d = --i;
}}
*** インデクス式 [#d8d6583e]
C++と同様のインデクス式が使えます。
添え字に使う型は,インデクス式がオーバーロードできるためオブジェクトのタイプごとに異なります。
 arr[i] = 1;
*** 条件式 [#f2128c3e]
C++と同等の三項条件式が使えます。
if文と同様に,条件部はbool値である必要があります。
 condition ? a : b;
*** メンバアクセス [#k8d2dc13]
 object.property = 1;
 object.method();

C++と同様にオブジェクトのメンバにアクセスできます。

ASではオブジェクトにプロパティアクセサを持つことができるため
メンバ変数にアクセスしているように見えて,実際は関数を呼び出しているということもありえます。
詳細はクラスのプロパティアクセサの項をご覧ください。
*** オブジェクトハンドルアクセス [#p581d30b]
オブジェクトのハンドルにアクセスする際に頭に''@''をつけます。
この記号を使うことで,オブジェクトハンドルが指すオブジェクトを変更したり,参照したりします。

#code(c,){{
  // objectが指すオブジェクトの参照をhandleにコピー
  @handle = @object;

  // handleが指すオブジェクトをnullに設定
  @handle = null;
}}
*** 丸括弧 [#ue94adc4]
C++と同様の丸括弧が使えます。
 a = c * ( a + b );
*** スコープ解決 [#zae249e6]
C++に似たスコープ解決演算子が使えます。
''::''を使うことでグローバル変数・関数を指定することができます。

#code(c,){{
  int value;
  void function()
  {
    int value;        // グローバル変数のvalueを隠してしまう
    ::value = value; // グローバル変数のvalueにローカル変数のvalueを代入している
  } 
}}
** オブジェクトハンドル [#o1d47802]
オブジェクトハンドルはオブジェクトの参照を保持することができる型です。
オブジェクトハンドルを使うことで,1つのオブジェクトを複数の変数からアクセスできるようになります。
C++でいうポインタや参照と同じようなものだと考えてください。
*** オブジェクトハンドル変数の宣言 [#la326047]
オブジェクトハンドルの型は型名 + ''@''で表現します。
 object@ obj_h;
このコードでは,obj_hにnull参照が保持されます。
null参照が保持されている状態は,オブジェクトの参照を保持していない状態と考えてください。

*** メンバへのアクセス [#xf43511b]
オブジェクトハンドルが参照しているオブジェクトのメンバへのアクセスは
通常のオブジェクトのメンバへのアクセスと同じようにアクセスします。
 object obj;
 object@ obj_h;
 obj.Method();
 obj_h.Method();
もし,オブジェクトハンドルがnull参照を保持している状態でメンバへアクセスしようとすると
例外が発生し,実行中のスクリプトが停止します。

*** 値のコピー [#f66cb352]
オブジェクトハンドルの変数に対して代入演算子''=''を使うと,
通常のオブジェクト変数に対して代入演算を記述したときと同じことが行われます。
 object obj;
 object@ obj_h;
 obj_h = obj;
もちろん,オブジェクトハンドルがnull参照を保持している場合は例外が発生します。

*** 参照のコピー [#l6496ebe]
オブジェクトハンドルが保持する参照をコピーしたい場合は''@''記号を使います。
 object obj;
 object@ obj_h;
 @obj_h = @obj;

*** 同一性チェック [#qbfc7e03]
同一性式を使うことで,2つのオブジェクトハンドルが同じオブジェクト参照を保持しているか調べることが出来ます。
 object@ obj_a, obj_b;
 if ( obj_a is obj_b ) {}
 if ( obj_a !is null ) {}
*** オブジェクトの寿命 [#b7126457]
オブジェクトハンドルが参照するオブジェクトは
全てのオブジェクトハンドルから参照を保持されなくなった時点で解放されます。

#code(c,){{
  object@ obj_h;
  {
    object obj;
    @obj_h = @obj;

    // 通常は,スコープブロックの終了時にオブジェクトは解放されます
    // しかし,オブジェクトハンドルが参照を保持しているため,ここでは解放されません
  }

  // obj_hはオブジェクトの参照を保持しているため,メンバにアクセスすることができます
  obj_h.Method();

  // このように,明示的にオブジェクトハンドルの参照を解除するか
  // スコープを抜けるタイミングでオブジェクトは解放されます
  @obj_h = null;
}}
*** ポリモフィズム [#xe49a700]
オブジェクトハンドルを使うことで基底クラス・インターフェースのへの変換やダウンキャストを実現します。

#code(c,){{
  interface I {}
  class A : I {}
  class B : I {}

  // インターフェースとして,オブジェクトハンドルに参照を保持させる
  I @i1 = A();  
  I @i2 = B();

  void function(I @i)
  { 
    // ダウンキャストをする場合,cast演算を使って変換を試みます
    A @a = cast<A>(i);
    if( a is null )
    {
      // ダウンキャストに失敗すればnull参照が格納されます
    }
    else
    { 
      // ダウンキャストに成功すれば有効な参照が格納されます
    }
  }

}}
** 関数 [#q7b2e2c2]
ほとんどC++と同じように書くことが出来ます。
C++のように宣言のみを書くことはできません。必ず本体も一緒に定義してください。

#code(c,){{
// aとbを加算して返す関数のサンプル。
int add( int a , int b )
{
    return a + b;
}
}}

関数の引数には参照を指定することができます。
C++にはconst&と&の2種類の参照がありましたが,ASには&in,&out,&inout,const &in,const &out, const &inoutの6種類があり,&の後ろを省略すると&inoutが使われます。
C++のconst参照の代わりにconst &inを,普通の参照の代わりに&inoutを使うと覚えておけばまず大丈夫です。
中でもconst &inは読み取り専用の引数に積極的に使うといいでしょう。処理の最適化が行われます。

#code(c,){{
class Hoge
{
    int value;
};

void modify( Hoge& aObj ) // Hoge &inoutと同じ
{
    aObj.value = 1;
}

int get( const Hoge&in aObj ) // 読み取り専用のオブジェクトはconst &inを使うとよい
{
    return aObj.value;
}

}}
** 変数 [#d428e214]
スクリプトで宣言されたグローバル変数は全てのコンテキスト間で共有されます。

関数呼び出しを必要としない単純な式を使ってグローバル変数の初期値を記述すると,その値はコンパイル時に計算されます。

string型のようなメモリリソースを必要とするグローバル変数は,モジュールやエンジンを破棄したときにメモリリソースを解放します。

組み込み型変数の初期化は,非組み込み型変数の初期化の前に行われます。
これによって,クラスのコンストラクタが初期化済みのグローバル変数をアクセスすることを可能にしています。

もし,非組み込み型のグローバル変数にアクセスしようとした場合,その変数が初期化済みであるかどうかの保証はされません。
** インターフェース [#w64b11fd]
ASのインターフェースはC#やD言語と同等のものと考えてもらって構いません。
インターフェースにはC++でいう純粋仮想関数を宣言することができます。
ただし,C#やD言語のインターフェースとは異なり,インターフェースが他のインターフェースを継承することはできません。
その代わり,クラスは複数のインターフェースを継承することができます。

#code(c,){{
  // インターフェースの宣言
  interface MyInterface
  {
    void DoSomething();
  }

  // MyInterfaceを実装するクラスの宣言
  class MyClass : MyInterface
  {
    void DoSomething()
    {
    }
  }

  // 複数のインターフェースの継承例
  interface IntfA {} 
  interface IntfB {}
  class Example : IntfA , IntfB {}

}}
** import [#o4ad69c4]
** 列挙型 [#ff101c74]
C++と同様のenumが使えます。

#code(c,){{
  enum MyEnum
  {
    eValue0,
    eValue2 = 2,
    eValue3,
    eValue200 = eValue2 * 100
  }
}}
** typedef [#c9cfcf91]
他の型のエイリアスを宣言することができます。
現在のバージョンでは組み込み型に限定されているようです。
 typedef float real32;
 typedef double real64;

** funcdef [#p2a12534]
関数ポインタを宣言することができます。
現在のバージョンではグローバル関数に限定されており,メンバ関数では使えないようです。

#code(c,){{
  // 関数ポインタの型を定義
  funcdef bool CALLBACK(int, int);

  // サンプル
  void main()
  {
    // 関数ポインタ変数を宣言し,myCompareを代入する
    CALLBACK @func = @myCompare;

    // isを使ってnullかどうかチェックすることができます
    if( func is null )
    {
      print("The function pointer is null\n");
      return;
    }

    // 関数ポインタを使って関数を呼び出します
    if( func(1, 2) )
    {
      print("The function returned true\n");
    }
    else
    {
      print("The function returned false\n");
    }
  }

  // 関数ポインタに渡す関数。
  // 関数ポインタに指定する関数は,戻り値と引数の型が同じである必要があります。
  bool myCompare(int a, int b)
  {
    return a > b;
  }
}}
** クラス [#ee65bf14]
ASのクラスはC++に似ていますが,仕様が異なる箇所がいくつかあるので注意してください。

#code(c,){{
  // クラスの宣言
  class MyClass
  {
    // デフォルトコンストラクタ
    MyClass()
    {
      a = 0;
    }

    // デストラクタ
    ~MyClass()
    {
    }

    // 引数指定付きコンストラクタ
    MyClass(int a)
    {
      this.a = a;
    }

    // メンバ関数
    void DoSomething()
    {
      a *= 2;
    }

    // メンバ変数
    int a;
  }
}}
*** アクセス修飾子 [#o29dc089]
ASのクラスはC++のようにアクセス修飾子(public,protected,private)が存在しません。
複数人でコーディングするときは,どのメンバが外部からアクセスしてよいメンバなのか分からなくなりますので
命名規則などを作り運用でカバーしましょう。

*** コンストラクタとデストラクタ [#z88ec839]
C++のように,コンストラクタ・デストラクタが定義できます。
デフォルトコンストラクタ,デフォルトデストラクタは定義しなければ自動生成されます。

ただ,自動で生成されるデフォルトコンストラクタは組み込み型のメンバ変数を初期化しません。
それらのメンバ変数は未初期化になるため気を付けましょう。

#code(c,){{
class Vector2
{
    float x;
    float y;
    Vector2()
    {
        x = 0;
        y = 0;
    }
};

class Obj
{
    int a;
    Vector2 vec;
};

Obj obj; 
// obj.aは未初期化になる
// obj.vecはVector2のデフォルトコンストラクタが実行されるためちゃんと初期化される

}}

また,ASはC++と異なりデフォルトコンストラクタは必ず生成されるので注意しましょう。
(C++ではコンストラクタを定義するとデフォルトコンストラクタは自動生成されません)

#code(c,){{
class Vector2
{
    float x;
    float y;
    Vector2(float aX, float aY)
    {
        x = aX;
        y = aY;
    }
};

Vector2 vec; // 自動生成されたデフォルトコンストラクタが実行されてしまう。そのためvec.x,vec.yは未初期化になる。
}}

*** クラスの継承・インターフェースの実装 [#m8eaaf33]
ASのクラスはC#・D言語のように1つだけクラスを継承することができ,
インターフェースは複数実装(継承)することができます。

ASのクラスのメンバ関数は,JAVAやD言語ように全て仮想関数として宣言されます。
そのためどのメンバ関数もオーバーライドすることができます。

#code(c,){{
interface IUpdatable
{
    void update();
};
interface IDrawable
{
    void draw()const;
};

// 2つのインターフェースを実装する
class Chara : IUpdatable , IDrawable
{
    int hitPoint;

    Chara()
    {
        hitPoint = 0;
    }
    Chara(int aHitPoint)
    {
        hitPoint = aHitPoint;
    }

    void update() {}
    void draw()const {}
};

// 1つのクラスを継承する
class Enemy : Chara
{
    Enemy()
    {
        // Chara()が自動で呼ばれる
    }
    Enemy(int aHitPoint)
    {
        super( aHitPoint ); // 継承元のコンストラクタはこのように呼び出すことができる
    }

    void update()
    {
        Chara::update(); // 隠蔽した関数はこのように呼び出すことが出来る
    }
};

}}

ちなみに,ASの''super''を使ったコンストラクタの呼び出し位置の制約はないようです。
(C#やD言語では先頭でしかできない仕様)
*** 演算子オーバーロード [#wd33cb58]
ASはC++のように演算子をオーバーロードすることができます。
演算記号に該当する関数名を実装することで演算子オーバーロードを実現できます。

:単項演算子|
|記号|関数名|h
|-|opNeg|
|〜|opCom|

これらの演算子を定義していてコード中で演算子が使われると,コンパイル時に a.opfunc() という形に展開されます。

:等値演算子|
|記号|関数名|h
|==|opEquals|
|!=|opEquals|

''a == b''は''a.opEquals(b)''と''b.opEquals(a)''のどちらかに展開されます。
opEqualsはbool値を返す関数として定義する必要があります。

:比較演算子|
|記号|関数名|h
|<|opCmp|
|<=|opCmp|
|>|opCmp|
|>=|opCmp|

比較演算は a.opCmp(b) か b.opCmp(a)のどちらかに展開されます。
opCmp関数はint値を返す関数として定義する必要があります。
a.opCmp(b)のとき,a < bなら-1を,a==bなら0を,a > bなら1を返すようにしてください。

opCmpが定義されていればopEqualsは定義されていなくてもopCmpを使うことで等値演算を実装できます。
しかしながら,等値演算は比較演算と比べて処理が軽いケースが多いです。
処理負荷の最適化を考えるならば,等値演算もオーバーロードしたほうがいいでしょう。

:代入演算子|
|記号|関数名|h
|=|opAssign|
|+=|opAddAssign|
|-=|opSubAssign|
|*~|opMulAssign|
|/=|opDivAssign|
|%=|opModAssign|
|&=|opAndAssign|
||=|opOrAssign|
|^=|opXorAssign|
|<<=|opShlAssign|
|>>=|opShrAssign|
|>>>=|opUShrAssign|

代入演算子が使用されるとa.opfunc(b)というコードに展開されます。
代入演算子の関数は次のように実装してください。

#code(c,){{
obj@ opAssign(const obj&in other)
{
    // 代入処理

    // 自分自身のオブジェクトハンドルを返すようにしてください
    return this;
}
}}

スクリプト上で宣言されたクラスは自動でコピー演算子(opAssign)が実装されます。
自動で生成される挙動とは異なる挙動を実装したいときにopAssignをオーバーロードしてください。

:算術演算子|
|記号|関数名|関数名(反転)|h
|+|opAdd|opAdd_r|
|-|opSub|opSub_r|
|*|opMul|opMul_r|
|/|opDiv|opDiv_r|
|%|opMod|opMod_r|
|&|opAnd|opAnd_r|
|||opOr|opOr_r|
|^|opXor|opXor_r|
|<<|opShl|opShl_r|
|>>|opShr|opShr_r|
|>>>|opUShr|opUShr_r|

『a 演算記号 b』と記述されると a.opfunc(b) か b.opfunc_r(a) に展開されます。
*** プロパティアクセサ [#l3502fb8]
ASではC#やD言語のようにプロパティアクセサを実装することができます。
クラスのメンバとして実装することはもちろん,グローバル変数としても実装することができます。

#code(c,){{
  // プロパティアクセサを使うクラスの宣言
  class MyObj
  {
    int get_prop() const
    {
      // propの値を返す
      return realProp;
    }

    void set_prop(int val)
    {
      // propの値を設定
      // 値のクリッピングをしてから代入する
      if( val > 1000 ) val = 1000;
      if( val < 0 ) val = 0;

      realProp = val;
    }

    // propがアクセスする変数
    int realProp;
  }

  // プロパティアクセサの動作例
  void Func()
  {
    MyObj obj;

    // プロパティアクセサで実装されたプロパティはメンバ変数のようにアクセスします
    // set_prop(10000)が呼ばれます
    obj.prop = 10000;

    // get_propがよばれます
    // クリッピング処理が呼ばれているはずなので,値は1000が返るはずです
    assert( obj.prop == 1000 );
  }

}}

    Front page List of pages Search Recent changes Backup Referer   Help   RSS of recent changes