Processing绘制星空-2-绘制流星
工具:processing3.5
静态星星的绘制原理参考上篇
- 最终效果
- 参考示例
- 绘制流星
- 总结与体会
- 笔者的话
前言
就这个项目而言不要再求源代码到邮箱了,认真看看博文吧,我有将项目完整代码都贴在文章末尾,可是还是有很多人直接要项目源码,问的人多了有些不开心,我不想断言你这样直接要代码的用途,但是希望看官尊重文章内容,不做伸手党,谢谢~
最终效果
请调到电脑最大亮度观看
局部:
最后的效果这里放上了动图,但是实际上的效果与这里有所不同,比如星星的颜色并不是白色,总之实际效果比动图美很多,如果你也能在电脑上跑一下源程序就能体会到。
截图如下:
参考示例汇总
参考示例来自《代码本色》的第1章,也就是向量的应用。(不感兴趣这些示例可以跳过这一块)
示例一
本示例主要展示了给物体添加速度以及简单运动模糊的实现。本项目参考其实现流星的运动和流星的运动模糊。
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com
// Example 1-2: Bouncing Ball, with PVector!
PVector position;
PVector velocity;
void setup() {
size(200,200);
background(255);
position = new PVector(100,100);
velocity = new PVector(2.5,5);
}
void draw() {
noStroke();
fill(255,20);
rect(0,0,width,height);
// Add the current speed to the position.
position.add(velocity);
if ((position.x > width) || (position.x < 0)) {
velocity.x = velocity.x * -1;
}
if ((position.y > height) || (position.y < 0)) {
velocity.y = velocity.y * -1;
}
// Display circle at x position
stroke(0);
fill(175);
ellipse(position.x,position.y,16,16);
}
运行效果:
示例二
本示例主要参考向量的更新,也就是从一边屏幕消失,从另一边屏幕中出现。在本程序中不断对物体的位置进行更新、判断,当物体的位置超出了画布范围,就将物体的挪动到画布的另一边,具体就是初始化坐标为0,或者为画布长宽。
绘制流星时也用到这个思想。
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com
Mover mover;
void setup() {
size(640,360);
mover = new Mover();
}
void draw() {
background(255);
mover.update();
mover.checkEdges();
mover.display();
}
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com
class Mover {
PVector position;
PVector velocity;
Mover() {
position = new PVector(random(width), random(height));
velocity = new PVector(random(-2, 2), random(-2, 2));
}
void update() {
position.add(velocity);
}
void display() {
stroke(0);
strokeWeight(2);
fill(127);
ellipse(position.x, position.y, 48, 48);
}
void checkEdges() {
if (position.x > width) {
position.x = 0;
}
else if (position.x < 0) {
position.x = width;
}
if (position.y > height) {
position.y = 0;
}
else if (position.y < 0) {
position.y = height;
}
}
}
运行效果:
注:黄色圆圈为录屏软件所自带,并不是程序运行的效果。
绘制流星
1.绘制普通星星
绘制静态的星星是一件很简单的事情,用点来代表星星,只需要随机地在画布上画上规定数目的点即可。
为了使代码可复用性高,最好将星星封装成一个类。
class Stars
{
int starsNum;//静态星星的数量
int maxX;//星星的分布范围
int maxY;
Stars(int num,int maxX,int maxY)
{
meteorNum=num;
starsNum=5000;
//初始化星空,随机生成星星
int x,y;
for (int i=0;i<starsNum;i++)
{
x=int(random(maxX));//产生0-maxX范围内的整数
y=int(random(maxY));
point(x,y);//在(x,y)处画点
}
}
}
2.绘制流星
流星=星星+速度+“尾巴”
分析:
流星的速度以及方向:
每次在draw函数绘制星星时将星星的坐标加上一个固定的值使得(x,y)变成(x+tx,y+ty),便可以使得星星运动起来。
代码如下,position数组存放流星的位置和方向(x,y),velocity数组就是控制方向和速度的向量(tx,ty)
for (int i=0;i<meteorNum;i++)
{
position[i].add(velocity[i]);//向量相加
}
流星的拖尾:
星星走过的路线本来是全亮的,在画面上展示出来就是一条白色的直线,但是我们可以通过使得星星先走过的路线先变黑来形成拖尾的效果。在每一帧刷新的时候画上一个大小为画布尺寸的矩形,用有透明度的黑色填充矩形。效果可由下面一张图片来表示:
是不是看起来像一条向右边运动的流星?
代码如下,写在draw函数里面。
fill(0,18);//颜色为黑色,透明度为18
rect(0,0,width,height);
边界判断:
流星超出画布范围的时候应该把流星的位置初始化。
代码如下,position数组存放的是流星的位置。
void checkEdges()
{
for (int i=0;i<meteorNum;i++)
{
if (position[i].x > width)
{
position[i].x = 0;
} else if (position[i].x < 0)
{
position[i].x = width;
}
if (position[i].y > height)
{
position[i].y = 0;
} else if (position[i].y < 0)
{
position[i].y = height;
}
}
}
- 最后加上一些简单的细节,比如星星们的大小不同,但是以小的星星为主,流星的大小也是不一样的,这些基本上都是随机数的应用。下面放上整个项目的完整代码:
两个文件
1.drawStars.pde
Stars myStars;
void setup()
{
size(1920,1080);
//流星数量30颗,普通星星5000颗,画布大小1920*1080
myStars=new Stars(30,5000,1920,1080);
}
void draw()
{
fill(0,18);//颜色为黑色,透明度为18
rect(0,0,width,height);
myStars.update();
myStars.checkEdges();
myStars.display();
}
2.Stars.pde
class Stars
{
int starsNum;//静态星星的数量
int maxX;//星星的分布范围
int maxY;
PVector []starsp;//星星的位置
int meteorNum;//流星的数量
PVector []position;//流星的位置
PVector []velocity;//流星的速度
Stars(int num1,int num2,int maxX,int maxY)
{
meteorNum=num1;
starsNum=num2;
starsp=new PVector[starsNum];
position=new PVector[meteorNum];//流星的位置
velocity=new PVector[meteorNum];//控制流星的向量
//初始化星空,随机生成星星
int x,y;
float xoff=0.0,yoff=10500.0;
for (int i=0;i<starsNum;i++)
{
x=int(map(noise(xoff),0,1,0,maxX+200));
y=int(map(noise(yoff),0,1,0,maxY+0));
point(x,y);
starsp[i]= new PVector(x, y);
yoff+=30;
xoff+=0.5;
}
//随机生成流星初始位置
for (int i=0;i<meteorNum;i++)
{
x=int(random(maxX));
y=int(random(maxY));
position[i]=new PVector(x,y);
x=int(random(1.0,2.0));
y=int(random(1.0,2.0));
velocity[i]= new PVector(x, y);
}
}
void display()
{
//画流星
stroke(#edccf7);
for (int i=0;i<meteorNum;i++)
{
if (i>meteorNum/2)
{
point(position[i].x, position[i].y);
}
else
{
float size=random(0.5);
ellipse(position[i].x, position[i].y,size, size);
}
}
//画普通星星
for (int i=0;i<starsNum;i++)
{
if (i%20==0)//每隔20颗,画一个大小随机的星星
{
stroke(#edccf7);
int flag=int(random(50));
float size=random(1);
if(flag>48)
{
stroke(#edccf7);
}
ellipse(starsp[i].x,starsp[i].y,size, size);
}
else//画大小为一个点的星星
{
//随机闪烁
stroke(#888deb);
int flag=int(random(50));
if(flag>48)
{
stroke(#edccf7);
}
point(starsp[i].x,starsp[i].y);
}
}
}
void update()
{
for (int i=0;i<meteorNum;i++)
{
position[i].add(velocity[i]);
}
}
void checkEdges()
{ //检查边界
for (int i=0;i<meteorNum;i++)
{
if (position[i].x > width)
{
position[i].x = 0;
} else if (position[i].x < 0)
{
position[i].x = width;
}
if (position[i].y > height)
{
position[i].y = 0;
} else if (position[i].y < 0)
{
position[i].y = height;
}
}
}
}
总结与体会
画一个简单的星空并不难,而且通过画星空的方式将一些看似枯燥的数学原理变的生动了起来。在这个项目中我其实有试过给流星添加加速度,流星就会变的有点像雨滴,这也是很有意思的变化,也许以后可以拓展。
笔者的话:
我个人还是非常喜欢这个画星空的小项目的,喜欢天空,也很喜欢星星
。(//▽//)。