ヒントとコツ1:遅延関数呼び出し(ファンクターマネージャー)

ゲームを作成するとき、開発者はしばしばそのようなタスクに遭遇します-どのイベントを完了する必要がありますが、現時点ではなく、しばらくしてからです。 この問題はさまざまな方法で解決されます。 ほとんどの場合、ゲームオブジェクトには独自の内部タイマーがあり、(オブジェクトに追加のコードを追加することで)望ましい遅延を実装できます。 ただし、独自のタイマーを持たないオブジェクトで遅延メソッド呼び出しを行う必要がある場合があります。たとえば、ウィンドウ、ラインを非表示にし、しばらくしてからアイコンやエフェクトを表示し、別の操作を行いますが、今すぐではなく、遅延を伴います。 1回または数回。



そのため、そのような類似の目的のために、私の同僚は、Functor Managerと呼ばれるユニバーサルシステムを開発しました(そのようなシステムの名前には別の用語があるかもしれませんが、彼らが教えてくれれば嬉しいです)。



当初、コードはC ++で記述され、過去のプロジェクトで使用されていましたが、現在はC#のプロジェクトがあるため、C#で実装を行います。 簡単な概念と実装コード(C#)カットの下。







ファンクター





ファンクトールは、どれだけ時間がかかり、どの関数を呼び出すかを覚えているクラシックです。



したがって、実際にはファンクターは次のもので構成されます。

Guid-ファンクターを作成するときに生成される一意の識別子。 この識別子は、必要に応じてファンクターを削除できるようにするために必要です。

_deltaTimeは内部タイマーです。 関数呼び出しが発生するまでの時間を記録します。

_func-タイマーが切れた後にコードを実行する必要がある関数へのポインター。 この関数は値を返します-ファンクタはメソッド呼び出しを繰り返す必要がある期間(秒単位)を経過します。 関数が値0.0fを返した場合、ファンクターは破られます。

_funcArgは関数の引数です。 ここにオブジェクトへのポインタ、またはその他の必要な情報を渡すことができます。



ファンクタコードではすべてが単純です。フィールドを埋めるコンストラクタであり、説明に値する唯一のメソッドはProcessメソッドです。 カウンターはティックの数だけ減少し、ゼロに達すると関数が呼び出されます。 戻り値はタイマーに割り当てられます。



ファンコード:

public delegate float Func(object funcArg); public class HOFunctor { float _deltaTime = 0; Func _func = null; object _funcArg = null; Guid _id = Guid.Empty; public HOFunctor(float deltaTime, Func func, object funcArg) { _id = Guid.NewGuid(); _deltaTime = deltaTime; _func = func; _funcArg = funcArg; } public HOFunctor(float deltaTime, Func func) : this(deltaTime, func, null) { } public bool Process(float deltaTime) { if (_func != null) { _deltaTime -= deltaTime; if (_deltaTime <= 0) { _deltaTime = _func(_funcArg); } } return _deltaTime > 0; } public Guid ID { get { return _id; } } }
      
      







ファンクターマネージャー





ファンクタマネージャは、ファンクタのリストを格納するシングルトンのクラスです(実際には2つのリストがあります。詳細は以下を参照)。



そのため、マネージャーには2つのリストがあり、そのうちの1つは現在アクティブであり、2つ目はアクティブリストの実行時に追加されたものを含む、新しく追加されたファンクターで満たされています。 変数_currentIndexもあり、どのリストが現在アクティブであるかが決定されます。



ほとんどのメソッドも直感的です(AddFunctor(...)-ファンクタをリストに追加し、RemoveFunctor()-リストから削除します)。 説明する価値があるのは、ProcessFunctors(float delta)メソッドだけです。 現在のリストが取得され、各ファンクターはProcessメソッドと呼ばれるため、時間をカウントし、必要に応じて実行されます。ファンクターがゼロ以外の値を返した場合(つまり、再度呼び出す必要がある時間)、新しいリストに追加されます。 最後に、アクティブリストがクリアされます。



マネージャーコード:

 public class HOFunctorMgr { #region Private fields private static HOFunctorMgr _instance = null; protected Dictionary<Guid, HOFunctor>[] _functors = { new Dictionary<Guid, HOFunctor>(), new Dictionary<Guid, HOFunctor>() }; int _currentIndex = 0; private HOFunctorMgr() { } #endregion public static HOFunctorMgr Instance { get { if (_instance == null) { _instance = new HOFunctorMgr(); } return _instance; } } #region Public methods public Guid AddFunctor(float deltaTime, Func func, object funcArg) { return AddFunctor(new HOFunctor(deltaTime, func, funcArg)); } public Guid AddFunctor(float deltaTime, Func func) { return AddFunctor(new HOFunctor(deltaTime, func, null)); } public Guid AddFunctor(HOFunctor functor) { if (functor != null && !_functors[_currentIndex].ContainsKey(functor.ID)) { _functors[_currentIndex].Add(functor.ID, functor); return functor.ID; } return Guid.Empty; } public void ProcessFunctors(float delta) { int indexToProcess = _currentIndex; _currentIndex ^= 1; foreach (HOFunctor f in _functors[indexToProcess].Values) { if (f.Process(delta)) { AddFunctor(f); } } _functors[indexToProcess].Clear(); } public void RemoveFunctor(Guid id) { if (_functors[0].ContainsKey(id)) { _functors[0].Remove(id); } if (_functors[1].ContainsKey(id)) { _functors[1].Remove(id); } } #endregion }
      
      







プロジェクト統合





プロジェクトへの統合は1行で行われます。 チャレンジを追加する必要があります



HOFunctorMgr.Instance.ProcessFunctors(デルタ);



ゲームループでは、最初にマネージャーファンクタークラスをプロジェクトに自然に追加します。 私の場合(NeoAxisエンジン)、これはゲームウィンドウの保護されたオーバーライドvoid OnTick(float delta)メソッドです。



deltaは、前の呼び出しからの経過時間(秒単位)です。



すべてがとてもシンプルです。



使用例





シングルコール:



 GameEntities.HOFunctorMgr.Instance.AddFunctor(2.0f, arg => { HidePuzzleWindow(); return 0.0f; }, null );
      
      







2秒後、HidePuzzleWindow()が呼び出され、パズルウィンドウが消えます。



複数呼び出し:



 ... int k = 5; HOFunctorMgr.Instance.AddFunctor(5, arg => { GameMap.Instance.AddScreenMessage(string.Format("{0}", arg)); if (k-- >= 0) { return 5; } return 0; }, "test");
      
      







ファンクターが5回呼び出され、テストという単語が表示され、そのたびに5秒間隔で表示されます。 ちなみに、間隔は変更できます。



静的メソッドを呼び出します。

 static float MyMethod(object arg) { if (arg != null) { ... } return 0; } HOFunctorMgr.Instance.AddFunctor(10, MyMethod, someObject);
      
      







10秒後、静的なMyMethodメソッドが呼び出され、someObjectが引数として渡されます。



使用について





プロジェクトでは、非商用および商用の両方のコードを変更および/または使用できます。



フルクラスコード





クラスコード、NeoAxis用にシャープ化された名前空間。



 using System; using System.Collections.Generic; using System.Text; namespace GameEntities { public delegate float Func(object funcArg); public class HOFunctor { float _deltaTime = 0; Func _func = null; object _funcArg = null; Guid _id = Guid.Empty; public HOFunctor(float deltaTime, Func func, object funcArg) { _id = Guid.NewGuid(); _deltaTime = deltaTime; _func = func; _funcArg = funcArg; } public HOFunctor(float deltaTime, Func func) : this(deltaTime, func, null) { } public bool Process(float deltaTime) { if (_func != null) { _deltaTime -= deltaTime; if (_deltaTime <= 0) { _deltaTime = _func(_funcArg); } } return _deltaTime > 0; } public Guid ID { get { return _id; } } } public class HOFunctorMgr { #region Private fields private static HOFunctorMgr _instance = null; protected Dictionary<Guid, HOFunctor>[] _functors = { new Dictionary<Guid, HOFunctor>(), new Dictionary<Guid, HOFunctor>() }; int _currentIndex = 0; private HOFunctorMgr() { } #endregion public static HOFunctorMgr Instance { get { if (_instance == null) { _instance = new HOFunctorMgr(); } return _instance; } } #region Public methods public Guid AddFunctor(float deltaTime, Func func, object funcArg) { return AddFunctor(new HOFunctor(deltaTime, func, funcArg)); } public Guid AddFunctor(float deltaTime, Func func) { return AddFunctor(new HOFunctor(deltaTime, func, null)); } public Guid AddFunctor(HOFunctor functor) { if (functor != null && !_functors[_currentIndex].ContainsKey(functor.ID)) { _functors[_currentIndex].Add(functor.ID, functor); return functor.ID; } return Guid.Empty; } public void ProcessFunctors(float delta) { int indexToProcess = _currentIndex; _currentIndex ^= 1; foreach (HOFunctor f in _functors[indexToProcess].Values) { if (f.Process(delta)) { AddFunctor(f); } } _functors[indexToProcess].Clear(); } public void RemoveFunctor(Guid id) { if (_functors[0].ContainsKey(id)) { _functors[0].Remove(id); } if (_functors[1].ContainsKey(id)) { _functors[1].Remove(id); } } #endregion } }
      
      










All Articles