Netwire - как построить провод, который создает позиции, подпрыгивая стены?

Использование netwire-4.0.7

Как говорится в заголовке вопроса, я пытаюсь создать провод, который создает позиции (перемещая позицию с определенной скоростью на каждом шаге), "отскакивая" от других объектов. Самым простым примером, который я мог придумать, было перемещение внутри рамки, как в некоторых заставках.

Я написал эту функцию как попытку сделать это (только для одной оси):

import Control.Wire
bouncyWire :: Double -> Double -> Double -> Double -> Wire () IO a Double
bouncyWire startPosition startVelocity lowerBound upperBound = proc _ -> do
  rec
     v <- delay startVelocity -< if p < lowerBound || p > upperBound
                                    then -v else v
     p <- integral_ startPosition -< v
  returnA -< p

(Я фактически использую это с другой монадой, но этот код фактически не использует это, и это усложнило бы проблему здесь).

Шагая с counterSession $ 1/60 однако у меня возникает проблема - вместо того, чтобы "отскакивать" от стены, он застревает там. Я думаю, что происходит то, что он продолжает менять скорость v снова и снова, но я не уверен почему. Я думаю, что я могу использовать задержку неправильно или что-то.

2 ответа

Решение

Как объяснил Ричард Хакстон, ваш провод застревает за границей. Одним из решений является проверка того, что вы не просто изменяете скорость, вы всегда указываете их в правильном направлении. Таким образом, провод всегда попадает из-за границы. Другое решение состоит в том, чтобы изменить положение обратно внутри границ, если оно выходит наружу. Таким образом, проволока никогда не воспринимается как отставание (это то, что вы обычно хотите делать в играх). В сочетании друг с другом это может выглядеть

import Prelude hiding ((.), id)
import Control.Category
import Control.Wire
import Control.Wire.Wire
import Control.Wire.Prefab.Move

bouncyWire :: (Monad m)
           => Double -> Double -> Double -> Double -> Wire () m a Double
bouncyWire startPosition startVelocity lowerBound upperBound =
    objPosition <$> object_ update (ObjectState startPosition startVelocity)
        . constant (Accelerate 0, ())
  where
    update _ s@(ObjectState pos vel)
        | (pos > upperBound)    = ObjectState (2*upperBound - pos) (- abs vel)
        | (pos < lowerBound)    = ObjectState (2*lowerBound - pos) (abs vel)
        | otherwise             = s

Я не очень знаком с обозначением стрелки, поэтому я использовал Category а также Applicative обозначение (состав проводов с использованием .). Для этой задачи object_ Особенно удобно. Он интегрирует скорость и циклы внутри, и все, что нам нужно сделать, это дать ему функцию модификации и извлечь позицию из выходных данных.

Ах, шанс изменить свои знания из моих дней, возиться с написанием 8-битных игр в сочетании BASIC и ассемблера!

Вероятно, происходит то, что "шар" застревает не с той стороны стены из-за ошибок округления. Итак, если у вашей стены 10,0, шарик колеблется между 11,0001 и 10,0001 с v=-1,0,+1,0,-1,0 и т. Д. Не уверен, как вы напечатаете позицию для проверки с помощью проводов.

Исходя из (древней) памяти, вы либо хотите рассчитать отскок, включая любой "оставшийся" вектор предыдущего шага, либо просто поместите мяч точно на стену, прежде чем отскочить от нее.

Другие вопросы по тегам