如何使用Gloss库在Haskell中创建粒子效果?(例如,显示爆炸)
如果有人能帮我解决这个问题,我将不胜感激。
最好的问候,Skyfe。
发布于 2017-10-15 21:57:57
关于这个问题的评论在提供高级解决方案方面做得很好,但我写这个答案是为了增加细节。
让我们首先对我们想要表示的真实世界对象进行建模。在我们的例子中,它是一个粒子。一个粒子应该有一个位置,一个速度和一个加速度,所有这些我们都可以用二维矢量来表示。在Haskell中存储2D向量的一种合理方法是使用Linear.V2模块。接下来,让我们考虑一下粒子应该具有的附加属性,特别是与烟花或爆炸相关的属性。注意到烟花中的粒子是如何燃烧一段时间然后“熄灭”的吗?让我们将said time称为粒子的寿命,并使用Float表示它。现在,我们可以为粒子和 of 粒子创建适当的表示
data Particle = Particle
{ _age :: Float
, _lifespan :: Float
, _position :: V2 Float
, _velocity :: V2 Float
, _acceleration :: V2 Float }
deriving ( Show )
type Cluster = [Particle]
makeLenses ''Particle在上面的数据类型中有一个额外的字段叫做age。粒子的寿命表示粒子从创建到死亡的时间,而它的年龄表示自粒子创建以来经过的时间。换句话说,当粒子的年龄超过其寿命时,它应该消失。请记住这一点。
接下来,让我们编写一个函数来帮助我们创建一个粒子。它所做的就是将初始年龄设置为0,其余的则留给其他参数处理
makeParticle :: Float -> V2 Float -> V2 Float -> V2 Float -> Particle
makeParticle = Particle 0完成后,我们可以编写一个函数来帮助我们创建n粒子的集群
makeCluster :: Int -> (Int -> Particle) -> Cluster
makeCluster n particleGen = map particleGen [0..(n - 1)]在那之后,我们创建一个函数,允许我们将粒子提前dt秒。该函数推进粒子的年龄,基于其速度改变其位置,并最终基于其加速度改变其速度。最后,如果粒子的年龄大于其寿命,我们通过计算Nothing而不是Just来表示删除粒子。
advanceParticle :: Float -> Particle -> Maybe Particle
advanceParticle dt = hasDecayed . updateVel . updatePos . updateAge
where
r2f = realToFrac
hasDecayed p = if p^.age < p^.lifespan then Just p else Nothing
updateAge p = (age %~ (dt +)) p
updatePos p = (position %~ (r2f dt * p^.velocity +)) p
updateVel p = (velocity %~ (r2f dt * p^.acceleration +)) p下面的函数推进了一个粒子集群,并清除了“死”的
advanceCluster :: Float -> Cluster -> Cluster
advanceCluster dt = catMaybes . map (advanceParticle dt)现在,我们可以转到使用Graphics.Gloss.实际绘制粒子的代码部分我们将使用集群来表示模拟的状态,因此我们从一个函数开始,该函数返回一个表示程序初始状态的集群。对于一个简单的动画,我们将模拟一个烟花,其中所有的粒子都从相同的位置开始,具有相同的寿命,以规则的角度从其中心位置向外辐射,并受到相同的加速度
initState :: Cluster
initState = makeCluster numParticles particleGen
where
numParticles = 10
particleGen :: Int -> Particle
particleGen i =
makeParticle initLifespan
initPosition
(initVelMagnitude * V2 (cos angle) (sin angle))
initAcceleration
where
fI = fromIntegral
angle = (fI i) * 2 * pi / (fI numParticles)
initLifespan = 10
initPosition = V2 0 0
initVelMagnitude = 5
initAcceleration = V2 0 (-3)然后,我们编写一个函数将群集绘制到屏幕上
drawState :: Cluster -> Picture
drawState = pictures . map drawParticle
where
drawParticle :: Particle -> Picture
drawParticle p =
translate (p^.position._x) (p^.position._y) .
color (colorAdjust (p^.age / p^.lifespan)) .
circleSolid $ circleRadius
where
circleRadius = 3
colorAdjust a = makeColor 1 0 0 (1 - a)与此相关的唯一非标准部分可能是colorAdjust函数。我在这里要做的是将粒子涂成红色,当它被创建时,它根本不是透明的(即alpha值为1),并且随着它的寿命接近它的寿命(即alpha值一直接近0),它会一直淡出。
我们就快完成了!添加一个更新群集以反映时间流逝的函数
stepState :: ViewPort -> Float -> Cluster -> Cluster
stepState _ = advanceCluster通过编写一个将所有内容联系在一起的main函数来完成程序
main :: IO ()
main =
simulate (InWindow name (windowWidth, windowHeight)
(windowLocX, windowLocY))
bgColor
stepsPerSec
initState
drawState
stepState
where
name = "Fireworks!"
windowWidth = 300
windowHeight = 300
windowLocX = 30
windowLocY = 30
stepsPerSec = 30
bgColor = white我希望这能帮到你!
https://stackoverflow.com/questions/26700346
复制相似问题