7

というわけで、この小さなサッカー ゲームをしばらく書いていますが、最初から 1 つ気になることがあります。このゲームはYampa Arcadeのパターンに従っているため、ゲーム内の「オブジェクト」には合計タイプがあります。

data ObjState = Ball Id Pos Velo
              | Player Id Team Number Pos Velo
              | Game Id Score

オブジェクトはメッセージに反応するため、別の合計型があります。

data Msg = BallMsg BM
         | PlayerMsg PM
         | GameMsg GM
data BM = Gained | Lost
data PM = GoTo Position | Shoot
data GM = GoalScored | BallOutOfBounds

Yampa フレームワークは、いわゆるシグナル関数に依存しています。私たちの場合、ボール、プレーヤー、およびゲームの動作のシグナル関数があります。大雑把に単純化:

ballObj, playerObj, gameObj :: (Time -> (GameInput, [Msg])) 
                               -> (Time -> (ObjState, [(Id, Msg)]))

たとえば、ballObj は GameInput (キー ストローク、ゲームの状態など) と特定の時点でのボール専用のメッセージのリストを生成する関数を受け取り、ボールの状態と他のオブジェクトへのメッセージを生成する関数を返します。 (ボール、ゲーム、プレーヤー)いつでも。Yampa では、型シグネチャは実際にはもう少し見栄えがします。

ballObj, playerObj, gameObj :: SF (GameInput, [Msg]) (ObjState, [(Id, Msg)])

Yampa フレームワークにとって、この一様な型シグネチャは重要です: (再び、非常に大雑把に単純化されています) 11 + 11 (プレーヤー) + 1 (ボール) + 1 (ゲーム) の同じ型の信号関数のリストから大きな信号関数を構築します。 (dpSwitch を介して) その後、(reactimate を介して) 実行されます。

さて、私を悩ませているのは、BallMsg を Ball に送信するか、PlayerMsg を Player に送信することだけです。たとえば誰かが GameMsg を Ball に送信すると、プログラムはクラッシュします。これを回避するために型チェッカーを配置する方法はありませんか? 私は最近、タイプ ファミリーに関するこの素敵なポケモンの投稿を読みましたが、いくつかの類推があるようです。だから多分これは出発点かもしれません:

class Receiver a where
  Msg a :: *
  putAddress :: Msg a -> a -> Msg a

data BallObj = ...
data GameObj = ...
data PlayerObj = ...

instance Receiver BallObj where
  Msg BallObj = Gained | Lost
(...)

さて、SF 関数は次のようになります。

forall b . (Receiver a, Receiver b) => SF (GameInput, [Msg a]) (a, [(b, Msg b)])

これで私はどこにでも行けますか?

4

2 に答える 2

2

Skimming the yampa arcade paper it seems like you have a route function drawn from their example.

My suggestion would be you alter route so it doesn't take a single list of objects, but instead a single game object, a single ball object, and a collection of player objects. Then have

data BallMsg = ...
data PlayerMsg = ...
data GameMsg = ...

data AnyMsg = ABallMsg BallMsg
            | APlayerMsg PlayerMsg
            | AGameMsg GameMsg

Now route works on a uniform AnyMsg but it dispatches them to the right destination depending on their contents.

于 2013-05-24T02:12:49.440 に答える
1

一見しただけで、あなたのデザインには大きな問題が 1 つあります。それは、完全に異なるエンティティをBall1つのタイプにまとめていることです。これらのエンティティにユニオン型が必要な場合は、メッセージと同じように別の型にすることにより、次のようにします。PlayerGame

data AnyObject = AnyObjectBall Ball
               | AnyObjectPlayer Player
               | AnyObjectGame Game

Ball -> BallMsg -> ...このようにして、特定の機能 ( ) と一般的な機能 ( )の両方を表現できますAnyObject -> AnyMsg -> ...

しかし、私があなたの問題を正しく理解していれば、ユニオン型を必要としない解決策があると思います:

class Signal object message where
  signal :: SF (GameInput, [message]) (object, [(Id, message)])

data Ball = Ball Id Pos Velo
data BallMsg = BallMsgGained | BallMsgLost
instance Signal Ball BallMsg where
  -- ...

-- so on for Player and Game
于 2013-05-25T08:33:22.287 に答える