10

編集:あなたがこの巨大な質問を読むのを気にすることができないならば、私は一番下に要約を置きました。

私は現在、コーディングの演習として、C#で作成するテキストアドベンチャーの一種の「フレームワーク」に取り組んでいます。このフレームワークでは、可能なアクションは「Interaction」クラスによって定義されます。

潜在的な「アクション可能な」オブジェクトは、インベントリアイテム(スティック、ガン、ソード)、環境アイテム(壁、ドア、窓)、キャラクター(人、動物)です。これらのそれぞれには、相互作用のリストであるプロパティがあります。現時点では、インタラクションは基本的に「アクション/レスポンス」の名前と値のペアです。「スマッシュウィンドウ」と入力すると、プレーヤーが利用できる可能性のあるすべてのアクション可能なアイテムが調べられ、件名(この場合は「ウィンドウ」)と一致します。次に、アクションが「スマッシュ」であることがわかり、ウィンドウのインタラクションのリスト(環境アイテム)を検索してスマッシュアクションの応答を取得し、コンソールに書き込みます。

これですべて完了ですが、ここで私が立ち往生している点があります。

アクションには、潜在的な相互作用ごとに異なる、潜在的な結果がいくつもあります。これらは:

-おそらく2番目のサブジェクトとの相互作用を調べて、アクションの結果を説明する応答を返します

どちらか -アクションの主題(インベントリアイテム、環境アイテム、またはキャラクター)は、その説明EGを変更します。「パンチウォール」は、壁の説明を変更して壁のへこみを表すことができます。 または -アクションの対象が別のアイテムEGに置き換えられます。「ボトルを壊す」と「ボトル」が「壊れたボトル」に変わるか、「ジョンを殺す」と、ジョンのキャラクターが環境アイテム「ジョンの死体」に置き換えられます。

-先行する変更EGを説明する応答を返します。「ボトルの破片が床に散らばっています。」

-エリアの説明が変更されました。例えば。「スマッシュ電球」を使用すると、部屋の説明が真っ暗な部屋の説明に変わります。

-アイテムは、インベントリまたは環境EGに追加/削除されます。「ボトルを拾う」。これでインベントリにボトルがあり、ボトルは環境から削除されます。

-移動に利用できる方向とそれらがつながる領域が変更されますEG。「鍵でドアのロックを解除する」を使用すると、東に別の部屋に移動できます

-プレイヤーは新しいエリアEGに移動します。「北へ行く」はあなたを別のエリアに連れて行きます。

特定のインタラクションがこれらの結果のどれを呼び出す必要があるかを一般的な方法で何らかの方法で決定し、それらを呼び出す必要があります。アクションは、これらの結果の多く、または1つだけを使用する可能性があります。

たとえば、アイテムがボトルの場合:

ボトルに水を入れる」は、最初に、ボトルに水を入れたことを示す応答を返します。次に、「ボトル」アイテムを「ボトル入り飲料水」アイテムに置き換えます。これは、応答を返すこととアイテムを置き換えることの2つの結果です。

次に、「窓にボトル入り飲料水を投げる」としましょう。これはもっと複雑です。それは最初に起こった出来事を説明する応答を返します、ボトルと窓は両方とも砕けて、水はいたるところに行きます。ボトルはプレイヤーのインベントリから削除されます。次に、「水のボトル」は「壊れたボトル」に置き換えられ、「窓」は「壊れた窓」に置き換えられます。エリアの説明もこれを反映して変更されます。これは5つの結果であり、応答を返し、在庫からアイテムを削除し、2つのアイテムを置き換え、現在のエリアの説明を更新します。

ご覧のとおり、「インタラクション」ごとに、そのアクションの結果がどうなるかを定義し、アイテム、プレーヤー(インベントリ用)、エリアなどの他のオブジェクトを適切に更新できる一般的な方法が必要です。

ご不明な点がございましたら、お詫び申し上げます。ご不明な点がございましたら、できる限り明確にさせていただきます。

編集:呼び出すためのいくつかのメソッド(およびそれらのパラメーター)を渡すことができるインタラクションのメソッドを定義する方法はありますか?返される最初の応答はデフォルトの必須の結果であり、指定されている場合は追加の応答が存在する可能性があります。

たとえば、上記の例では、最初のインタラクション「水で満たす」の場合、応答(「ボトルに水を入れました」)を返し、「水で満たす」を置き換えるReplaceItemメソッドを呼び出すように指示します。 「ボトル」の件名に「ボトル入り飲料水」を添えます。

2番目のインタラクションでは、応答を返すように指示し(「ボトルが空中を駆け抜けて...」)、アクションの件名でRemoveFromInventoryを呼び出し、ボトルでUpdateStatusを呼び出します(「ボトルが壊されました」)。ウィンドウ(「ウィンドウが壊された」)およびUpdateAreaDescriptionを呼び出して、現在の領域の説明を変更します(「あなたは単一のウィンドウのある部屋に立っており、ガラスが粉々に砕かれています」)。

それは実現可能ですか?考えられるさまざまな相互作用のために、これをできるだけ一般的にしようとしています。

編集2:さらに明確にし、問題を要約しようとする:

私のゲームには、アクション可能なオブジェクト(ボトル、壁、ジョン)があります。各Actionableオブジェクトには、プレーヤーがそれらと対話する方法を説明するインタラクションオブジェクトのリストがあります。現時点では、インタラクションには「Name」プロパティ(「throw」、「hit」、「break」)があり、Response(「Youthrow」)を返します。

私が解決しようとしている問題は、インタラクションが特定のインタラクションごとに異なる他の多くのことも実行する必要があるということです。ガラス瓶を例にとってみましょう。

「ガラス瓶を投げる」
-応答が返されます(「ガラス瓶を投げました。」)
-「瓶」はプレイヤーのインベントリから削除されます。
-変更を反映するために、は新しいものに置き換えられます。(「ボトル」は「壊れたボトル」に置き換えられました)。
-2番目の応答が返されます(「ガラス瓶の破片が床に散らばっています」)。

「窓にガラス瓶を投げる」
-応答が返されます(「窓にガラス瓶を投げました。」)
-オブジェクト「ボトル」は、プレイヤーのインベントリから削除されます。
-オブジェクトは、変更を反映するために新しいオブジェクトに置き換えられます。(「ボトル」は「壊れたボトル」に置き換えられました)。
-2番目のオプションのオブジェクトは、変更を反映するために新しいオブジェクトに置き換えられます。(「ウィンドウ」は「壊れたウィンドウ」に置き換えられました)。
-現在のエリアの「説明」プロパティが更新されます。(「あなたは、壊れた窓が1つある部屋に立っています。」)

インタラクションを作成するときに、サブジェクトのステータスの変更や現在のエリアの説明の変更など、インタラクションが実行する追加のアクションをどのように変更できますか?

上記のアクションの例がさらに必要な場合は、お知らせください。さらにいくつか実行します。

4

7 に答える 7

2

認識できる動詞の数を決めてから、オブジェクトごとに、それらの動詞のどれに応答できるかを決める必要があると思います。

オブジェクト認識動詞をロックする

  • 見て
  • UseItemOn(Key001、LockPicks、Sledgehammer、...)
  • パンチ

そうすれば、「<object>を<verb>することはできません>」のような応答で認識できない動詞を一般的に処理でき、イベントなどで認識できる動詞を処理できます。

編集

あなたのコメントによると、私は明らかにあなたの質問をスキャンしました(私には長すぎます)。それでも、違いはわかりません。重要なのは、オブジェクトがイベントに参加するということです。ボトルの観点からは、壁にぶつかります。壁の観点からは、ボトルにぶつかります。両方のオブジェクトには、特定の方法で応答する動詞のリストがあります。

したがって、壁がスローされたオブジェクトに応答するように計画している場合は、そのリストにCollide動詞を追加する必要があります。どのオブジェクトとの衝突を気にする必要があるか、そしておそらくそれらのそれぞれについて、特定の大きさの力にどのように応答するかなどを指定する必要があります。

しかし、原理は同じです。どのイベントにも多数の参加者がいて、各参加者は気になる特定の刺激を持ち、それらの刺激については気になる特定の刺激起源オブジェクトを持ちます。それが気になる動詞であるが、その起源が気になるオブジェクトではない場合、それは事実上それを無視します-または何らかのバニラの方法で応答します。

ボトルは壁との衝突に参加します。ボトルの動詞リストには、衝突インタラクションタイプがあります。衝突を気にする単一のオブジェクトがある場合もあれば、Any、AnySolidなどの値を持つ場合もあります。それを設計する方法は無数にあります。いずれの場合も、Wallも参加し、その動詞リストにCollideインタラクションタイプが含まれている場合があります。しかし、それはスレッジハンマーオブジェクトとの衝突だけを気にします-または多分10以上の質量を持つAnySolid ...

インターフェイスを使用してこれを行うこともできます。ICollidibleインターフェイスなどを実装するLootableObjectを使用できます。ICollidible(たとえば、ボトル)がCollideメソッドを実行するときは、特定のパラメーターが必要になります。それは、壊れやすいか、受けた力の大きさ、衝突するオブジェクトが気になるものかどうかなどです。

液体でいっぱいになる可能性があるため、Spillメソッドを持つIContainerインターフェイスと、Drinkメソッドを持つIConsumeableインターフェイスも実装します。これは、Unlock(obj Key)メソッドとPick(int PickSkill)メソッドを持つILockableインターフェイスを実装するロックである可能性があります。これらの各メソッドは、オブジェクトおよびインタラクションの他の参加者の状態に特定の変化をもたらす可能性があります。必要に応じて、イベントを使用してこれを行うことができます。

基本的に、必要な(予測不可能な)レベルを決定してから、相互作用のマトリックスを作成する必要があります(必ずしも物理学ではありませんが、操作する予定のあらゆる種類の相互作用-ピッキングイベント、衝突イベント、飲酒イベント)特定の予測可能なプロパティが含まれます。

于 2010-10-02T13:39:00.397 に答える
1

説明したすべてのアクションは、次のもので構成されています。

  • 動詞(たとえば「投げる」)
  • オブジェクト(たとえば「ボトル」)
  • アクションをさらに説明するオプションの追加パラメーター(たとえば、「ウィンドウで」)

各アクション可能なオブジェクトを共通の祖先から派生したクラスとしてモデル化し、そのクラスにアクション自体を処理させるのはどうでしょうか。何かのようなもの

public interface IObjectBase
{
   bool HandleAction(string verb,string [] params)
}

public class Bottle: IObjectBase
{
   bool HandleAction(string verb,string [] params)
   {
     //analyze verb and params to look for appropriate actions
     //handle action and return true if a match has been found
   }
}
于 2010-10-02T12:00:20.547 に答える
0

プレイヤーと環境の2つがあります(他のプレイヤーもいる可能性があります)。

それらの両方を各インタラクションに渡します。

interaction.ActOn(environment, player);

//eg:
smash.ActOn(currentRoom, hero);

次に、各インタラクションに何をすべきかを考えさせます。

environment.ReplaceObject("window", new Window("This window is broken. Watch out for the glass!");
player.Inventory.RemoveObject("bottle");
player.Hears("The window smashes. There is glass all over the floor! If only John McLane were here...").

環境に実際にウィンドウがあること、プレーヤーにボトルがあることなどを確認するための通常のチェックを行います。

player.Inventory.ReplaceObject("bottle", new BottleOfWater());

インタラクションは、環境、プレーヤー、ボトルなど、システム内のあらゆるものに接続できる共通のインターフェイスになります。重複を削除するために使用できる特定のタイプのインタラクションを作成できる可能性がありますが、始めます。シンプルでそこから行きます。

ダブルディスパッチも参照してください。

于 2010-10-02T12:12:32.097 に答える
0

ああ、私も似たようなことに取り組んでいます。あなたのフレームワークが私のプロジェクトであるテキストアドベンチャークリエーターになるのではないかと思います。

私のアプローチは、ゲーム内のすべての最も基本的なアクションを表すメソッドで構成される一種のAPIを使用することです。次に、「スクリプト」を使用します。これは、基本的に、これらの基本的なアクションの組み合わせを含むメソッドです。これらの基本的なアクションには、次のものが含まれます。

  • メッセージを印刷する
  • オブジェクト/部屋の説明を変更する
  • オブジェクトを「ロック」または「ロック解除」します。これは、「ベルトを調べる」が「ここにベルトが見当たらない」と言うことを意味します。「死体を調べる」が実行され、「死体の腰に光沢のあるベルトがある」ことを学習します。
  • 部屋の出口をロックまたはロック解除する
  • プレーヤーをある部屋に移動します
  • プレイヤーのインベントリに何かを追加/削除する
  • いくつかのゲーム変数を設定/変更します。「movedGlowingRock=true」または「numBedroomVisits=13」など。

など...これは私が現在考えていることです。これらはすべてAPIクラスのメソッドであり、必要に応じてさまざまなパラメータを取ります。

今、部屋があります。部屋にはオブジェクトがあります。特定のコマンドは各オブジェクトに対して有効です。簡単な方法の1つは、各部屋のオブジェクトに許可されたコマンドの辞書を保持させることです。スクリプトは、アクションスクリプトを指すデリゲートです。これを熟考してください:

delegate void Script();

class GameObject
{
    public Dictionary<string, Script> Scripts {get; set;}
    public string Name {get; set;}

    //etc...
}

そして、関連するRoomインスタンスに保存されているスクリプト:

    //In my project, I plan to have such an abstract class, and since it is a game _creator_, the app will generate a C# file that contains derived types containing info that users will specify using a GUI Editor.
abstract class Room
{
    protected Dictionary<string, GameObject> objects;

    public GameObject GetObject(string objName) {...//get relevant object from dictionary}
}


class FrontYard : Room
{

    public FrontYard()
    {
        GameObject bottle;
        bottle.Name = "Bottle";
        bottle.Scripts["FillWithWater"] = Room1_Fill_Bottle_With_Water;
        bottle.Scripts["ThrowAtWindow"] = Room1_Throw_Bottle_At_Window;
        //etc...
    }

    void void Room1_Fill_Bottle_With_Water()
    {
         API.Print("You fill the bottle with water from the pond");
         API.SetVar("bottleFull", "true");         
    }

    void Room1_Throw_Bottle_At_Window()
    {
         API.Print("With all your might, you hurl the bottle at the house's window");
         API.RemoveFromInventory("bottle");
         API.UnlockExit("north");
         API.SetVar("windowBroken", "true");    
         //etc...     
    }    
}

これはすべて、私が念頭に置いているもののスケルトンビューのようなものです(私が指摘した微妙な点はたくさんありますが、これは例としては十分です)。悲しいことに、私は自分のプロジェクトのために一言もコーディングしていません。紙のすべて。

だから...これはすべてあなた自身のプロジェクトのためにいじくり回すためのいくつかのアイデアをあなたに与えるかもしれません。不明な点がある場合は、質問してください。私があなたの質問か何かから逸脱していないことを願っています。

このすべてを入力するのに時間がかかりすぎたと思います>_>

編集: PS:私のスケルトンの例は、複数のゲームオブジェクトを含むコマンドを管理する方法を正確に示していません(これは、私が示唆した多くの微妙な点の1つにすぎません)。「ウィンドウにボトルを投げる」のようなものについては、そのような構文を管理する方法を考える必要があります。これに対する私の解決策の好みは、どのコマンドが発行されているかを解析して発見することです...「GOをGOに投げる」。ゲームオブジェクトが何であるかを調べてから、現在の部屋にそれらがあるかどうかを確認します。などなど。

さらに重要なことに、これにより、1つのコマンドに複数のゲームオブジェクトが含まれるため、ゲームオブジェクトインスタンス内にスクリプトを保持できなくなります。おそらく、辞書をRoomインスタンスに保存する方がよいでしょう。(これは私が私のプロジェクトでいる場所のようなものです。)

私のとりとめのないことを許してください...>_>

于 2010-10-02T17:45:43.563 に答える
0

さて、これが私がそれを処理した方法です。それは私が考えることができる最も一般的な方法であり、私が達成しようとしていることに合っていると思います。

「Invoke()」メソッドをInteractionクラスに追加し、「Initiate()」メソッドを定義するIActionResultと呼ばれる新しいインターフェイス、および考えられる結果ごとにさまざまなActionResultタイプを追加しました。また、インタラクションにActionResultsのリストを追加しました。Invokeメソッドは、すべてのIActionResultオブジェクトをループして、Initiate()メソッドを呼び出すだけです。

アイテムにインタラクションを定義するときは、そのインタラクションの動詞のリストを渡し、そのインタラクションの結果に応じていくつかのActionResultオブジェクトを追加します。

また、アクションが実行されるたびに更新されるGlobalActionReferenceを追加しました。これにより、ActionResultは、これを介して更新する必要のあるオブジェクトに適切にアクセスできます。

私はあなたの提案のすべてに本当に感謝します、そして私が私の質問または私のコメント(あるいはこの答えさえ)ではっきりしなかったならば申し訳ありません。ご協力いただきありがとうございます。

于 2010-10-02T22:17:53.383 に答える
0

あなたの問題はイベントの伝播を管理しているようです。Microsoftは、オブザーバーパターン/イベントを使用してこの問題を処理します(あまりカラフルではありません)。

ガンマなどの本「DesignPatterns」のオブザーバーとメディエーターのデザインパターンを組み合わせると非常に役立つと思います。この本には、役立つかもしれないサンプルのChangeManagerクラスがありますが、役立つはずのその他のリンクをいくつか添付しました。

私が持っている実装の提案の1つは、メディエーターとして機能し、アクティブなメモリ内のすべてのアクション可能なオブジェクトへの参照と、呼び出されたすべてのアクションを格納する静的クラスまたはシングルトンクラスを使用することです。このクラスは、アルゴリズムを処理して、すべての応答と、特定のアクションからの応答の時系列順を決定できます。(プライマリアクションAの付随的効果が、アクションが完了する前にそのアクションAの結果に影響を与える可能性があることを考慮すると、適切な時系列が必要であることが明らかになり、それぞれを呼び出す前に更新する必要があります。担保措置。)

オブザーバーパターンに関するMicrosoftの記事:http://msdn.microsoft.com/en-us/library/ee817669.aspx

MediatorパターンのDoFactory(UML図を使用): http: //www.dofactory.com/Patterns/PatternMediator.aspx

オブザーバーパターン上のDoFactory(UML図を使用): http: //www.dofactory.com/Patterns/PatternObserver.aspx

.Net4のIObserverインターフェイスドキュメント http://msdn.microsoft.com/en-us/library/dd783449.aspx

オブザーバーパターンに関する別の記事。 http://www.devx.com/cplus/Article/28013/1954

于 2010-10-03T05:59:21.563 に答える
0

インタラクションは、「動詞+{フィルターのリスト}+{応答のリスト}」として定義できます。

「ボトルに水を入れる」例の場合、インタラクションは次のようになります。

  • 動詞:Fill({"fill"、 "pour"})
  • フィルタのリスト:Have(player, "bottle")Have(currentRoom, "water tap")
  • 回答のリスト:Print("You filled the bottle with water")、、Remove(player, "bottle")Add(player, "bottle of water")
  • または、応答のリストは次のようになります。SetAttribute(player.findInventory("bottle"), "fill", "water")

次に、「窓にボトル入り飲料水を投げる」必要がある場合:

  • 動詞:Throw({"throw"、 "smash"})
  • フィルタのリスト:Have(player, "bottle of water")Have(currentRoom, "windows")
  • 応答のリスト:、、、、、、、Print("The bottle smashed with the windows, and both of them are broken")_Remove(player, "bottle of water")Add(curentRoom, "broken bottle")Remove(currentRoom, "window")Add(currentRoom, "broken window")SetAttribute(currentRoom, "description", "There is water on the floor")

ルームに入ると、フレームワークはルーム内のすべてのオブジェクトに有効な動詞のリストを照会し、それらを列挙します。プレーヤーがコマンドを入力すると、フレームワークはコマンドに一致する動詞を検索します。次に、フィルターのリストをチェックし、すべてがTrueの場合は、応答のリストを反復処理して順番に実行します。

Responsesは、いくつかのコンストラクターを持つIResponseインターフェイスとIResponse.do()メソッドを実装する関数オブジェクトになります。Filtersは、IFilterインターフェイスを実装する関数オブジェクトであり、ここでもいくつかのコンストラクターがあり、IFilter.check()メソッドはブール値を返します。And()、Or()、およびNot()フィルターを使用して、より複雑なクエリを作成することもできます。

いくつかの便利なメソッドを使用することで、物事をさらに読みやすくすることができます。PlayerにはPlayer.have(Actionable)便利なメソッドを使用できるため、player.have( "bottle of water")を記述できます。これは、ボトルオブジェクト自体ではなく、 .check()メソッドが呼び出されたときに、プレーヤーに「ボトル入り飲料水」があるかどうかをチェックするIFilterオブジェクト。基本的に、オブジェクトを怠惰にします。

于 2010-10-03T14:46:48.563 に答える