Haskell Gloss:随机星空不随机?
Haskell Gloss: random starfield not random?
使用 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
只是将速度加到星星的当前位置。
我认为这个问题与我的随机生成器没有正确传递有关,但我很困惑,无法想出解决方案……非常感谢任何帮助!
这与光泽无关,只是纯随机数生成的语义。每次用新种子重新初始化新生成器 不会 给你伪随机数,因为随机性仅来自 新生成器 由例如创建randomR
或 split
的种子与原始种子有很大不同。您只需 map mkStdGen [0..]
,因为您只是在每一步将 1
添加到 timer
。
考虑以下分布的差异:
map (fst . randomR (1,1000) . mkStdGen) [0..1000]
take 1000 $ randomRs (1,1000) (mkStdGen 123)
第一个是你在做什么,第二个是适当的随机数。
解决方法很简单,只需在你的时间更新函数中使用 split
(你已经在 spawnParticle
中使用了它):
timeHandler dt world =
let (newRndGen, g) = split (rndGen world) in
world { rndGen = newRndGen
, timer = (timer world) + 1 ,
, stars = spawnParticle world g : updateParticles (stars world) dt world
}
请注意,现在 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 的 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
只是将速度加到星星的当前位置。
我认为这个问题与我的随机生成器没有正确传递有关,但我很困惑,无法想出解决方案……非常感谢任何帮助!
这与光泽无关,只是纯随机数生成的语义。每次用新种子重新初始化新生成器 不会 给你伪随机数,因为随机性仅来自 新生成器 由例如创建randomR
或 split
的种子与原始种子有很大不同。您只需 map mkStdGen [0..]
,因为您只是在每一步将 1
添加到 timer
。
考虑以下分布的差异:
map (fst . randomR (1,1000) . mkStdGen) [0..1000]
take 1000 $ randomRs (1,1000) (mkStdGen 123)
第一个是你在做什么,第二个是适当的随机数。
解决方法很简单,只需在你的时间更新函数中使用 split
(你已经在 spawnParticle
中使用了它):
timeHandler dt world =
let (newRndGen, g) = split (rndGen world) in
world { rndGen = newRndGen
, timer = (timer world) + 1 ,
, stars = spawnParticle world g : updateParticles (stars world) dt world
}
请注意,现在 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 }