0

Netwire を使用して移動オブジェクトをモデル化しようとしていますが、ボールが壁から跳ね返るようなものを実装するための推奨される方法を知りたいと考えています。これを行うためのいくつかの可能な方法に遭遇しましたが、実際にそれらを機能させるにはいくつかの助けが必要です。

モーション コードは次のようになります。

type Pos = Float
type Vel = Float

data Collision = Collision | NoCollision
           deriving (Show)

motion :: (HasTime t s, MonadFix m) => Pos -> Vel -> Wire s Collision m a Pos
motion x0 v0 = proc _ -> do
             rec
                 v <- vel <<< delay 0 -< x
                 x <- pos x0 -< v
             returnA -< x

pos :: (HasTime t s, MonadFix m) => Pos -> Wire s Collision m Vel Pos
pos x0 = integral x0

main :: IO ()
main = testWire clockSession_ (motion 0 5)

特定の位置 (x=20 など) で跳ね返る速度矢印を作成するには、どのような方法が推奨されますか?

私はこれを行うことができるかもしれない3つの異なる方法を見てきました:

  • 最も単純に見えるnetwire-->関数。この関数を使用したプロトタイプがありますが、衝突時の速度に基づいて新しい速度矢印を作成する方法がわかりません。オブジェクトが加速できる場合は役に立たない固定値しか使用できません。

    vel :: (HasTime t s, MonadFix m) => Wire s Collision m Pos Vel
    vel = pure 5 . unless (>20) --> pure (-5)
    
  • NetwireでEventとを使用します。switchこれの使い方がわかりません。

  • (|||)一般的に矢印で使用できる機能を使用します。

最初の 2 つは最良のオプションのように思えますが、それらを実装する方法がわかりません。

これと同様の質問を他にも見ましたが、ネットワイヤの異なるバージョン間の非互換性により、回答が役に立たなくなりました。

4

1 に答える 1

3

免責事項:「推奨」とは何かについてコメントすることはできませんが、あなたがやりたいことを行う方法を示すことはできます.

2 つの方法について説明します。1 つ
目はステートフル ワイヤを使用する方法で、2013 年の少し古いチュートリアルですが、Netwire 5.0.2 に基づいています。
2 つ目は、ステートレス ワイヤを使用することです。それらはステートレスであるため、以前の値をフィードバックする必要があります。これにより、ワイヤーの入力タイプとワイヤーの最終的な組み合わせが少し複雑になります。それ以外はかなり似ています。

両方の例に含まれる基本型は次のとおりです。

type Collision = Bool
type Velocity = Float
type Position = Float

ステートフル

2 つの (ステートフルな) ワイヤを組み合わせて問題をモデル化できます。

1 本のワイヤは、一定の速度をモデル化し、衝突が発生すると方向を変えます。これの (簡略化された) タイプは ですWire s e m Collision Velocity。つまり、入力は衝突が発生した場合であり、出力は現在の速度です。

もう 1 つは位置をモデル化し、衝突を処理します。これの (簡略化された) タイプは ですWire s e m Velocity (Position, Collision)。つまり、速度を受け取り、新しい位置を計算し、それを返し、衝突が発生したかどうかを返します。

最後に、速度が位置ワイヤーに送られ、衝突結果が速度ワイヤーに戻されます。

速度ワイヤーの詳細を見てみましょう。

-- stateful fixed velocity wire that switches direction when collision occurs
velocity :: Velocity -> Wire s e m Collision Velocity
velocity vel = mkPureN $ \collision ->
  let nextVel = if collision then negate vel else vel
  in (Right nextVel, velocity nextVel)

mkPureN入力とそれ自体の状態 (モナドや時間ではなく) のみに依存するステートフル ワイヤを作成します。Collision=True状態は現在の速度であり、が入力として渡された場合、次の速度は無効になります。戻り値は、速度値と新しい状態の新しいワイヤのペアです。

integral位置については、ワイヤーを直接使用するだけではもはや十分ではありません。integral値が上限より低く 0 より大きいことを確認し、そのような衝突が発生した場合に情報を返す、強化された「境界付き」バージョンが必要です。

-- bounded integral [0, bound]
pos :: HasTime t s => Position -> Position -> Wire s e m Velocity (Position, Collision)
pos bound x = mkPure $ \ds dx ->
  let dt = realToFrac (dtime ds)
      nextx' = x + dt*dx -- candidate
      (nextx, coll)
        | nextx' <= 0 && dx < 0     = (-nextx', True)
        | nextx' >= bound && dx > 0 = (bound - (nextx' - bound), True)
        | otherwise                 = (nextx', False)
  in (Right (nextx, coll), pos bound nextx)

mkPureに似てmkPureNいますが、ワイヤを時間に依存させることができます。
dt時差です。
nextx'によって返される新しい位置integralです。
次の行は境界をチェックし、衝突が発生した場合は新しい位置を返し、新しい状態の新しいワイヤを返します。

rec最後に、 andを使用してそれらを相互にフィードしますdelay。完全な例:

{-# LANGUAGE Arrows #-}

module Main where

import Control.Monad.Fix
import Control.Wire
import FRP.Netwire

type Collision = Bool
type Velocity = Float
type Position = Float

-- bounded integral [0, bound]
pos :: HasTime t s => Position -> Position -> Wire s e m Velocity (Position, Collision)
pos bound x = mkPure $ \ds dx ->
  let dt = realToFrac (dtime ds)
      nextx' = x + dt*dx -- candidate
      (nextx, coll)
        | nextx' <= 0 && dx < 0     = (-nextx', True)
        | nextx' >= bound && dx > 0 = (bound - (nextx' - bound), True)
        | otherwise                 = (nextx', False)
  in (Right (nextx, coll), pos bound nextx)

-- stateful fixed velocity wire that switches direction when collision occurs
velocity :: Velocity -> Wire s e m Collision Velocity
velocity vel = mkPureN $ \collision ->
  let nextVel = if collision then negate vel else vel
  in (Right nextVel, velocity nextVel)

run :: (HasTime t s, MonadFix m) => Position -> Velocity -> Position -> Wire s () m a Position
run start vel bound = proc _ -> do
  rec
    v <- velocity vel <<< delay False -< collision
    (p, collision) <- pos bound start -< v
  returnA -< p

main :: IO ()
main = testWire clockSession_ (run 0 5 20)

ステートレス

ステートレス バリアントはステートフル バリアントに非常に似ていますが、ワイヤを作成する関数のパラメータではなく、ワイヤの入力タイプに状態が変化する点が異なります。

したがって、速度ワイヤーは(Velocity, Collision)入力としてタプルを取り、関数を持ち上げて作成するだけです。

-- pure stateless independent from time
-- output velocity is input velocity potentially negated depending on collision
velocity :: Monad m => Wire s e m (Velocity, Collision) Velocity
velocity = arr $ \(vel, collision) -> if collision then -vel else vel

関数mkSF_from を使用することもできますControl.Wire.Core(その後、 to の制限を取り除きMonad mます)。

posになる

-- pure stateless but depending on time
-- output position is input position moved by input velocity (depending on timestep)
pos :: HasTime t s => Position -> Wire s e m (Position, Velocity) (Position, Collision)
pos bound = mkPure $ \ds (x,dx) ->
  let dt = realToFrac (dtime ds)
      nextx' = x + dt*dx -- candidate
      (nextx, coll)
        | nextx' <= 0 && dx < 0     = (-nextx', True)
        | nextx' >= bound && dx > 0 = (bound - (nextx' - bound), True)
        | otherwise                 = (nextx', False)
  in (Right (nextx, coll), pos bound)

ここでも mkPure を使用する必要があります。これは、 time に依存するステートレス ワイヤに特に使用できる関数がないためです。

2 本のワイヤを接続するには、速度の出力をワイヤ自体と位置に送り、posワイヤから位置をワイヤ自体に送り、衝突情報を速度ワイヤに送る必要があります。

しかし、実際にはステートレス ワイヤを使用すると、ワイヤの「統合」部分と「境界チェック」部分を分離することもできますpos。その場合、posワイヤーは上にあるWire s e m (Position, Velocity) Positionものを直接返すであり、ワイヤーは速度から新しい位置を取得し、バウンド チェックを適用する です。そうすれば、さまざまな論理部分がうまく分離されます。nextx'boundedPosWire s e m (Position, Velocity) (Position, Collision)pos

完全な例:

{-# LANGUAGE Arrows #-}

module Main where

import Control.Monad.Fix
import Control.Wire
import FRP.Netwire

type Collision = Bool
type Velocity = Float
type Position = Float

-- pure stateless but depending on time
-- output position is input position moved by input velocity (depending on timestep)
pos :: HasTime t s => Wire s e m (Position, Velocity) Position
pos = mkPure $ \ds (x,dx) ->
  let dt = realToFrac (dtime ds)
  in (Right (x + dt*dx), pos)

-- pure stateless independent from time
-- input position is bounced off the bounds
boundedPos :: Monad m => Position -> Wire s e m (Position, Velocity) (Position, Collision)
boundedPos bound = arr $ \(x, dx) ->
  let (nextx, collision)
        | x <= 0 && dx < 0 = (-x, True)
        | x >= bound && dx > 0 = (bound - (x - bound), True)
        | otherwise          = (x, False)
  in (nextx, collision)

-- pure stateless independent from time
-- output velocity is input velocity potentially negated depending on collision
velocity :: Monad m => Wire s e m (Velocity, Collision) Velocity
velocity = arr $ \(vel, collision) -> if collision then -vel else vel

-- plug the wires into each other
run :: (HasTime t s, MonadFix m) => Position -> Velocity -> Position -> Wire s () m a Position
run start vel bound = proc _ -> do
  rec
    v <- velocity <<< delay (vel, False) -< (v, collision)
    lastPos <- delay start -< p'
    p <- pos -< (lastPos, v)
    (p', collision) <- boundedPos bound -< (p, v)
  returnA -< p'

main :: IO ()
main = testWire clockSession_ (run 0 5 20)
于 2016-11-25T20:49:07.827 に答える