Cocos2d-xによる2.5次元表現
背景
「次元」というと理系出身の人は大体ベクトル空間の次元を思い浮かべると思います。 最近はネットで「2.5次元の女の子しか興味ない」というと3DCGの美少女のことを指したりしますよね。 実は形式的に整数でない「次元」を定義する場合もあり、「フラクタル次元」では次元は一般に無理数です。
大分話が脱線しましたが、この記事では2次元ゲームライブラリのCocos2dを使用して「奥行き」を表現するための工夫を取り扱います。
Cocos2d-xというと、名前の通り「2次元」を連想する方がほとんどだと思います。 しかし、ちょっとした拡張でspriteに奥行きがあるように見せることができますので、ここで紹介しようと思います。
使用する技法は3DCGでお馴染みの、「Perspective transformation」です。 さらに、奥行き方向の座標に応じてSpriteをスケーリングすることで、リアルな奥行き表現が可能となります。
目的
本記事では上図に示す、2個の座標系 Screen, World を考えます。 Screenは画面上に表示されるSpriteの位置、 Worldはゲーム内で定義されるモデルの位置に対応します。
従来はSpriteの描画位置指定には通常cocos2d::Point
クラスを使用してきましたが、今回は奥行きを表現するためにそのサブクラスPointWithDepth
を使用します。 PointWithDepth::SetWorldPosition
メソッドによってWorld座標系によってSpriteの位置を指定した上で、描画の処理のためにはScreen座標系の座標を返すcocos2d::Point
クラス(アップキャストしたもの)及び画像のスケール値を返すPointWithDepth::GetScale
メソッドを実装します。
座標変換
図に示す座標系において、z軸に直交する平面上への透視投影を考えると、
ここで無限遠点( )を考えると、定数の設定が直感的になります。
「遠くにあるものは小さく見える」ことを表現するには、次式によりスケール比率 を指定します。
実装
本機能の実装前は、キャラクタの位置はcharacter->setPosition(cocos2d::Point(x,y))
のように実施していました。 下記のようなクラスを新たに定義することで、自然な拡張ができます。
class PointWithDepth:public cocos2d::Point{
...
void SetWorldPosition(float local_x,float local_y,float local_z){
m_scale=(Z_S-Z_E)/(local_z-Z_E);
cocos2d::Point::x=X_INF+m_scale*local_x;
cocos2d::Point::y=Y_INF+m_scale*local_y;
}
float GetScale(void){
return m_scale;
}
private:
float m_scale;
};
PointWithDepth
クラスを使用することにより、キャラクタの位置指定を 座標により次のように指定できます。
PointWithDepth point;
point.SetWorldPosition(local_x,local_y,local_z);
character->setPosition(point);
character->setScale(point.GetScale());
以上の実装により、3DCGでperspective transformationを用いたような演出が2次元のライブラリで実現します。 この機能により得られる出力の例を下図に示します。
まとめ
2次元空間の点を表すクラスを数行拡張するだけで、スプライトが3次元空間に存在するような表現が可能になりました。
この記事で取り扱ったような表現は trial and error によるパラメータ調整で「なんとなく」実装することもできますが、コードの品質を保つためにも、表現したい対象を数学的・体系的に捉えた上で実装することが重要であると考えています。