概要
静的型付けスクリプト言語AngelScriptの紹介です。
https://www.angelcode.com/angelscript/
今回はクラスの実装と機能についての記載です。
前回記事はこちら
環境
- Windows10 64bit
- Visual Studio 2017
- AngelScript ver.2.33.0
実装
3要素ベクター型クラスCVector3をC++側で実装して、スクリプト側で使用する例を記載してみます。
ヘッダファイル
ベクター型クラス定義です。
省略可能な(デフォルトな)コピーコンストラクタを実装していますが、理由は後述します。
// 3要素ベクター型クラス
class CVector3
{
public:
// コンストラクタ
CVector3()
{
x = y = z = 0.0f;
}
// コンストラクタ
CVector3(float _xx, float _yy, float _zz)
:x(_xx), y(_yy), z(_zz)
{
}
// コピーコンストラクタ
CVector3(const CVector3& _Other)
:x(_Other.x), y(_Other.y), z(_Other.z)
{
}
//---------------
// 初期化
//---------------
CVector3& SetVector(float _xx, float _yy, float _zz)
{
x = _xx; y = _yy; z = _zz;
return(*this);
}
//----------------
// 代入演算子
//----------------
// =
CVector3& operator=(const CVector3& _Other)
{
x = _Other.x; y = _Other.y; z = _Other.z;
return(*this);
}
// +=
CVector3& operator+=(const CVector3& _Other)
{
x += _Other.x; y += _Other.y; z += _Other.z;
return(*this);
}
// -=
CVector3& operator-=(const CVector3& _Other)
{
x -= _Other.x; y -= _Other.y; z -= _Other.z;
return(*this);
}
// *=
CVector3& operator*=(const CVector3& _Other)
{
x *= _Other.x; y *= _Other.y; z *= _Other.z;
return(*this);
}
// /=
CVector3& operator/=(const CVector3& _Other)
{
x /= _Other.x; y /= _Other.y; z /= _Other.z;
return(*this);
}
//----------------
// 2項演算子
//----------------
// +
CVector3 operator+(const CVector3& other) const {
return( CVector3(x+other.x, y+other.y, z+other.z) );
}
// -
CVector3 operator-(const CVector3& other) const {
return( CVector3(x-other.x, y-other.y, z-other.z) );
}
// *
CVector3 operator*(float rate) const {
return( CVector3(x*rate, y*rate, z*rate) );
}
// /
CVector3 operator/(float rate) const {
return( CVector3(x/rate, y/rate, z/rate) );
}
//----------------
// 比較演算子
//----------------
// ==
bool operator==(const CVector3& other) const {
return( (x==other.x) && (y==other.y) && (z==other.z) );
}
// !=
bool operator!=(const CVector3& other) const {
return( !(*this == other) );
}
// 正規化
CVector3& Normalize()
{
float _ff = x*x + y*y + z*z;
if(DBL_EPSILON >= _ff){
x = y = z = 0.0f;
return(*this);
}
_ff = 1.0f / sqrtf(_ff);
x *= _ff;
y *= _ff;
z *= _ff;
return(*this);
}
// 長さを取得
float Length() const {
return( sqrtf(x*x + y*y + z*z) );
}
// 内積
float DotProduct(
const CVector3& _Vec // 対象ベクトル
) const;
public:
float x{0.0f};
float y{0.0f};
float z{0.0f};
};
cppファイル
処理部分
Native版とGeneric版の両方を実装しています。
Native版はほぼコンストラクタとデストラクタだけで実装はヘッダファイルにあるものを使います。Generic版は引数の取得から処理を行うため全てのメソッドを実装しています。
// 内積計算
constexpr float mathVec3DotProduct(const CVector3& _Vec1, const CVector3& _Vec2 ){
return(_Vec1.x * _Vec2.x + _Vec1.y * _Vec2.y + _Vec1.z * _Vec2.z);
}
//---------------------------------------
// コンストラクタ(Native)
static void Construct1CVector3(
CVector3* thisPointer
)
{
new(thisPointer) CVector3();
}
// コンストラクタ(Native)
static void Construct2CVector3(
float _xx,
float _yy,
float _zz,
CVector3* thisPointer
)
{
new(thisPointer) CVector3(_xx, _yy, _zz);
}
// コピーコンストラクタ(Native)
static void CopyConstructCVector3(
const CVector3& other,
CVector3* thisPointer
)
{
new(thisPointer) CVector3(other);
}
// デストラクタ(Native)
static void DestructCVector3(
CVector3* thisPointer
)
{
thisPointer->~CVector3();
}
// 内積(Native)
float CVector3::DotProduct(
const CVector3& _Vec // 対象ベクトル
) const
{
return( mathVec3DotProduct(*this, _Vec) );
}
//-------------------------------------------
// コンストラクタ(Generic)
static void Construct1CVector3Generic(
asIScriptGeneric *_pGen
)
{
new (_pGen->GetObject()) CVector3();
}
// コンストラクタ(Generic)
static void Construct2CVector3Generic(
asIScriptGeneric *_pGen
)
{
float _xx = *static_cast<float*>(_pGen->GetAddressOfArg(0));
float _yy = *static_cast<float*>(_pGen->GetAddressOfArg(1));
float _zz = *static_cast<float*>(_pGen->GetAddressOfArg(2));
new (_pGen->GetObject()) CVector3(_xx, _yy, _zz);
}
// コピーコンストラクタ(Generic)
static void CopyConstructCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* ptr = static_cast<CVector3*>(_pGen->GetArgObject(0));
new (_pGen->GetObject()) CVector3(*ptr);
}
// デストラクタ(Generic)
static void DestructCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* ptr = static_cast<CVector3*>(_pGen->GetObject());
ptr->~CVector3();
}
// 初期化(Generic)
static void SetVectorCVector3Generic(
asIScriptGeneric *_pGen
)
{
float _xx = *static_cast<float*>(_pGen->GetAddressOfArg(0));
float _yy = *static_cast<float*>(_pGen->GetAddressOfArg(1));
float _zz = *static_cast<float*>(_pGen->GetAddressOfArg(2));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
_Self->SetVector(_xx, _yy, _zz);
}
// 代入演算子(=)(Generic)
static void AssignCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Other = static_cast<CVector3*>(_pGen->GetArgObject(0));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
*_Self = *_Other;
_pGen->SetReturnAddress(_Self);
}
// 代入演算子(+=)(Generic)
static void AddAssignCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Other = static_cast<CVector3*>(_pGen->GetArgObject(0));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
*_Self += *_Other;
_pGen->SetReturnAddress(_Self);
}
// 代入演算子(-=)(Generic)
static void SubAssignCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Other = static_cast<CVector3*>(_pGen->GetArgObject(0));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
*_Self -= *_Other;
_pGen->SetReturnAddress(_Self);
}
// 代入演算子(*=)(Generic)
static void MulAssignCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Other = static_cast<CVector3*>(_pGen->GetArgObject(0));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
*_Self *= *_Other;
_pGen->SetReturnAddress(_Self);
}
// 代入演算子(/=)(Generic)
static void DivAssignCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Other = static_cast<CVector3*>(_pGen->GetArgObject(0));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
*_Self /= *_Other;
_pGen->SetReturnAddress(_Self);
}
// 2項演算子(+)(Generic)
static void AddCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Other = static_cast<CVector3*>(_pGen->GetArgObject(0));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
CVector3 _Ret = CVector3(_Self->x+_Other->x, _Self->y+_Other->y, _Self->z+_Other->z);
_pGen->SetReturnObject(&_Ret);
}
// 2項演算子(-)(Generic)
static void SubCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Other = static_cast<CVector3*>(_pGen->GetArgObject(0));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
CVector3 _Ret = CVector3(_Self->x-_Other->x, _Self->y-_Other->y, _Self->z-_Other->z);
_pGen->SetReturnObject(&_Ret);
}
// 2項演算子(*)(Generic)
static void MulCVector3Generic(
asIScriptGeneric *_pGen
)
{
float _Rate = *static_cast<float*>(_pGen->GetAddressOfArg(0));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
CVector3 _Ret = CVector3(_Self->x*_Rate, _Self->y*_Rate, _Self->z*_Rate);
_pGen->SetReturnObject(&_Ret);
}
// 2項演算子(/)(Generic)
static void DivCVector3Generic(
asIScriptGeneric *_pGen
)
{
float _Rate = *static_cast<float*>(_pGen->GetAddressOfArg(0));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
CVector3 _Ret = CVector3(_Self->x/_Rate, _Self->y/_Rate, _Self->z/_Rate);
_pGen->SetReturnObject(&_Ret);
}
// 比較演算子(==)(Generic)
static void EqualsCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Other = static_cast<CVector3*>(_pGen->GetArgObject(0));
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
bool _Ret = ( (_Self->x==_Other->x)&&(_Self->y==_Other->y)&&(_Self->z==_Other->z) );
_pGen->SetReturnByte(_Ret);
}
// 正規化(Generic)
static void NormalizeCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
_Self->Normalize();
_pGen->SetReturnAddress(_Self);
}
// 長さ取得(Generic)
static void LengthCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
float _Len = _Self->Length();
_pGen->SetReturnFloat(_Len);
}
// 内積(Generic)
static void DotProductCVector3Generic(
asIScriptGeneric *_pGen
)
{
CVector3* _Self = static_cast<CVector3*>(_pGen->GetObject());
CVector3* _Vec = static_cast<CVector3*>(_pGen->GetArgObject(0));
_pGen->SetReturnFloat( mathVec3DotProduct(*_Self, *_Vec) );
}
登録部分
登録部分の実装コードです。Native版とGeneric版の両方の登録コードです、登録は*RegisterObject???*系メソッドで行います。AS_MAX_PORTABILITYで切り替えができます。
登録時にassertで返り値エラーを逐次確認するのが推奨のようです。
この登録処理*RegistClass_CVector3(mpEngine)*をスクリプトエンジン作成後に行うことでスクリプト側で使用可能になります。
// Vector型の登録
void RegistClass_CVector3(asIScriptEngine* _pEngine)
{
int _Result = 0;
// オブジェクトタイプの登録
_Result = _pEngine->RegisterObjectType("CVector3", sizeof(CVector3), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CDAK | asOBJ_APP_CLASS_ALLFLOATS); assert(_Result >= 0);
if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY"))
{
//-----------
// Generic
//-----------
// オペレータの登録
_Result = _pEngine->RegisterObjectBehaviour("CVector3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Construct1CVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectBehaviour("CVector3", asBEHAVE_CONSTRUCT, "void f(const float, const float, const float)", asFUNCTION(Construct2CVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectBehaviour("CVector3", asBEHAVE_CONSTRUCT, "void f(const CVector3 &in)", asFUNCTION(CopyConstructCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectBehaviour("CVector3", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
// メソッドの登録
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& opAssign(const CVector3 &in)", asFUNCTION(AssignCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& opAddAssign(const CVector3 &in)", asFUNCTION(AddAssignCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& opSubAssign(const CVector3 &in)", asFUNCTION(SubAssignCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& opMulAssign(const CVector3 &in)", asFUNCTION(MulAssignCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& opDivAssign(const CVector3 &in)", asFUNCTION(DivAssignCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3 opAdd(const CVector3 &in) const", asFUNCTION(AddCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3 opSub(const CVector3 &in) const", asFUNCTION(SubCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3 opMul(float) const", asFUNCTION(MulCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3 opDiv(float) const", asFUNCTION(DivCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "bool opEquals(const CVector3 &in) const", asFUNCTION(EqualsCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& SetVector(const float, const float, const float)", asFUNCTION(SetVectorCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& Normalize()", asFUNCTION(NormalizeCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "float Length()", asFUNCTION(LengthCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "float DotProduct(const CVector3 &in)", asFUNCTION(DotProductCVector3Generic), asCALL_GENERIC); assert(_Result >= 0);
}
else
{
//-----------
// Native
//-----------
// オペレータの登録
_Result = _pEngine->RegisterObjectBehaviour("CVector3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Construct1CVector3), asCALL_CDECL_OBJLAST); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectBehaviour("CVector3", asBEHAVE_CONSTRUCT, "void f(const float, const float, const float)", asFUNCTION(Construct2CVector3), asCALL_CDECL_OBJLAST); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectBehaviour("CVector3", asBEHAVE_CONSTRUCT, "void f(const CVector3 &in)", asFUNCTION(CopyConstructCVector3), asCALL_CDECL_OBJLAST); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectBehaviour("CVector3", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructCVector3), asCALL_CDECL_OBJLAST); assert(_Result >= 0);
// メソッドの登録
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& opAssign(const CVector3 &in)", asMETHODPR(CVector3, operator =, (const CVector3&), CVector3&), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& opAddAssign(const CVector3 &in)", asMETHODPR(CVector3, operator+=, (const CVector3&), CVector3&), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& opSubAssign(const CVector3 &in)", asMETHODPR(CVector3, operator-=, (const CVector3&), CVector3&), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& opMulAssign(const CVector3 &in)", asMETHODPR(CVector3, operator*=, (const CVector3&), CVector3&), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& opDivAssign(const CVector3 &in)", asMETHODPR(CVector3, operator/=, (const CVector3&), CVector3&), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3 opAdd(const CVector3 &in) const", asMETHODPR(CVector3, operator+, (const CVector3&) const, CVector3), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3 opSub(const CVector3 &in) const", asMETHODPR(CVector3, operator-, (const CVector3&) const, CVector3), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3 opMul(float) const", asMETHODPR(CVector3, operator*, (float) const, CVector3), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3 opDiv(float) const", asMETHODPR(CVector3, operator/, (float) const, CVector3), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "bool opEquals(const CVector3 &in) const", asMETHODPR(CVector3, operator==, (const CVector3&) const, bool), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& SetVector(const float, const float, const float)", asMETHODPR(CVector3, SetVector, (float, float, float), CVector3&), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "CVector3& Normalize()", asMETHODPR(CVector3, Normalize, (), CVector3&), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "float Length()", asMETHODPR(CVector3, Length, () const, float), asCALL_THISCALL); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectMethod("CVector3", "float DotProduct(const CVector3 &in)", asMETHODPR(CVector3, DotProduct, (const CVector3&) const, float), asCALL_THISCALL); assert(_Result >= 0);
}
// オブジェクトプロパティの登録
_Result = _pEngine->RegisterObjectProperty("CVector3", "float x", asOFFSET(CVector3, x)); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectProperty("CVector3", "float y", asOFFSET(CVector3, y)); assert(_Result >= 0);
_Result = _pEngine->RegisterObjectProperty("CVector3", "float z", asOFFSET(CVector3, z)); assert(_Result >= 0);
}
テスト
テスト用スクリプトコードです。
コンストラクタ、加算、内積、正規化の各メソッドが呼ばれています。
// CVector3型を定義
CVector3 _Vec1(1.0f, 1.0f, 0.0f);
CVector3 _Vec2(0.0f, 2.0f, 0.0f);
// 加算
CVector3 _Vec3 = _Vec1 + _Vec2;
// -> _Vec3 = (1.0, 3.0, 0.0)
// 内積
printf("DotProduct = %f\n", _Vec1.DotProduct(_Vec2));
// -> DotProduct = 2.000000
// 正規化
_Vec1.Normalize();
// -> _Vec1 = (0.707107, 0.707107, 0.000000)
// もう1回内積
printf("DotProduct = %f\n", _Vec1.DotProduct(_Vec2));
// -> DotProduct = 1.414214
// 配列でCVector3テーブルを作成する
array<CVector3> _VecArr = {
CVector3(1.0f, 2.0f, 3.0f),
CVector3(4.0f, 5.0f, 6.0f),
CVector3(7.0f, 8.0f, 9.0f)
};
printf("%f\n", _VecArr[1].x);
// -> 4.000000
考察
デフォルトコピーコンストラクタについて
64bitGUNCコンパイラではユーザー実装クラスのデフォルトコピーコンストラクタの呼び出し時に問題を起こす場合があり、CVector3などユーザー実装のクラスの場合、明示的なコピーコンストラクタを書く必要があるようです。
仮にCVector3のヘッダファイルに実装してあるコピーコンストラクタを省略して登録したCVector3クラスを引数に使った以下のような関数を別途作成し、Nativeで実行をすると一部の環境では落ちます。
// 座標を設定(Native)
void SetPos(
const CVector3& _Pos // 座標
)
{
printf("SetPos(%.1f, %.1f, %.1f)\n",_Pos.x, _Pos.y, _Pos.z);
}
// 座標を設定(Generic)
void SetPosGeneric(
asIScriptGeneric *_pGen
)
{
CVector3 _Pos = *reinterpret_cast<CVector3*>(_pGen->GetAddressOfArg(0));
// Nativeを呼び出す
SetPos(_Pos);
}
回避するには明示的にコピーコンストラクタを書くか、Generic版で起動すると回避できます(問題のクラス登録だけGenericにするという方法でもOKです)。
この現象についての開発者のコメントは以下の様になっているようです。
On Linux/amd64 with GNUC a class is treated very differently if the copy constructor is declared or not.
A class with a copy constructor is always passed through a reference even when the function is declared to pass the type by value.
When the copy constructor is not declared, the compiler will pass the type in the CPU registers instead, if the class is small enough.
The real complicated part is that when the type is passed in the CPU registers, it is split up by the type of each member, so integer members are passed in the general purpose registers, and float members are passed in the floating point registers.
This is why AngelScript doesn't allow these types to be passed by value, the implementation to support that in the native calling convention is just too difficult.You're not seeing this problem on Windows or Linux 32bit because the existence of the copy constructor or not doesn't make a difference on these platforms.
サイズの小さいクラスは明示的にコピーコンストラクタを書いた方が良いようです。
メンバ変数のpublicアクセスについて
C++側のCVector3クラスのメンバx, y, zのアクセス修飾子をprotected/privateにすると登録時のRegisterObjectProperty関数にてエラーになるためpublicにしています。
対策としてはスクリプトエンジンクラスasIScriptEngine をfriend classにしてしまえばいいかもしれません。(未検証)
スクリプト側でのクラス記述について
上記までの例はC++側で実装したクラスを使用しましたが、スクリプト側で記述したクラスももちろん使用できます。
スクリプト側での定義クラス
クラスの記述はC++とかなり似ています。
アクセス修飾子の指定がC#のようにメソッドやメンバ毎に記述する必要があります。省略時はpublicになります。
各メソッドにてデフォルト引数指定も可能です。
staticはありませんのでそれらは記述できません。
継承、オーバーライド禁止のfinal、オーバーライドしたことを明示するoverrideキーワードも使用可能です。virtualはないようです。
// スクリプト側で記述したテストクラス
class CTest{
// コンストラクタ
CTest(){
a = 0;
b = 0.f;
}
CTest(int _aa, float _bb){
a = _aa;
b = _bb;
}
// デストラクタ
~CTest(){}
// メソッド(public)
int GetA() { return(a); }
float GetB() { return(b); }
// メンバ
protected int a;
protected float b;
};
スクリプト側での継承(とミックスイン他)
通常のクラス以外には抽象クラス、インターフェイス、ミックスインクラスがあります。これらは単独指定は不可で、クラスに継承して使用します。
// 抽象クラス例
abstract class CATest{
protected int z;
};
// インターフェイス例
interface ITest{
int GetPow();
};
// ミックスインクラス例
mixin class CTestMixin{
int GetC(){ return(c); }
protected int c;
};
CTestクラスとCTestMixinミックスインクラスを継承した例。
// 継承例
class CTest2 : CTest, CTestMixin{
CTest2(){ a = 1; b = 1.5f; c = 20; }
int GetA() override { return(a*10); }
};
スクリプト側ではなく、C++側で登録した各クラスも同じように扱うことができます。
予め作成することが分かっている基底クラスやインターフェイスなどはC++側で用意しておいたほうが処理速度的に有利かと思います。
まとめ
AngelScriptのクラスはC++にかなり近いため、学習コストが低いと思います。用途としてはC++でクラス定義と受け渡し用関数定義を行って、スクリプト側にてデータテーブルを実装して受け渡し関数を経由してC++にデータを受け渡す、などの実装が考えられると思います。
今回提示したC++側で定義したクラスをスクリプトで使う場合以外にもスクリプト側で定義したクラスをC++で利用することもできるようです。しかし各メンバを読み出すのが大変で扱いにくい印象でしたので基本使用しない方がいいと思います。