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 и т. Д. Не уверен, как вы напечатаете позицию для проверки с помощью проводов.
Исходя из (древней) памяти, вы либо хотите рассчитать отскок, включая любой "оставшийся" вектор предыдущего шага, либо просто поместите мяч точно на стену, прежде чем отскочить от нее.