首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Haskell Gloss粒子效果

Haskell Gloss粒子效果
EN

Stack Overflow用户
提问于 2014-11-02 22:21:21
回答 1查看 483关注 0票数 1

如何使用Gloss库在Haskell中创建粒子效果?(例如,显示爆炸)

如果有人能帮我解决这个问题,我将不胜感激。

最好的问候,Skyfe。

EN

回答 1

Stack Overflow用户

发布于 2017-10-15 21:57:57

关于这个问题的评论在提供高级解决方案方面做得很好,但我写这个答案是为了增加细节。

让我们首先对我们想要表示的真实世界对象进行建模。在我们的例子中,它是一个粒子。一个粒子应该有一个位置,一个速度和一个加速度,所有这些我们都可以用二维矢量来表示。在Haskell中存储2D向量的一种合理方法是使用Linear.V2模块。接下来,让我们考虑一下粒子应该具有的附加属性,特别是与烟花或爆炸相关的属性。注意到烟花中的粒子是如何燃烧一段时间然后“熄灭”的吗?让我们将said time称为粒子的寿命,并使用Float表示它。现在,我们可以为粒子和 of 粒子创建适当的表示

代码语言:javascript
复制
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,其余的则留给其他参数处理

代码语言:javascript
复制
makeParticle :: Float -> V2 Float -> V2 Float -> V2 Float -> Particle
makeParticle = Particle 0

完成后,我们可以编写一个函数来帮助我们创建n粒子的集群

代码语言:javascript
复制
makeCluster :: Int -> (Int -> Particle) -> Cluster
makeCluster n particleGen = map particleGen [0..(n - 1)]

在那之后,我们创建一个函数,允许我们将粒子提前dt秒。该函数推进粒子的年龄,基于其速度改变其位置,并最终基于其加速度改变其速度。最后,如果粒子的年龄大于其寿命,我们通过计算Nothing而不是Just来表示删除粒子

代码语言:javascript
复制
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

下面的函数推进了一个粒子集群,并清除了“死”的

代码语言:javascript
复制
advanceCluster :: Float -> Cluster -> Cluster
advanceCluster dt = catMaybes . map (advanceParticle dt)

现在,我们可以转到使用Graphics.Gloss.实际绘制粒子的代码部分我们将使用集群来表示模拟的状态,因此我们从一个函数开始,该函数返回一个表示程序初始状态的集群。对于一个简单的动画,我们将模拟一个烟花,其中所有的粒子都从相同的位置开始,具有相同的寿命,以规则的角度从其中心位置向外辐射,并受到相同的加速度

代码语言:javascript
复制
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)

然后,我们编写一个函数将群集绘制到屏幕上

代码语言:javascript
复制
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),它会一直淡出。

我们就快完成了!添加一个更新群集以反映时间流逝的函数

代码语言:javascript
复制
stepState :: ViewPort -> Float -> Cluster -> Cluster
stepState _ = advanceCluster

通过编写一个将所有内容联系在一起的main函数来完成程序

代码语言:javascript
复制
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

我希望这能帮到你!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26700346

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档