微程序控制器实验实验报告
Rust的99 LoC中快速而又肮脏的过程生成
几天前,我尝试创建一个过程景观生成器。 这是实际结果: https : //rap2hpoutre.github.io/landscape-site/
我想知道事情是如何运作的。 这就是我建造那个的方式。
山脉
我不是一个优秀的程序员,也不知道如何创建山脉。 我在Stack Exchange上找到了这个答案: https : //gamedev.stackexchange.com/a/93531/81351
因此,我将此代码转换为Rust,因为出于某些原因我喜欢Rust。 首先,我创建了一个Mountain
结构。
struct Mountain {
points: Vec < u32 >,
}
这些points
是每个峰的“ y”位置:在640x480图像上,有640个点从0(最高)到480(最低)不等。
然后,我创建了两个关联的函数:一种用于初始化点的new
方法和一种用于绘制山脉的每个像素的draw
方法。
impl Mountain {
fn new (y_amp: ( f64 , f64 )) -> Mountain {
let mut rng = rand::thread_rng();
let step_max = rng.gen_range( 0.9 , 1.1 );
let step_change = rng.gen_range( 0.15 , 0.35 );
let (height_min, height_max) = y_amp;
let mut height = rng.gen_range( 0.0 , height_max);
let mut slope = rng.gen_range( 0.0 , step_max) * 2.0 - step_max;
let mut points: Vec < u32 > = Vec ::new();
for _ in 0 .. 640 {
height = height + slope;
slope = slope + (rng.gen_range( 0.0 , step_change) * 2.0 - step_change);
if slope > step_max {
slope = step_max;
} else if slope < -step_max {
slope = -step_max;
}
if height > height_max {
height = height_max;
slope = slope * - 1.0 ;
} else if height < height_min {
height = height_min;
slope = slope * - 1.0 ;
}
points.push(height as u32 );
}
Mountain {
points: points
}
}
fn draw (& self , img: & mut RgbImage, color: Rgb< u8 >, c_fog: Rgb< u8 >) {
let mut i = 0 ;
for &point in self .points.iter() {
img.put_pixel(i, point, color);
for j in point.. 480 {
img.put_pixel(i, j, interpolate(c_fog, color, j as f32 / 480.0 ));
}
i = i + 1 ;
}
}
}
这是最长的部分(虽然很脏,但不是最丑的!),认为这只是我提到的StackExchange帖子中的改编副本/粘贴。
new
方法采用参数y_amp
的元组,该参数y_amp
是当前山脉的边界(最大高度和最小高度)。 用随机的相邻值初始化这些点。
draw
方法会参考要绘制的图像和两种颜色:山脉的初始颜色( color
)和雾的颜色( c_fog
),这是创建渐变所必需的。 对于每个点,从图像的顶点到顶点绘制一条渐变“线”,将雾色与初始色混合。
我可以使用常量(用于640、480等),为变量选择更好的名称并添加注释,但是,嘿! 没时间! 快点,脏点。
随机颜色帮手
该程序的主要任务是生成随机颜色。 我创建了一个小助手来生成颜色:
fn rgb_rand (rng: & mut ThreadRng, r: ( u8 , u8 ), g: ( u8 , u8 ), b: ( u8 , u8 )) -> Rgb< u8 > {
Rgb([rng.gen_range(r. 0 , r. 1 ), rng.gen_range(g. 0 , g. 1 ), rng.gen_range(b. 0 , b. 1 )])
}
我忘了提及所有功能和结构,如 Rgb
, RgbImage
, interpolate
等,都来自于出色的,部分记录的或多或少稳定的“ image ”和“ imageproc ”板条箱(板条箱是Rust lib)。
我编写的rgb_rand
函数可以从红色范围,绿色范围和蓝色范围生成随机颜色。 从现在开始,我将在任何地方使用它。
渐变的天空和随机的月亮
借助rgb_rand
函数,我可以初始化随机的天空颜色,随机的雾颜色和随机的月亮颜色。
天空。 它可以是浅色,深色或浅蓝色,让我们抛出1D3:
let mut rng = rand::thread_rng();
let c_sky = match rng.gen_range( 1 , 4 ) {
1 => rgb_rand(& mut rng, ( 1 , 40 ), ( 1 , 40 ), ( 1 , 40 )),
2 => rgb_rand(& mut rng, ( 215 , 225 ), ( 215 , 225 ), ( 230 , 255 )),
_ => rgb_rand(& mut rng, ( 200 , 255 ), ( 200 , 255 ), ( 200 , 255 )),
};
雾的颜色是完全随机的,而行星(月亮?太阳?谁在乎?)颜色是天空和随机颜色的混合:
let c_fog = rgb_rand(& mut rng, ( 1 , 255 ), ( 1 , 255 ), ( 1 , 255 ));
let c_planet = interpolate(rgb_rand(& mut rng, ( 1 , 255 ), ( 1 , 255 ), ( 1 , 255 )), c_sky, 0.1 );
现在让我们绘制所有内容。 所以我从天空开始(我们的基本图像缓冲区):
let mut img = ImageBuffer::from_pixel( 640 , 480 , c_sky);
然后,该程序会在用gen_weighted_bool
投掷硬币后尽可能不频繁地绘制行星。 行星只是一个实心圆。 有时,会在天空之后立即绘制另一个实心圆,以创建新月形效果。
if rng.gen_weighted_bool( 2 ) {
let x = rng.gen_range( 101 , 520 );
let y = rng.gen_range( 81 , 200 );
let rad = rng.gen_range( 20 , 80 );
draw_filled_circle_mut(& mut img, (x, y), rad, c_planet);
if !rng.gen_weighted_bool( 5 ) {
draw_filled_circle_mut(& mut img, (x + rng.gen_range(- 2 , 4 ) * 10 , y), rad, c_sky);
}
}
然后将渐变应用于我们绘制的所有内容。
for (_, y, pixel) in img.enumerate_pixels_mut() {
*pixel = interpolate(c_fog, *pixel, y as f32 / 1000.0 );
}
有了所有这些代码后,我们现在有了一个带有月亮的渐变天空。
无声的山脉,在渐变的天空
现在是时候在此渐变天空上添加一些山脉了。
let mountain_count: u32 = rng.gen_range( 4 , 7 );
let c_mountain = rgb_rand(& mut rng, ( 1 , 255 ), ( 1 , 255 ), ( 1 , 255 ));
for i in 0 ..mountain_count {
let c = interpolate(c_mountain, c_sky, (i + 1 ) as f32 / mountain_count as f32 );
let y_amp = ( ( 399 - 480 / 2 / mountain_count * (mountain_count - i)) as f64 , 401.0 );
Mountain::new(y_amp).draw(& mut img, c, c_fog);
}
简而言之,我只是定义了一个随机数的山脉mountain_count
,然后定义了基色c_mountain
。 然后,我们对每个山脉进行迭代,构建和绘制第一步中创建的Mountain
。
高山越深,天空的色彩越柔和。 请记住,雾色还与山脉混合在一起,使它们显得不太平坦。
也许我忘记了一些,整个代码可以在这里找到: https : //github.com/rap2hpoutre/landscape/blob/master/src/main.rs
最后的想法
我喜欢程序生成,即使我大部分时间都不了解,我也喜欢阅读有关如何创建程序性内容的信息。 这篇文章是我编写代码的描述,显然不是Rust的使用方法或教程。 结果不是超级性感,但我喜欢创建它。 我还建立了一个小型网站,每分钟都会产生一个新鲜的风景。 在框架中,因为我希望它看起来像艺术品! 它在这里: https : //rap2hpoutre.github.io/landscape-site/
我的主要灵感来自纳瓦拉山脉。 这里列出的项目远比我的要好,我也掠夺了他们的想法:
- https://foopod.github.io/sunset/
- http://v21.io/softlandscapes/
- https://www.reddit.com/r/proceduralgeneration/comments/606y33/procedurally_generation_landscapes/
- http://imgur.com/a/3Fmuf
- https://gamedev.stackexchange.com/questions/93511/how-can-i-generate-a-2d-mountain-landscape-procedurally
感谢@dorhan_的评论!
翻译自: https://hackernoon.com/a-procedural-landscape-experiment-4efe1826906f
微程序控制器实验实验报告