ISurface/CFastDraw/CFastPlane/CPlane




すべてのサーフェースの基底クラス。インターフェースクラスですが、このクラス自体が、ビットマップ等のファイルの読み込み、保存能力を持ち、基本的なBltter/Effecter等が用意されています。

現在のところ、ISurfaceの具象クラスとしては、CFastPlaneが有りますが、CFastPlaneを生で扱うことはせず、smart_ptr<ISurface>に入れて使うのが好ましいです。将来的に、CFastPlane以外の具象クラスを用意するかも知れないので、そのときに、以前のソースが使いまわせるからです。

CFastPlane/CFastDrawについては、yaneSDK2ndにも同名のクラスが存在しますので、そちらも参照してください。

今回、CFastPlaneのコンストラクタは、CFastDrawのポインタをパラメータとして必要とします。ということは、

枠-1
    CFastDraw draw;
    CFastPlane plane(&draw);


としないと使えないということです。さらに言えば、それをsmart_ptr<ISurface>に代入して使うとなると..

枠-2
    CFastDraw draw;
    smart_ptr<ISurface> surface(new CFastPlane(&draw));


このようにして使うということです。しかし、CFastDrawは描画のための母体であり、アプリケーションに唯一か、あるいは、ウィンドゥひとつに対して1つでしょう。ということは、CFastDrawは、CApp(アプリケーションクラス)のメンバとして持たせているわけで、末端のシーンクラス(⇒ゲームは、通例、シーン管理を行なう。「天才ゲームプログラマ養成ギプス」参照のこと)は、このCFastDrawのポインタを得るために、CAppにアクセスしないといけないということになります。

これは、非常にダサいです。ダサい上に面倒くさい。これでは、yaneSDK2ndを改悪か?と言われても仕方がないです。さすがに、わたしも、こんなことを強要しません。

次のCPlaneクラスを使います。これは、smart_ptr<ISurface>のwrapperです。

枠-3
class CPlane {
/**
    smart_ptr<ISurface> といちいち書くのは面倒極まりないので..
*/
public:
    CPlane();

    /// ポインタのふりをするための仕掛け
    ISurface& operator*() const  {return *get(); }
    ISurface* operator->() const {return get();  }

    /// ISurfaceへの暗黙の変換
    operator ISurface* () const { return get(); }

    ISurface* get() const { return p_.get(); }

    static void SetFactory(smart_ptr<IPlaneFactory>&pFactory)
        { pFactory_ = pFactory; }
    static smart_ptr<IPlaneFactory> GetFactory()
        { return pFactory_; }

protected:
    smart_ptr<ISurface> p_; // p_; ←って泣いてるの?

    static ThreadLocal<smart_ptr<IPlaneFactory> > pFactory_;
};


見てのとおり、factoryを設定しておいて、生成に関しては、そのfactoryを用いて生成、ISurface*へ暗黙で変換でき、かつoperator->をオーバーロードしてあるので、ISurface*かのように使えるというクラスです。

このfactoryの設定という部分についてもう少し見ていきましょう。factory自体は、次のようにISurface*を返すものです。

list-1 factory
class IPlaneFactory {
public:
    virtual ISurface* CreateInstance() = 0;
};


これを派生させ、CFastPlaneを生成するfactoryは、

list-2
struct CPlaneFactoryFastPlane : public IPlaneFactory {
/**
    IPlaneFactory派生クラス。こいつはCFastPlaneを生成するためのfactory
*/
    CPlaneFactoryFastPlane(CFastDraw*p) : pDraw_(p) {}
    virtual ISurface* CreateInstance(){ return new CFastPlane(pDraw_);}
protected:
    CFastDraw* pDraw_;
};


これです。見てのとおり、このfactoryは、コンストラクタでCFastDraw*を取り、CreateIntanceでは、そのCFastDraw*をCFastPlaneのコンストラクタに渡してあります。

ついでに、こんなfactoryをCPlane::SetFactoryで設定してやること自体が面倒なので、CFastDrawを抱え込んで、自動的に、CPlane::SetFactoryしてくれるクラスを用意してあります。

list-3
class CFastPlaneFactory {
/**
    このクラスは、コンストラクタのなかで、
    IPlaneFactory派生クラスをCPlaneに対して設定する
    これにより、このクラスをCAppに持たせさえすれば、
    そのスレッドは、CPlaneは、すべてCFastPlaneを意味することになる
*/
public:
    CFastDraw*  GetDraw() { return m_vFastDraw.get(); }

    CFastPlaneFactory();

protected:
    smart_ptr<CFastDraw>    m_vFastDraw;    //  FastDraw持ってますねん
};


このコンストラクタで、CPlane::SetFactoryを行なうので、以降、CPlaneと書けば自動的にCFastPlaneがnewされることになります。

list-4 CApp.h
class CApp : public CAppFrame {
public:
    virtual void    MainThread();   //  これがメインスレッド

    CFastDraw* GetDraw() { return drawFactory_.GetDraw(); }
protected:
    CFastPlaneFactory   drawFactory_;
};


などと書いておけば、以降、サーフェース(CFastPlane)が使いたいときは、単にCPlaneと書くだけで良いわけです。

list-5
    CPlane  plane;
    plane->Load("moemoe.jpg");

    //  回転描画
    GetDraw()->GetSecondary()->RotateBltFast(plane,0,0,nAngle);


というようにポインタ感覚で使えます。

注意すべきことは、CFastPlaneFactoryのコンストラクタで、factoryを設定しているということです。クラスのメンバ変数は、宣言順に初期化されますので、CFastPlaneFactoryのメンバより前にCPlaneのメンバを用意してしまうと、その初期化段階でfactoryが設定されていないことになり、ダメなのです。

枠-4 こういう順番で書いてはいけない
    CPlane  plane_;
    CFastPlaneFactory planeFactory_;


まあ、CAppクラスに、CPlaneを持たせること自体無いと思うので、別に問題は無いと思いますが..。(factoryが設定されていない状態でCPlaneが生成されると、CRuntimeException例外がスローされるようになっています)