1

DirectX9でコールバックイベントシステムを作成しようとしています。メソッド関数ポインターを使用して、マウスクリックのイベントをトリガーしようとしています。しかし、私はいくつかの問題を抱えています。私のゲームは、ゲーム状態マネージャーを使用してレンダリングを管理しています。私のゲームステートはすべて、基本クラスAbstractGameStateから派生しています。

この特定のメソッドを持つスプライトオブジェクトがあります:

m_NewGameSprite->OnClick(this, &MainMenuState::StartGame);

MainMenuStateは私のゲームが存在する現在のゲーム状態であり、StartGameはこのクラスのvoidメソッド部分です。ユーザーがクリックしたときに実行できるように、関数ポインターをスプライトクラス内の変数に格納したいと思います。

template <typename T>
void OnClick(GameState* LinkedState, void (T::*EventPointer)())
{
    m_LinkedGameState = LinkedState;
    m_EventPointer = EventPointer; // <- Doesnt compile
}

ポインタをダウンキャストしようとしましたが、実際には機能しませんでした。

私のスプライトクラスには、これら2つの変数も含まれています

void                (GameState::*m_EventPointer)();
GameState*          m_LinkedGameState;

どんな助けでもいただければ幸いです

4

5 に答える 5

2

あなたの課題がうまくいかない理由はよくわかりません。まもなくlitbがその理由を説明します。Boost.Functionは、美しく、汎用的で、タイプセーフで標準的な関数オブジェクトであり、ほとんどすべての状況で関数ポインターの代わりに使用できます。私はこれを行います:

typedef boost::function0<void> Event;
Event m_Event;

イベントクラスは、呼び出したいオブジェクトを含む、関数呼び出しの状態をカプセル化することに注意してください。通常、Boost.Bindを使用してクロージャを作成しますが、free関数やその他の関数オブジェクトにも簡単にバインドできます。

void OnClick(Event &event)
{
  m_Event=event;
}

このように呼んでください:

OnClick(boost::bind(&MainMenuState::StartGame, this));

このスキームでは、「リンクされたゲームの状態」を実際に保存する必要はありません。これは、イベントオブジェクトにカプセル化されています。

于 2009-06-25T23:55:12.330 に答える
1

ここでの問題は、そのパラメーターを使用してどのインスタンスStartGameでも呼び出すことができないことです。タイプがのパラメータでのみ呼び出すことができます。GameStatethisthisMainMenuState

で定義されたメソッドをvoid (GameState::*)()指すには、メソッドはで定義されMainMenuStateている仮想メソッドである必要がありますGameState

メンバー関数ポインターを格納する代わりに、次のようなものを使用して「コマンドオブジェクト」(またはファンクター)ポインターを格納することをお勧めします。

class Callback
{
public:
    virtual void Execute() = 0;
};

そして、次のような実装を定義します。

template <typename TClass>
class MemberCallback : public Callback
{
public:

    MemberCallBack(TClass * pThis, void (TClass::*pFunc)() )
    {
        m_pThis = pThis;
        m_pFunc = pFunc;
    }
    void Execute()
    {
        m_pThis->*m_pFunc();
    }

private:
    TClass * m_pThis;
    void (TClass::*m_pFunc)();
};

次に、タイプのメンバーを定義できますCallback *

于 2009-06-25T23:56:24.027 に答える
1

私は非常によく似た問題を抱えていました。私がこれを正しく読んでいるなら、あなたはほとんどActionEventJavaのようなシステムを書き込もうとしています。これは次のようになります:

Component.addActionListener( new ActionEventListener( new ActionEvent(
            public void execute() { /* Component clicked Do Something */ } )));

C ++では、以下に示すように、非常によく似た処理を行う必要があります。

私の意見では、メソッド呼び出しを実際に再利用して自由に変更できるため、これははるかに優れています。これにより、柔軟性が大幅に向上します。

Event.hpp

#pragma once

class Event 
{
    public:
        Event(void) { }
        ~Event(void) { }

    public:
        //Stores a function to callback. Example shown in Main.cpp
        void operator += (void (*callback)())
        {
            this->callback = callback;
        }

        //Executes our stored call back method at any given time.
        void execute(void)
        {
            callback();
        }

        //Variable of type VOID* -> This is a function which is stored + executed.
        private:
            void (*callback)();
};

Main.cpp

#include <iostream>
#include "Event.hpp"

using namespace std;

void print(void)
{
    cout << "Hello This Works" << endl;
}

void print2(void)
{
    cout << "Success again!" << endl;
}

int main()
{
    Event task_event;
    task_event += print;
    task_event.execute();
    task_event += print2;
    task_event.execute();
    system("pause");
    return 0;
}
于 2012-08-11T00:44:19.487 に答える
0

なぜそこでテンプレート関数を使用しているのですか? GameState以外のOnClick値については、コンパイルまたは機能しません。T

ただし、この状況では、通常、メンバー関数へのポインターは使用しません。関数オブジェクトを作成し、オーバーロードoperator()して、代わりにそれらを渡します。これはより良い方法です。構文がより明確になり、必要に応じて関数オブジェクトに状態を格納できます。

于 2009-06-25T23:49:22.777 に答える
0

パラメータは次のように取る必要があります。

void (GameState::*EventPointer)()

一度ポインタになると、そのようにダウンキャストすることはできません(同じポインタが基本クラスに存在するかどうかについての情報はありません)。これが機能するには、関数がGameState上にある必要があります(仮想の可能性があります)

でも!コールバック(基本的にはオブザーバーパターン)を実行しているので、 boost::signalsをよく見ることができます。これすべて(およびそれ以上)を実行します。GameStateに関数を追加する必要はありません。

class Foo {
 // ...
 template <typename T>
 boost::signals::connection OnClick(const boost::function<void ()>& func)
 {
    return sig.connect(func);
 }

 void FireClick() { sig_(); }
private:
  boost::signal<void ()> sig_;
};

int this_will_be_called() { }

Foo f = // ...;

f.OnClick(boost::bind(&this_will_be_called));
于 2009-06-26T00:06:05.740 に答える