3

Inputと"EventSystem"の2つのクラスがあります。EventSystemは、「内部アプリケーションイベント」を処理する内部(システムに依存しない)クラスです。入力は依存クラス(システム依存)であり、キー/ボタンイベントを処理し、それらをキーイベントと「EventSystem」の両方にマップします。現在データをどのように通過させているかをお見せします。内部は非常にきれいに見えますが、外部は非常に汚れています。カスタム値を渡すためのより良い方法、またはより簡単な方法を知っている人はいますか?

EventSystem:

// Raisable Events
public enum EventType { Action, Cancel };

// Base for custom arguments
public class EventArguments
    {
        public double time;

        public EventArguments(double _time)
        {
            time = _time;
        }
    }


// Event Delegate (invoked on raise)
public delegate void Event(EventArguments eventArgs);

// full class
static class EventSystem
{
    private static Dictionary<EventType, Event> eventMapping = new Dictionary<EventType, Event>();

    public static void HookEvent(EventType eventType, Event _event)
    {
        if (eventMapping.ContainsKey(eventType))
        {
            eventMapping[eventType] += _event;
        }
        else
        {
            eventMapping.Add(eventType, _event);
        }
    }

    public static void RaiseEvent(EventType eventType, EventArguments args)
    {
        if (eventMapping.ContainsKey(eventType))
        {
            eventMapping[eventType].Invoke(args);
        }
        else
        {
            // do nothing
        }
    }
}

私の入力引数は単にEventArgumentsを継承します。

// Inherits EventArguments (double time) and adds it's own, "bool pressed"
class KeyInputArguments : EventArguments
{
    public bool pressed;

    public KeyInputArguments(double time, bool _pressed) :
        base(time)
    {
        pressed = _pressed;
    }
}

キーが押されると、キー(入力)イベントが発生し、キーが内部イベントにマップされているかどうかを確認して発生させます。別のクラス(Config)は、キーをイベントにマッピング/バインドするためのすべての構成を処理します。

// Raise on press
EventSystem.RaiseEvent(eventType, new KeyInputArguments(time, true));
// [...]
// Raise on release
EventSystem.RaiseEvent(eventType, new KeyInputArguments(time, false));

最後に、イベントを発生させるために、イベントへのキーを登録する必要があります(これは「外部」コードです)

// Hook our "Enter" function into the Action event
EventSystem.HookEvent(EventType.Action, Enter);

// [...]

public void Enter(EventArguments eventArg)
{
    if (((KeyInputArguments)eventArg).pressed == false)
    {
        Error.Break();
    }
}

' ((( 'を見るまでは、すべてが素晴らしいです。これは、C#およびOOPプログラミング全般に関する私の限られた知識の醜い結果です。

イベントデリゲートは明示的にEventArgumentsを必要とするため、Enterメソッド引数を変更できません。(KeyInputArgumentsはそれを継承しますが?)。また、eventArgをKeyInputArgumentsにキャストするのにそれほど時間がかかる理由もわかりません。

最後に、これも試してみました(あまり好きではありませんが)

KeyInputArguments keyInputArguments = (KeyInputArguments)eventArg;
            if (keyInputArguments.pressed == false)

カスタムデータが必要な理由は、ゲームパッドなどの複数の形式の入力から入力を受け取ることを計画しているためです。これは、システムに依存するデータ(ゲームパッドデバイス情報)を独立した引数に処理できることを意味します。これにより、システムに依存するデータがInputクラスに制限され、イベントシステムは内部的に独立したものとして活用されます。 私がしていることのためのより良い方法はありますか?

4

2 に答える 2

2

このコードをプロジェクトのどこかに置きます。

public static class SomeClassName
{
    public static T CastTo<T>(this object source)
    {
        return (T)source;
    }
}

これにより、次のように書くことができます

if(!eventArg.CastTo<KeyInputArguments>().pressed)
    Error.Break();

左から右への順序が維持されるので、私はこれが本当に好きです。これは、linq を記述するときに特に適しています。

「KeyInputArguments はそれを継承していますが?」という質問への回答です。ここでの問題は、基本クラスを使用する汎用メソッドを定義したため、継承されたクラスを認識できないことです。その問題を修正するには、そのメソッドでジェネリックを使用する必要があります。たとえば、イベント タイプをジェネリック パラメータとして渡すなどです。

public static void HookEvent<TEventType>(....)
于 2012-08-06T05:47:18.847 に答える
0

私はMikeKullsの回答「HookEvent」と友人からのアドバイスを混ぜ合わせて、これを思いついた。

// Event types to raise.
enum EventType { Action, Cancel };

// Base abstract class to use in the generic type field
abstract class BaseEvent
{
    public double time;

    protected BaseEvent(double _time)
    {
        time = _time;
    }
}

// Event delegate, T is a sub of BaseEvent that contains the data we need to send
delegate void EventDelegate<T>(T eventClass) where T : BaseEvent;

// Event class
static class EventSystem
{
    // EventClass is the storage of all the generic types and their mapping to an event delegate.
    private static class EventClass<T> where T : BaseEvent
    {
        public static Dictionary<EventType, EventDelegate<T>> eventMapping = new Dictionary<EventType, EventDelegate<T>>();
    }

    /// <summary>
    /// Hooks an event to a delegate for use with EventSystem.RaiseEvent
    /// </summary>
    /// <typeparam name="T">Sub-class of BaseEvent that your delegate will be receiving</typeparam>
    /// <param name="eventType">Type of event to hook to</param>
    /// <param name="_event">The callback</param>
    public static void HookEvent<T>(EventType eventType, EventDelegate<T> _event) where T : BaseEvent
    {
        if (EventClass<T>.eventMapping.ContainsKey(eventType))
        {
            EventClass<T>.eventMapping[eventType] += _event;
        }
        else
        {
            EventClass<T>.eventMapping.Add(eventType, _event);
        }
    }

    /// <summary>
    /// Raises an event and calls any callbacks that have been hooked via HookEvent
    /// </summary>
    /// <typeparam name="T">The sub-class of BaseEvent you are sending</typeparam>
    /// <param name="eventType">Type of event you are raising</param>
    /// <param name="args">A subclass of BaseEvent containing the information to pass on</param>
    public static void RaiseEvent<T>(EventType eventType, T args) where T : BaseEvent
    {
        if (EventClass<T>.eventMapping.ContainsKey(eventType))
        {
            EventClass<T>.eventMapping[eventType].Invoke(args);
        }
        else
        {
            // do nothing
        }
    }
}

私は次のように入力を設定します:

class KeyEvent : BaseEvent
    {
        public bool pressed;

        public KeyEvent(double _time, bool _pressed)
            : base(_time)
        {
            pressed = _pressed;
        }
    }
// [...]
// Key down
EventSystem.RaiseEvent<KeyEvent>(eventType, new KeyEvent(time, true));
// Key up
EventSystem.RaiseEvent<KeyEvent>(eventType, new KeyEvent(time, false));

ハンドラー/デリゲートを次のように設定します。

EventSystem.HookEvent<KeyEvent>(EventType.Action, Enter);
// [...]
public void Enter(KeyEvent keyEvent)
{
    if (keyEvent.pressed == false)
    {
        Error.Break();
    }
}
于 2012-08-12T17:38:01.523 に答える