代码本色+processing
创意编程
实验要求
参考《代码本色》0-4章节,针对每个章节,编写5个习作。要求每个习作至少参考2个案例且必须有一定拓展。
本次作业按照规定要求认真学习书上相关内容,并运行浏览学习了每一段给出的范例代码。每一个习作都至少参考2个案例并且结合自己的想法加以拓展。
代码本色 第0章 引言
在这一章中介绍了随机和噪声,在书中介绍的原理我们看的很清楚,在随机游走的过程中有两种情况一个是有五种可能行走,一个是九种可能行走:
通过参考随机游走和噪声函数的学习我制作了一个可以随机游走且利用noise函数来实现颜色的变化:
class Walker{
float x,y;
float tx,ty;
float r,b,g;
float tr,tb,tg;
Walker()
{
tx=0;
ty=1000;
tr=0;
tb=200;
tg=200;
}
void step()
{
x=map(noise(tx),0,1,0,width);
y=map(noise(ty),0,1,0,height);
tx+=0.01;
ty+=0.01;
}
void c() //color
{
r=map(noise(tr),0,1,0,255);
b=map(noise(tb),0,1,0,255);
g=map(noise(tg),0,1,0,255);
tr+=0.05;
tb+=0.05;
tg+=0.05;
}
}
Walker w;
void setup()
{
size(500,500);
w=new Walker();
}
void draw()
{
w.step();
w.c();
fill(w.r,w.b,w.g);
ellipse(w.x,w.y,30,30);
}
代码本色 第1章 向量
通过对这一章的processing的学习,学到了一个很有用的东西就是PVector的使用,他是一个作为一种数据结果其存储,有利于我们对仿真的实现,再加上同上上一章节对于随机的学习我实现了一个出现任意小球,在到达一个边界以后就会到另一边:
int ball=20;
class Mover
{
PVector[] location=new PVector[ball];
PVector[] v=new PVector[ball];
PVector[] acceleration=new PVector[ball];
Mover()
{
for(int i=0;i<ball;i++)
{
location[i]=new PVector(random(width),random(height));
v[i]=new PVector(random(-2,2),random(-2,2));
acceleration[i]=PVector.random2D();
}
}
void update()
{
for(int i=0;i<ball;i++)
{
PVector dir=new PVector(0,0);
for(int j=0;j<ball;j++)
{
if(i==j)
{
}
else
{
PVector tempdir=PVector.sub(location[j],location[i]);
dir.add(tempdir);
}
}
dir.normalize();
dir.mult(0.5);
acceleration[i]=dir;
v[i].add(acceleration[i]);
v[i].limit(10);
}
for(int i=0;i<ball;i++)
{
location[i].add(v[i]);
}
}
void display()
{
stroke(0);
fill(175);
for(int i=0;i<ball;i++)
{ellipse(location[i].x,location[i].y,16,16);
}
}
void checkEdges()
{
for(int i=0;i<ball;i++)
{
if(location[i].x>width)
{
location[i].x=0;
}
else if(location[i].x<0)
{
location[i].x=width;
}
if(location[i].y>height)
{
location[i].y=0;
}
else if(location[i].y<0)
{
location[i].y=height;
}
}
}
}
Mover mover;
void setup()
{
size(480,480);
mover=new Mover();
smooth();
}
void draw()
{
fill(255,25);
rect(0,0,480,480);
fill(0,255);
mover.display();
mover.update();
mover.checkEdges();
}
代码本色 第2章 力
这里我实现的效果主要是根据代码本色当中的重力和弹力来模拟小球在下落的过程当中碰到一个物体然后改变了运动轨迹的想法。这里也包含了牛顿运动定律,在学习代码本色当中给我最好的感觉就是在做的过程当中能够很好地感觉到如何用代码去模仿自然。
void draw() {
background(100);
ps.addParticle();
PVector gravity = new PVector(0,0.1);
ps.applyForce(gravity);
ps.applyRepeller(repeller);
ps.run();
repeller.display();
}
class ParticleSystem {
ArrayList<Particle> particles;
PVector origin;
ParticleSystem(PVector location) {
origin = location.copy();
particles = new ArrayList<Particle>();
}
void addParticle() {
particles.add(new Particle(origin));
}
void applyForce(PVector f) {
for (Particle p: particles) {
p.applyForce(f);
}
}
void applyRepeller(Repeller r) {
for (Particle p: particles) {
PVector force = r.repel(p);
p.applyForce(force);
}
}
void run() {
Iterator<Particle> it = particles.iterator();
while (it.hasNext()) {
Particle p = (Particle) it.next();
p.run();
if (p.isDead()) {
it.remove();
}
}
}
}
class Repeller {
float strength = 100;
PVector location;
float r = 10;
Repeller(float x, float y) {
location = new PVector(x,y);
}
void display() {
stroke(255);
fill(255);
ellipse(location.x,location.y,r*2,r*2);
}
PVector repel(Particle p) {
PVector dir = PVector.sub(location,p.location);
float d = dir.mag();
dir.normalize();
d = constrain(d,5,100);
float force = -1 * strength / (d * d);
dir.mult(force);
return dir;
}
}
class Particle {
PVector location;
PVector velocity;
PVector acceleration;
float lifespan;
float mass = 1;
Particle(PVector l) {
acceleration = new PVector(0,0);
velocity = new PVector(random(-1,1),random(-2,0));
location = l.copy();
lifespan = 255.0;
}
void run() {
update();
display();
}
void applyForce(PVector force) {
PVector f = force.copy();
f.div(mass);
acceleration.add(f);
}
void update() {
velocity.add(acceleration);
location.add(velocity);
acceleration.mult(0);
lifespan -= 2.0;
}
void display() {
stroke(255,lifespan);
fill(255,lifespan);
ellipse(location.x,location.y,8,8);
}
boolean isDead() {
if (lifespan < 0.0) {
return true;
} else {
return false;
}
}
}
代码本色 第3章 震荡
在通过对力的学习以后做了一个简单的乒乓球小游戏,具体的描述会在游戏中进行;
通过鼠标控制板子的运动
function mousePressed() { //如果按下鼠标就调用此函数
if (gameScreen==0) { //按下鼠标时,如果当前界面是游戏准备开始界面
startGame(); //调用游戏开始方法
}
if (gameScreen==2) { //按下鼠标时,如果当前界面是游戏结束界面
restart(); //调用游戏重新开始函数
}
}
定义使球保持在画布的方法
function keepInScreen() {
// 第一种情况,球撞击到地面
if (ballY+(ballSize/2) > height) { //如果球的下表面的纵坐标超过画布的最下边
gameOver(); //调用游戏结束方法
}
// 第二种情况,球撞击到天花板
if (ballY-(ballSize/2) < 0) { //如果球的上表面的纵坐标超过画布的最上边
makeBounceTop(0); //调用上边反弹方法,并将画布最上边的纵坐标传回去
}
// 第三种情况球撞击到左边的墙
if (ballX-(ballSize/2) < 0) { //如果球的左表面的纵坐标超过画布的最左边
makeBounceLeft(0); //调用左边反弹方法,并将画布最左边的横坐标传回去
}
// 第四种情况球撞击到右边的墙
if (ballX+(ballSize/2) > width) { //如果球的右表面的纵坐标超过画布的最右边
makeBounceRight(width); //调用右边反弹方法,并将画布最右边的横坐标传回去
}
}
产生重力的方法
function applyGravity() {
ballSpeedVert += gravity; //在球的垂直速度上加上重力加速度
ballSpeedVert -= (ballSpeedVert * airfriction); //在球的垂直速度上减去空气阻力的加速度
ballY += ballSpeedVert; //使球产生垂直位移
}
function applyHorizontalSpeed() { //定义使球产生水平速度的方法
ballSpeedHorizon -= (ballSpeedHorizon * airfriction); //使球的水平速度减去空气阻力造成的加速度
ballX += ballSpeedHorizon; //使球产生水平位移
}
function makeBounceBottom(surface) { //定义下边反弹方法, 并获取球的下表面的纵坐标
ballY = surface-(ballSize/2); //设置球的中心位置
ballSpeedVert*=-1; //使球的垂直速度方向翻转
ballSpeedVert -= (ballSpeedVert * friction); //在球的垂直速度上减去碰撞产生的减速
}
function makeBounceTop(surface) { //定义上边反弹方法并获取球的上表面的纵坐标
ballY = surface+(ballSize/2); //设置球的中心位置
ballSpeedVert*=-1; //使球的垂直速度方向翻转
ballSpeedVert -= (ballSpeedVert * friction); //在球的垂直速度上减去碰撞产生的减速
}
function makeBounceLeft(surface) { //定义左边反弹方法并获取球的左表面的纵坐标
ballX = surface+(ballSize/2); //设置球的中心位置
ballSpeedHorizon*=-1; //使球的水平速度方向翻转
ballSpeedHorizon -= (ballSpeedHorizon * friction); //在球的水平速度上减去碰撞产生的减速
}
function makeBounceRight(surface) { //定义右边反弹方法并获取球的右表面的纵坐标
ballX = surface-(ballSize/2); //设置球的中心位置
ballSpeedHorizon*=-1; //使球的水平速度方向翻转
ballSpeedHorizon -= (ballSpeedHorizon * friction); //在球的水平速度上减去碰撞产生的减速
}
function watchRacketBounce() { //定义球拍反弹球的方法
var overhead = mouseY - pmouseY; //定义和设置球拍运动的速度
//如果球的横坐标位于球拍横坐标范围内
if ((ballX+(ballSize/2) > mouseX-(racketWidth/2)) && (ballX-(ballSize/2) < mouseX+(racketWidth/2))) {
//并且球中心的纵坐标与球拍中心的纵坐标小于球的半径加上球拍厚度的一半再加上球拍移动的距离
if (dist(ballX, ballY, ballX, mouseY-haddle)<=(ballSize/2)+abs(overhead)+(racketHeight/2)) {
makeBounceBottom(mouseY-haddle-(racketHeight/2)); //调用下边反弹方法并将球拍上表面的纵坐标传回去,使球向上反弹
ballSpeedHorizon = ballSpeedHorizon + (ballX - mouseX)/10; //使球的水平速度改变,使方向翻转并且越靠球拍边缘反弹速度越大
addScore(); //每次用球拍击球,得分加一
if (overhead<0) { //如果球拍是向上运动的
ballY+=overhead; //改变球的位置
ballSpeedVert+=overhead/2; //使球的垂直速度加上球拍运动的速度
}
}
}
}
添加在过程中可移动的板子
function boardAdder() {
if (millis()-lastAddTime > boardInterval) {
var randWidth = round(random(minGapWidth, maxGapWidth)); //定义板子空隙的宽度的变量,设置为一个随机数,范围是从最小值到最大值
var randX = round(random(0, width-randWidth)); //定义左边板子的宽度的变量,设置为一个随机数,范围是从0到画布的宽度减去间隙的宽度
var randBoard = [randX/2, -boardHeight/2, randWidth, boardHeight, 0]; //定义一个记录这个新的板子参数的数组,并设置数组的值
boards.push(randBoard); //将这个新的板子的参数添加到板子数组中,板子数组中存储着所有没有被删除的板子
lastAddTime = millis(); //设置上次添加板子的时间为当前的时间
}
}
function boardMover(index) { //定义板子移动的方法
var board = boards[index]; //定义一个数组,并将板子数组中的一个板子取出来,把它的参数赋值给该数组
board[1]+= boardSpeed; //使这个数组的第二个元素,即板子的纵坐标,加上板子的速度,这样就可以使板子向下移动
}
function boardRemover(index) { //定义板子删除的方法
var board = boards[index]; //定义一个数组,并将板子数组中的一个板子取出来,把它的参数赋值给该数组
if (board[1]> height+boardHeight/2) { //如果板子向下移动是,它的纵坐标超过了画布的高度,即画布的最下边
boards.splice(index,1); //使用删除数组元素的方法,将这个板子从板子数组中删除
}
}
定义板子碰撞
function watchBoardCollision(index) {
var board = boards[index]; //定义一个数组,并将板子数组中的一个板子取出来,把它的参数赋值给该数组
// get gap wall settings
var boardLeftX = board[0]; //定义左边板子的横坐标,并把板子的横坐标赋值给它
var boardLeftY= board[1]; //定义左边板子的纵坐标,并把板子的纵坐标赋值给它
var gapBoardWidth = board[2]; //定义板子间隙的宽度,并给板子的宽度赋值给它
var gapBoardHeight = board[3];
var boardScored = board[4]; //定义通过板子的得分情况,得分为1,未得分为0
var boardLeftWidth = 2*boardLeftX; //定义左边板子的宽度,并给它赋值
var boardRightX = width-(width-(boardLeftWidth+gapBoardWidth))/2; //定义右边板子的横坐标,并给它赋值
var boardRightY = boardLeftY; //定义右边板子的纵坐标,并给它赋值
var boardRightWidth =width-(boardLeftWidth+gapBoardWidth) ; //定义右边板子的宽度,并给它赋值
var gapBoardY = boardLeftY; //定义板子间隙的纵坐标,并给它赋值
var boardMoveDistance = boardSpeed; //定义板子在两个帧之间移动的距离
//左边板子向下反弹球
if (
(ballY>boardLeftY)&& //如果球在板子的下方
(ballX-(ballSize/2)<boardLeftX+boardLeftWidth/2) && //并且球没有超出板子的右边缘
(ballX+(ballSize/2)>boardLeftX-boardLeftWidth/2) //并且球没有超出板子的左边缘
){
if(dist(ballX, ballY, ballX, boardLeftY)<=(ballSize/2+boardMoveDistance+boardHeight/2)){ //继续判断,如果球与板子相接触
makeBounceTop(boardLeftY+boardHeight/2); //调用上边反弹方法并将板子下表面的纵坐标传回去,使球向下反弹
ballY+=boardMoveDistance; //设置球的位置,使球增加一段向下的移动距离
ballSpeedVert+= boardSpeed; //设置球的垂直速度,使球在垂直速度上增加板子的移动速度
}
}
//右边板子向下反弹球
if (
(ballY> boardRightY)&& //如果球在板子的下方
(ballX-(ballSize/2)<boardRightX+boardRightWidth/2) && //并且球没有超出板子的右边缘
(ballX+(ballSize/2)>boardRightX-boardRightWidth/2) //并且球没有超出板子的左边缘
){
if(dist(ballX, ballY, ballX, boardRightY)<=(ballSize/2+boardMoveDistance+boardHeight/2)){ //继续判断,如果球与板子相接触
makeBounceTop(boardRightY+boardHeight/2); //调用上边反弹方法并将板子下表面的纵坐标传回去,使球向下反弹
ballY+=boardMoveDistance; //设置球的位置,使球增加一段向下的移动距离
ballSpeedVert+= boardSpeed; //设置球的垂直速度,使球在垂直速度上增加板子的移动速度
}
}
控制板子的方法
function boardHandler() { 控制板子的方法
for (var i = 0; i < boards.length; i++) { //使用for循环,将数组的里每个板子都进行控制
boardRemover(i); //调用移动板子的方法,将板子从数组中删除
boardMover(i); //调用清除板子的方法,使板子可以向下移动
boardDrawer(i); //调用绘制板子的方法,将板子在画布中显示出来
watchBoardCollision(i); //调用板子碰撞的方法,来控制球的运动与得分
}
}
代码本色 第4章 粒子系统
在学习了代码本色第4章的粒子系统以后了解到了许多有关于粒子效果的东西,参考了上面的代码以后自己也动手做了一个粒子效果虽然比较简单,但是看起来还是 挺有意思的,这里最主要的就是运用了一个公式:
这个公式可以很好地让我们实现我们想要的效果:
使用P3D来实现
void setup(){
size(1080,680,P3D);
colorMode(HSB);
Rotate = new PVector();
particles = new ArrayList<Particle>();
for(int i = 0; i < num; i++){
particles.add( new Particle() );
}
background(255);
}
下面给矩阵计算3个轴向的旋转,根据时间来变化
Rotate = new PVector(frameCount*0.003, frameCount*0.04, frameCount*0.03);
pushMatrix();
translate(width/2, height/2);
rotateX(Rotate.x);
rotateY(Rotate.y);
rotateZ(Rotate.z);
刷新粒子类
for(int i = particles.size()-1; i >= 0; i--){
tempP = particles.get(i);
tempP.move();
}
popMatrix();
}
画面的流动
void move(){
update();
display();
}
void update(){
pos.x = dis*sin(radian.x)*cos(radian.y);
pos.y = dis*cos(radian.x)*cos(radian.y);
pos.z = dis*sin(radian.y);
}
void display(){
h = noise(radian.x*0.3, frameCount*0.012)*95 + 130 + map(mouseX, 0 ,width, -20, 20);
noStroke();
fill(h,85,95);
translate(pos.x,pos.y,pos.z);
sphere(_weight);
translate(-pos.x,-pos.y,-pos.z);
}
}
最终的效果如下图所示: