一、要求
主题:自拟
技术:
参考《代码本色》教程,运用不少于3个章节的动画技术,实现一个交互应用,将动画技术充分运用于交互过程中;
最好能充分融入其他课程的知识。
报告:
撰写一篇博文/推文,介绍该应用的背景、创意、功能、用法、技术等各个方面,并可自行选定特殊主体做深入讨论,尽可能从多种角度、关联多学科的方式来探讨;
开发工具:p5.js, processing, openframeworks, unity, 若涉及其他工具或语言,要提前找老师讨论并征求意见
二、实践
1、作品预览
2、开发工具
Windows10&processing3(Java)
3、功能及关键代码
(1)、GUI界面设计
页面中一共有六个控制按钮,还有四个滑块控制。六个控制按钮分别为:功能操作介绍、雨、雪、暴风雪、其他功能介绍。
GUI功能的部分代码:
birdButton birdButton;
scrollBar hs;
Handle[] handles;
helpButton helpButton;
rainButton rainButton;
snowButton snowButton;
blizzardButton blizzardButton;
otherButton otherButton;
..............
void GUI() {
birdButton.draw();
helpButton.draw();
rainButton.draw();
snowButton.draw();
blizzardButton.draw();
otherButton.draw();
hs.display();
for (int i = 0; i < handles.length; i++) {
handles[i].update();
handles[i].display(i);
}
(2)、三种粒子动态
下雨:
雨和暴雪数组是相似的,唯一的区别是:(1)暴雪类使用rotation()函数使其看起来像是在大风中行进的雪;(2)使用ellipse() 创建雪的形状;(3)雪的大小没有随机性;(4)使用push和pop矩阵防止随机旋转受到影响。以及使generateBlizzard()函数中的屏幕变白,使其看起来更自然。
class Rain {
float xpos, ypos, z, len, yspeed;
Rain() {
xpos = random(width);
ypos = random(-1000, -500);
z = random(0, 10);
len = map(z, 0, 1, 1, 2);
yspeed = map(z, 0, 20, 4, 10);
}
void fall() {
ypos = ypos + yspeed;
float gravity = map(z, 0, 20, 0, 0.2);
yspeed = yspeed + gravity;
if (ypos > height) {
ypos = random(-1000, -500);
yspeed = map(z, 0, 20, 10, 30);
}
}
void display() {
float thickness = map(z, 0, 20, 1, 2);
fill(0, 0, 225, 90);
rect(xpos, ypos, thickness, (ypos+len)/10);
}
}
下雪:
snow类使用一个数组,该数组在一个名为getsnow()的函数中初始化和填充。我参考了一个关于数组的处理教程,它演示了如何在屏幕上制作点。因为它是一个简单的例子,并且与缺乏随机性保持一致,所以除了值(例如雪的数量和Y轴位置使雪看起来更自然)、变量名和封装之外,我没有对代码做太多的更改。
class Snow {
float x, y;
float diameter;
float speed;
float r;
float g;
float b;
int direction = 1;
Snow(float xpos, float ypos, float dia, float sp) {
x = xpos;
y = ypos;
diameter = dia;
speed = sp;
}
void move() {
if (y > height) {
y = -50;
}
y += (speed * direction);
}
void display() {
//r = x/4;
//g= y/4;
//b = (x+y)/8;
// fill(r,g,b);
ellipse(x, y, diameter, diameter);
}
}
暴风雪:
class Blizzard {
float xposBlizzard, yposBlizzard, z, len, yspeed;
Blizzard() {
xposBlizzard = random(width);
yposBlizzard = random(-1000, -500);
z = random(20, 100);
len = map(z, 0, 20, 10, 20);
yspeed = map(z, 0, 20, 1, 20);
}
void fall() {
yposBlizzard = yposBlizzard + yspeed;
float gravity = map(z, 0, 20, 0, 0.02);
yspeed = yspeed + gravity;
if (yposBlizzard > height) {
yposBlizzard = random(-1000, -500);
yspeed = map(z, 0, 20, 1, 20);
}
}
void display() {
pushMatrix();
rotate(radians(z));
fill(255);
ellipse(xposBlizzard, yposBlizzard, 5, 5);
popMatrix();
}
}
(3)、鼠标和键盘交互
例如:按“2”键会在鼠标光标所在位置种一棵随机大小的小树。
首先,声明arraylist并为tree类打开了一个新标签。使用add()函数添加树,该函数根据光标的X轴和Y轴传递值。这允许在用户选择的位置创建树,以及树的高度。与其他对象(如human)相比,使用forloop和get()函数(位于draw()函数内部)访问tree类。它将获取索引并将其传递给get()函数,然后获取并调用display()函数,该函数将绘制树。display()函数包括使用rect()函数绘制树的木材,使用triangle()函数作为基础绘制树叶,然后使用更多的triangle()函数在其顶部添加forloop和更改的值。
void Weather() {
if (key == '3') {
snowFlag = true;
}
if (key == '2') {
treeFlag = true;
trees.add(new Tree(mouseX, mouseY));
keyReleased();
}
if (key == '4') {
rainFlag = true;
}
if (key == '5') {
blizzardFlag = true;
}
if (key == '1') {
birdFlag = true;
}
else if (key == '6') { //remove weather functions
snowFlag = false;
treeFlag = false;
rainFlag = false;
blizzardFlag = false;
birdFlag = false;
}
}
......
void checkFlags() {
if (snowFlag == true) {
generateSnow();
}
if (rainFlag == true) {
generateRain();
}
if (blizzardFlag == true) {
generateBlizzard();
}
if (birdFlag == true) {
generateBird();
}
}
(4)、图片动画
class Bird {
PImage[] images;
int imageCount;
int frame;
float xposBird;
float yposBird = height * 0.25;
Bird(String imagePrefix, int count) {
imageCount = count;
images = new PImage[imageCount];
for (int i = 0; i < imageCount; i++) {
String filename = imagePrefix + i + ".png";
images[i] = loadImage(filename);
}
}
void display(float xpos, float ypos) {
frame = (frame+1) % imageCount;
image(images[frame], xpos, ypos);
image(images[frame], xpos, ypos);
}
void draw() {
float drag = 30.0;
float dx = mouseX - xposBird;
xposBird += dx/drag;
float dy = mouseY - yposBird;
yposBird += dy/drag;
bird_1.display(xposBird-bird_1.getWidth()/2, yposBird-bird_1.getHeight()/2);
}
int getWidth() {
return images[0].width;
}
int getHeight() {
return images[0].height;
}
}
(5)、操作说明
键盘交互:
“a”向左走
“d”向右走
“1”召唤一只鸟
“2”种一棵树
“3”下雪
“4”下雨
“5”暴风雪
“6”删除所有内容
鼠标交互:
-鼠标点击狗来改变它摇摆尾巴的速度。
-鸟跟着光标移动。
-生成的树由光标位置决定,包括放置树的位置(光标的X轴)和树的高度(光标的Y轴)。
if (mousePressed) {
fill(255, 255, 255, 50);
rect(imageX, imageY, 1000, 500);
fill(0);
textSize(18);
textFont(myFont);
text("功能简介", imageX + 20, imageY + 20);
textSize(12);
text("“a”和“d”键移动人的向左向右。或者,使用上面的灰色滑块移动人。", imageX + 20, imageY + 40);
text("按'1'键召唤小鸟跟随光标。或者,单击bird图标来调用它。它会跟随你的光标。", imageX + 20, imageY + 60);
text("按“2”键种植一棵树。根据光标的x轴创建树;树的高度取决于光标的y轴;树干的宽度是随机的;叶的高度、宽度和层数是随机的。", imageX + 20, imageY + 80);
text("按‘3’键下雪。或者,点击“Snow”按钮。可以使用滑块来改变雪的直径。", imageX + 20, imageY + 100);
text("按“4”键使天下雨。或者,点击“Rain”按钮。可以使用滑块来改变水滴的大小。", imageX + 20, imageY + 120);
text("按‘5’键制造暴风雪。或者,点击“暴雪”按钮。使用黑色的“暴雪”滑块来改变速度。", imageX + 20, imageY + 140);
text("按'6'键删除所有内容。", imageX + 20, imageY + 160);
text("鼠标点击狗来改变它摇尾巴的速度。", imageX + 20, imageY + 180);
}
}
......
if (mousePressed) {
fill(255, 255, 255, 50);
rect(imageX, imageY, 1000, 180);
fill(0);
textSize(18);
text("树、删除和狗的说明", imageX + 20, imageY + 20);
textSize(12);
text("对于树,按'2'键种植一棵树。根据光标的x轴创建树;\n \t树的高度取决于光标的y轴;树干的宽度是随机的;叶的高度、宽度和层数是随机的。", imageX + 20, imageY + 40);
text("按'6'键删除所有内容。", imageX + 20, imageY + 80);
text("鼠标点击狗来改变它摇尾巴的速度.", imageX + 20, imageY + 100);
text("在‘介绍’按钮中列出的更多功能。.", imageX + 20, imageY + 120);
}
4、知识涉及
1)、ArrayList
构造函数:
ArrayList<Type>()
ArrayList<Type>(initialCapacity)
参数:
类型类名:要放置在ArrayList中的对象的数据类型。
initialCapacity int:定义列表的初始容量;默认为空。
ArrayList存储的对象数量可变。这类似于创建对象数组,但使用arraylist,可以轻松地添加和删除arraylist中的项,并动态调整其大小。这可能非常方便,但比在使用许多元素时创建对象数组要慢。请注意,对于整数、浮点数和字符串的可调整大小的列表,可以使用处理类intlist、floatlist和stringlist。
ArrayList是Java列表接口的可调整数组实现。它有许多方法用于控制和搜索其内容。例如,arraylist的长度由其size()方法返回,该方法是列表中元素总数的整数值。元素用add()方法添加到arraylist,用remove()方法删除。get()方法返回列表中指定位置的元素。
2)、中文字体的设置与使用
a、首先,使用PFont类的静态函数list,可以列出当前系统可用字体,返回一个String[]数组。使用printArray显示。
b、要在Processing中绘制文字,必须创建PFont字体类型。
如图,根据系统字体创建,最简单的用法为:
PFont字体变量=createFont("字体名",字体大小);
c、在默认渲染模式下,对于矢量字体,createFont函数中指定的字体大小无关紧要。但是在P2D模式和P3D模式下,createFont是创建纹理图片。
函数使用说明:
PFont -> 创建字体类型对象
loadFont("字体类型") //在工具中创建字体文件 (.vlw 类型的文件)
textFont(类型对象) //引用新创建的字体类型对象
textSize(x) //修改字体的大小
text("文本文件", x, y) //x,y 设定水平和垂直的位置, 这个位置是想对于文字的基线而言的
text("文本文件", 26, 30, width, height) //同上, width, height 表示绘制文字的区间高低宽度,
5、参考资料
1)、《代码本色:用编程模拟自然系统》
2)、Processing 入门教程(三十七)图层动画:https://blog.csdn.net/qq_39097425/article/details/85110281
3)、Processing 入门教程(十九) 图片动画:http://www.pianshen.com/article/1148135405/
4)、Processing大神教程:The Nature of Code -- 粒子系统:https://www.bilibili.com/video/av4044431/
5)、Processing 教程3--Arry和For Loop :http://www.sohu.com/a/205366456_654105