Haskell Gloss:随机星场不是随机星场?

Haskell Gloss:随机星场不是随机星场?,haskell,random,particles,gloss,Haskell,Random,Particles,Gloss,使用Haskell的Gloss库,我试图模拟一个星场。视觉方面(以各种速度和大小在屏幕上绘制“星星”)正在发挥作用。然而,由于某些原因,恒星并不是随机分布的,这导致了一个具有模式的模拟。对于爆炸模拟,我也有这个问题,但为了简单起见,我暂时不考虑这个问题。这是迄今为止我的代码的简化版本: type Position = (Float, Float) type Velocity = (Float, Float) type Size = Float type

使用Haskell的Gloss库,我试图模拟一个星场。视觉方面(以各种速度和大小在屏幕上绘制“星星”)正在发挥作用。然而,由于某些原因,恒星并不是随机分布的,这导致了一个具有模式的模拟。对于爆炸模拟,我也有这个问题,但为了简单起见,我暂时不考虑这个问题。这是迄今为止我的代码的简化版本:

type Position       = (Float, Float)
type Velocity       = (Float, Float)
type Size           = Float
type Speed          = Float
type Drag           = Float
type Life           = Int

type Particle       = (Position, Velocity, Speed, Drag, Life, Size)

-- timeHandler is called every frame by the gloss ‘Play’ function. It's being passed
-- the delta time and the world it needs to update.
timeHandler dt world = world {rndGen = mkStdGen (timer world),
                              timer  = (timer world) + 1,
                              stars  = spawnParticle world (rndGen world) : updateParticles (stars world) dt world}

randomFloat :: StdGen -> Float -> Float -> Float
randomFloat rand min max = fst $ randomR (min, max) rand

spawnParticle :: World -> StdGen -> Particle
spawnParticle world gen = ((pos, (1 * speed, 0), speed, 1, 0, size), snd (split gen))
where pos   = (px', py')
      px'   = randomFloat gen (-600) (-500)
      py'   = randomFloat gen (-250) 250
      speed = size * (randomFloat gen 100 300) -- the smaller a particle, the slower 
      size  = randomFloat gen 0.1 1.3

updateParticles :: [Particle] -> Float -> World -> [Particle]
updateParticles []     _  _     = []
updateParticles (x:xs) dt world | fst(posPart x) > 500 = updateParticles xs dt world
                                | otherwise            = updatedPart : updateParticles xs dt world
    where pos'        = updateParticlePosition dt x world
          updatedPart = (pos', velPart x, speedPart x, 1, 0, sizePart x)
注意:
velPart
speedPart
等是从给定粒子中获取属性的函数。再说一遍,画图效果很好,所以我将省略该代码
updateParticlePosition
只需将速度添加到恒星的当前位置


我认为这个问题与我的随机生成器没有正确传递有关,但我太困惑了,无法想出解决方案…非常感谢任何帮助

这与gloss无关,只与纯随机数生成的语义有关。每次使用新种子重新初始化新生成器时,都不会产生伪随机数,因为随机性仅来自这样一个事实,即由例如
randomR
split
创建的新生成器将具有与原始生成器非常不同的种子。您只需将
映射到mkStdGen[0..]
,因为您只需在每一步向
计时器添加
1

考虑以下分布中的差异:

map (fst . randomR (1,1000) . mkStdGen) [0..1000]
take 1000 $ randomRs (1,1000) (mkStdGen 123)
第一个是你在做什么,第二个是正确的随机数

解决方案很简单,只需在时间更新函数中使用
split
(在
spawnParticle
中已经有了它):

请注意,现在没有使用
timer
,不管怎样,使用
timer=timer world+dt
可能更有意义(时间增量可能并不完全相等)

还请记住,您不应该重用生成器,因此,如果您有许多本地函数将生成器作为参数,则可能需要以下内容:

timeHandler dt world = 
  let (newRndGen:g0:g1:g2:_) = unfoldr (Just . split) (rndGen world) in 
   world { rndGen = newRndGen 
         , timer  = (timer world) + 1 , 
         , stars  = spawnParticle world g0 : updateParticles (stars world) dt world
         , stuff0 = randomStuff0 g1 , stuff1 = randomStuff1 g2 }

记住,所有Haskell函数都是纯函数。考虑到这一点,
randomFloat::StdGen->Float->Float->Float
如何在调用之间给出不同的结果?我认为如果每次调用都向它传递一个唯一的生成器,它应该给出不同的结果。至少,这就是我试图做的。您可以通过丢弃从
randomR
返回的值的后半部分来丢弃新的生成器状态。谢谢您的回复!这是有道理的,然而,星场仍然不是随机的。值得注意的是,从屏幕顶部到底部,颗粒大小/速度都有所降低。。。这是因为我在我的
spawnParticle
函数中重用
gen
吗?我应该在询问之前先尝试一下:我在
spawnParticle
中应用了与您在
timeHandler
中使用的相同的技术,它现在正在工作!
timeHandler dt world = 
  let (newRndGen:g0:g1:g2:_) = unfoldr (Just . split) (rndGen world) in 
   world { rndGen = newRndGen 
         , timer  = (timer world) + 1 , 
         , stars  = spawnParticle world g0 : updateParticles (stars world) dt world
         , stuff0 = randomStuff0 g1 , stuff1 = randomStuff1 g2 }