A5 “运动”主题的创意编程习作
终于到了这学期最后一个编程习作了,来看看最后这学期都憋出来什么玩意吧!
- A4pluse,上次作业中对蝴蝶的飞舞动作进行了优化,并且增加了一些环境细节,日夜更替有周期,但是无规则的运动没有周期。代码很容易读,只做了简单的注释
直接看效果:
完整代码:
xoff=0;
yoff=10000;
bgc=225;//背景亮度
function setup() {
createCanvas(500, 500);
}
function draw() {
background(bgc);
drawSun();//白天
drawMoon();//夜晚
drawmountain();//山峦
move();//蝶舞
drawbutterfly();//蝶
}
function drawSun()
{
fill(205,0,0);
noStroke();
ellipse(width/2+width/2*cos(millis()/4000),width/1.5+width/2*sin(millis()/4000),20+40*noise(xoff),20+40*noise(xoff));//太阳移动
bgc=50+175*sin(millis()/4000+PI);//背景亮度变化
}
function drawMoon()//与太阳运动相差PI相位
{
fill(200,200,100);
noStroke();
ellipse(width/2+width/2*cos(millis()/4000+PI),width/1.5+width/2*sin(millis()/4000+PI),20+20*noise(xoff),20+20*noise(xoff));
}
function move()//柏林噪声的移动模式
{
xoff = xoff + 0.005;
yoff= yoff+0.005;
x= noise(xoff) * width;
y= noise(yoff)*height;
translate(x,y)
rotate(-PI/2*noise(yoff))
}
function drawmountain()//基于噪声的山峦画法
{
for(var i=1;i<=9;i++)
{
for (let x=0; x < width; x++)
{
let noiseVal = noise(i*10+x/100-xoff*i/1.2);//控制山的移动,实际是取随机的范围移动
if(bgc>100)
stroke(bgc/i-10);
else
stroke(100/i);
line(x, height*3/(10-i)+noiseVal*height/2, x, height);
}
}
}
function drawbutterfly()//基于随机性质与贝塞尔曲线的翅膀震动,D为震动幅度
{
noStroke();
fill(150,20,20);
ellipse(0,0,8,10);
arcline0424(5,0,20+50*noise(yoff),-25+10*noise(yoff),20);
arcline0424(5,0,20+50*noise(yoff),25+10*noise(yoff),20);
fill(100,20,20);
arcline0424(5,0,30+50*noise(yoff),50+10*noise(xoff),60);
arcline0424(5,0,30+50*noise(yoff),-50+10*noise(xoff),60);
}
function arcline0424(x1,y1,x2,y2,D)
{
bezier(x1,y1,x1+(x2-x1)/3+D/2-random(D),y1+(y2-y1)/3+D/2-random(D),x1+2*(x2-x1)/3+D/2-random(D),y1+2*(y2-y1)/3+D/2-random(D),x2,y2);
}
function arclinepluse0424(x1,y1,x2,y2,D)//贝塞尔曲线绘制
{
x3=x1+(x2-x1)/3+D/2-random(D);y3=y1+(y2-y1)/3+D/2-random(D);
x4=x1+(x2-x1)/3*2+D/2-random(D);y4=y1+(y2-y1)/3*2+D/2-random(D);
arcline0424(x1,y1,x3,y3,D);
arcline0424(x3,y3,x4,y4,D);
arcline0424(x4,y4,x2,y2,D);
}
- 《跃动树莓》
两个周期函数的组合的华丽碰撞加上一点点随机性。灵感来自于陈伟老师的一个智能算法实验的拓展,可以观察到周期性的组合带来的绝美画面,这边的gif演示掉帧且限制在5M以下,实际效果大家可以复制下面的代码实际跑一下看看。
(代码量很少,但效果是真的好)
var col;//控制颜色
var freq = 0.000001; //步长
var r;//半径
function setup() {
createCanvas(600, 600);
}
function draw()
{
background(25*noise(millis()/2000)); //会呼吸的宇宙背景
translate(300, 300);
rotate(millis()/2000);//随时间旋转
ellipseMode(RADIUS); //以半径模式画圆
drawstar(millis()/30000);
}
function drawstar(rate)
{
for (var i=0; i<200; i ++) { // 粒子总数
cir= 200 + 175*sin(millis()*freq*i); //粒子基础位置
col=map(cir,25,375,175,0); //颜色映射
r=map(cir,25,375,5*noise(i),15*noise(i)); //圆半径的计算,由远及近变大
//r=map(cir,25,375,2,10);
fill(col,col,200*noise(millis()/2000));//紫红为主色调
noStroke();
ellipse(cir*cos(i), cir*sin(rate*i),r,r); //周期核心
}
}
3.《追逐》 随机游走&物理仿真&3维绘图&有点意思的函数实现类的功能的p5.js 带着粒子拖尾的追随体,通过修改受力与速度来追着目标跑
小球在三维空间中平滑随机游走,追随体以小球的位置作为目标实时修正加速度与速度。简化了重量等概念实现的伪物理仿真效果。
由于这种实现面向对象的方法是第一次遇到,所以给代码做了详尽的注释,这里就不多做介绍了。
var boxSz = 200;//目标物移动的立方体半径大小
var t;//目标物
var seekers = [];//追逐者群
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);//3D画布
t = new Target();//生成一个目标
for (var i = 0; i < 20; i++) {
seekers[i] = new Seeker();//生成n个追逐者
}
}
function draw() {
background(0);
translate(0, 0, -boxSz);//把目标物移动到屏幕中间
rotateY(frameCount * 0.002);//每帧旋转,增强空间感
rotateX(frameCount * 0.004);//每帧旋转,增强空间感
t.move();//目标平滑移动
t.display();//display
for (var i = 0; i < seekers.length; i++) {
seekers[i].update();
seekers[i].seek(createVector(t.pos.x, t.pos.y, t.pos.z));
seekers[i].display();
}//更新每个追随者及其拖尾
drawBox();//画出空间盒子,增强纵深感
}
function Seeker() {
this.pos = createVector(random(-boxSz * 2, boxSz * 2), random(-boxSz * 2, boxSz * 2), random(boxSz * 2, boxSz * 4));//初始位置
this.vel = createVector(random(-2, 2), random(-2, 2), random(-2, 2));//初始速度
this.acc = createVector(0, 0, 0);//初始加速度
this.sz = random(4,8);//尺寸
this.maxSpeed = random(10, 20);//速度
this.maxForce = random(0.05, 0.2);//施加力
this.trail = [];//拖尾群
this.trailMax = random(5,20);//拖尾数目
this.display = function() {
stroke(180,180,0);
push();
translate(this.pos.x, this.pos.y, this.pos.z);
sphere(int(this.sz), int(this.sz));
pop();//画出本体
for (var i = 0; i < this.trail.length; i++) {
var pos = this.trail[i];//每次更新一个拖尾元素的状态
cor=map(i,0,this.trail.length,0,160);//拖尾颜色渐弱,使用map映射
stroke(cor,cor,20);//色彩填充
push();
translate(pos.x, pos.y, pos.z);
r=map(i,0,this.trail.length,0,this.sz)//尺寸渐弱、map映射
sphere(int(r), int(r));//拖尾大小渐弱
pop();
}//画出拖尾
}
this.update = function() {
this.vel.add(this.acc);//每帧速度变化受加速度影响
this.pos.add(this.vel);//位置受速度影响(简化了时间的概念)
this.acc.mult(0);//加速度重置
if (this.trail.length > this.trailMax) {
this.trail.splice(0, 1);
}//超出最大尾迹的拖尾不显示
if (frameCount % 5 == 0) {
var v = createVector(this.pos.x, this.pos.y, this.pos.z);
this.trail.push(v);//每五帧更新一个拖尾元素
}
}
this.applyForce = function(force) {
this.acc.add(force);//施加加速度(力,这里简化了重量的概念)
}
this.seek = function(target) {
var desired = p5.Vector.sub(target, this.pos);
desired.setMag(this.maxSpeed);//最大速度约束
var steering = p5.Vector.sub(desired, this.vel);
steering.limit(this.maxForce);//最大力约束
this.applyForce(steering);
}
}
function Target() {
this.pos = createVector(random(-boxSz, boxSz), random(-boxSz, boxSz), random(-boxSz, boxSz));//生成时取范围内的随机位置
this.display = function() {
fill(255, 100, 0);
push();
translate(this.pos.x, this.pos.y, this.pos.z);
noStroke();
sphere(20);
pop();
stroke(255,255,0);
}//画出目标物
this.move=function()//更新目标物的位置,使柏林噪声
{
this.pos.x=boxSz-2*boxSz*noise(millis()/5000);//让平滑噪声的取值错开
this.pos.y=boxSz-2*boxSz*noise((millis()+500000)/5000);
this.pos.z=boxSz-2*boxSz*noise((millis()+1000000)/5000);
}
}
function drawBox() //画出空间盒子,增强纵深感
{
stroke(0,75,175);
strokeWeight(5); // Thicker
line(-boxSz, -boxSz, boxSz, boxSz, -boxSz, boxSz);
line(-boxSz, boxSz, boxSz, boxSz, boxSz, boxSz);
line(-boxSz, -boxSz, boxSz, -boxSz, boxSz, boxSz);
line(boxSz, -boxSz, boxSz, boxSz, boxSz, boxSz);
line(-boxSz, -boxSz, -boxSz, boxSz, -boxSz, -boxSz);
line(-boxSz, boxSz, -boxSz, boxSz, boxSz, -boxSz);
line(-boxSz, -boxSz, -boxSz, -boxSz, boxSz, -boxSz);
line(boxSz, -boxSz, -boxSz, boxSz, boxSz, -boxSz);
line(-boxSz, -boxSz, boxSz, -boxSz, -boxSz, -boxSz);
line(-boxSz, boxSz, -boxSz, -boxSz, boxSz, boxSz);
line(boxSz, -boxSz, boxSz, boxSz, -boxSz, -boxSz);
line(boxSz, boxSz, -boxSz, boxSz, boxSz, boxSz);
}
4.类实现面向对象的简单粒子系统(一个经典的连线例子)
感觉,p5里的类也太难用了吧,代码写出来还不好看
const particles = [];
const number=50;
function setup() {
createCanvas(500, 500);
const particlesLength =100 ;// 粒子数量
for(let i=0;i<particlesLength;i++)
{
particles.push(new Particle());
}
}
function draw() {
background('#34495e');
particles.forEach((p,index) => {
p.customUpdate();
p.customDraw();
p.checkParticles(particles.slice(index));}
);
mouse=createVector(mouseX,mouseY);
}
class Particle {
constructor() {
this.pos = createVector(random(width),random(height));
// 粒子的大小
this.size = random(5,10);
// 移动速度
this.vel = createVector(random(-2,2),random(-2,2));
}
customDraw() {
// 绘制单个粒子
noStroke();
fill('#1abc9c');
circle(this.pos.x,this.pos.y,this.size);
}
customUpdate(){
this.pos.add(this.vel);
this.customEdges();
}
customEdges() {
if(this.pos.x < 0 || this.pos.x > width) {
this.vel.x *= -1;
}
if(this.pos.y < 0 || this.pos.y > height) {
this.vel.y *= -1;
}
}
// 粒子连线
checkParticles(particles) {
particles.forEach(particle => {
// 距离矩阵来限制范围
const d = dist(this.pos.x, this.pos.y,particle.pos.x,particle.pos.y);
if(d >50){
stroke(255,0,0);
//line(this.pos.x, this.pos.y,particle.pos.x,particle.pos.y);
this.size=this.size-0.0001;
}
if(d < 100){
stroke('#3498db');
line(this.pos.x, this.pos.y,particle.pos.x,particle.pos.y);
this.size=this.size+0.0001;
}
})
}
}