问题描述
我对 Processing 还是很陌生,我正在尽我最大的努力弄清楚这个项目:(
我正在尝试创建一个有序列的动画,在整个动画中播放一个 soundFile。但是,只有 class Two 需要有声音响应元素。
所有三个类对象在各自的草图文件中都可以正常工作。但当他们在一个草图中作为班级时则不然。
现在我只有第二类的声音文件,因为我不知道如何拥有相同的声音文件但响应不同的类。
当不同的类需要打开和关闭时,我不确定 背景 是如何工作的。 我也不确定我的排序方式是否正确...... 我很感激任何帮助。谢谢各位!
One objectone;
Two objecttwo;
Three objectthree;
int myAnimationCounter = 0; //set myAnimationCounter incerment to 0,so allows to reset to 0 to start the animation.
int myAnimationCounter1 = 0;
int myAnimationCounter2 = 0;//set a parallel myAnimationCounter incerment to 0.
boolean startAnimation = false; //Boolean switch to control starting animation.
boolean openingAnimation = false;
void setup() {
size(displayWidth,displayHeight); //sketch present
frameRate(50);
objectone=new One();
objecttwo=new Two();
objectthree=new Three();
}
void draw() {
if (openingAnimation) { //start the opening seq when press spacebar
if ( myAnimationCounter>0 && myAnimationCounter<750) {
objectone.show1();
}
myAnimationCounter++;
}
//when clicking the mouse 'openingAnimation' stops,playing 'startAnimation'
if (startAnimation) {
if (myAnimationCounter1 > 0 && myAnimationCounter1 < 2500) {
objecttwo.update2();
objecttwo.show2();
} else
if (myAnimationCounter1 > 2500 && myAnimationCounter1 < 3000) {
objectthree.update3();
objectthree.show3();
}
myAnimationCounter1++;
//rect shape drawing at the bottom of the screen showing the playing progression of the animation
if (myAnimationCounter2 > 0 && myAnimationCounter2 < 9000) { //independent animation through out the whole animation
fill(d);
nostroke();
rect(width,height-10,10,width/myAnimationCounter2);
}
myAnimationCounter2++;
}
}//end of draw
void keypressed() {
if (key == ' ') {
openingAnimation = true;
}
}
void mousepressed() {
if (mousepressed) {
startAnimation = true;
openingAnimation=false;
myAnimationCounter=0;
myAnimationCounter1=0;
myAnimationCounter2=0;
}
class One {
void show1() {
background(bg);
}
}
class Two {
import processing.sound.*;
SoundFile loadSound;
Amplitude measureVol;
void update2() {
loadSound = new SoundFile(this,"music.mp3");
loadSound.loop();
measureVol = new Amplitude(this);
measureVol.input(loadSound);
}
void show2() {
float howLoud = measureVol.analyze();
}
}
class Three {
void update3() {
background(0);
}
void show3() {
}
}
解决方法
您可以将声音代码放在自己单独的类中。然后,在 Setup
中实例化该类并作为参数注入到类 Two
中。确保用于提供音频功能的此类具有用于您希望从 Two
中调用的操作的公共方法。
例如:
class MyAudioClass {
SoundFile loadSound;
Amplitude measureVol;
public MyAudioClass() {
measureVol = new Amplitude(this);
}
public void loadSound(String soundFileName) {
loadSound = new SoundFile(this,soundFileName);
measureVol = input(loadSound);
}
public void playSound() {
loadSound.loop();
}
public float getHowLoud() {
return measureVol.analyze();
}
}
然后,在 Two
中:
class Two {
private MySoundClass mySoundClass;
// using Constructor Dependency Injection
public Two(MySoundClass mySoundClass) {
this.mySoundClass = mySoundClass;
}
public float showHowLoud() {
return mySoundClass.getHowLoud();
}
(注意:我没有使用过Processing,我只是根据最佳猜测转换你写的东西——所以可能需要一些调整。)
IDK 如果您还需要将类注入到 One
或 Three
。也许您只需要在 Setup
中开始播放?只要您在 Setup
中处理与 Two
中相同的实例(或您注入的任何其他类),事情应该没问题。
这种编码模式通常被称为依赖注入或作为控制反转的一个例子,并且非常有用。一种变体是使用 setter 方法而不是构造函数来传递您的声音资源类。
控制反转——DI背后的概念
这表明类不应静态配置其依赖项 但应该由外部的其他类配置。
这是S.O.L.I.D的第五项原则——五项基本原则 鲍勃叔叔的面向对象编程和设计——其中指出 一个类应该依赖于抽象而不是具体化(在 简单的术语,硬编码)。
按照原则,一个班级应该专注于完成 它的职责,而不是创建它需要的对象 履行这些职责。这就是依赖注入的地方 发挥作用:它为类提供所需的对象。
以上摘自文章A Quick Intro to Dependency Injection。
,根据您的代码,这是我理解的顺序:
- 如果按下空格并且
One
的实例将显示前 750 个“滴答声”(15 秒,每秒 50 帧) - 如果按下鼠标,所有计数器和标志都会重置
-
Two
的一个实例更新并显示第一个 2500 个“滴答声”(50 秒 50 帧) - 然后
Three
的实例将更新 并显示 2500 到 3000 个“滴答声”(50 秒到 60 秒,每秒 50 秒) - 同时,对于前 9000 个“滴答声”(180 秒,每秒 50 帧),将显示一个缩小的矩形
关于background()
:它只是清除先前绘制的内容,否则绘制的内容会累积。
以这个基本示例为例:
void setup(){
size(300,300);
background(255);
}
void draw(){
if(mousePressed){
line(mouseX,mouseY,pmouseX,pmouseY);
}
}
void keyPressed(){
background(255);
}
当您在屏幕上拖动鼠标时,线条不断被绘制(甚至在彼此之上)。当按下一个键时,背景(设置为白色)会重新绘制在所有先前行的顶部,并清除它们。
这给您的代码带来了另一个小问题:
void mousePressed() {
if (mousePressed) {
startAnimation = true;
openingAnimation=false;
myAnimationCounter=0;
myAnimationCounter1=0;
myAnimationCounter2=0;
}
}
mousePressed()
函数仅在鼠标按下时被调用,这使得检查鼠标是否按下的 if 条件变得多余。这应该足够了:
void mousePressed() {
startAnimation = true;
openingAnimation=false;
myAnimationCounter=0;
myAnimationCounter1=0;
myAnimationCounter2=0;
}
关于背景声音,它似乎在 update2()
中每秒加载多次:
class Two {
import processing.sound.*;
SoundFile loadSound;
Amplitude measureVol;
void update2() {
loadSound = new SoundFile(this,"music.mp3");
loadSound.loop();
measureVol = new Amplitude(this);
measureVol.input(loadSound);
}
void show2() {
float howLoud = measureVol.analyze();
}
}
我的假设是 update()
将被多次调用以更新数字,而 show()
将被多次调用以呈现这些值(如文本、形状等)
由于您在 Two
中实例化了 setup()
,因此您可以在 Two
的构造函数中加载一次声音:
class Two {
SoundFile loadSound;
Amplitude measureVol;
float howLoud;
// this gets called once when you make a new instance (e.g = new Two())
Two() {
loadSound = new SoundFile(this,"music.mp3");
loadSound.loop();
measureVol = new Amplitude(this);
measureVol.input(loadSound);
}
void update() {
howLoud = measureVol.analyze();
}
void show() {
fill(127);
text("Two instance show() -> howLoud: " + howLoud,10,15);
}
}
通过这种方式,声音被创建并循环一次,然后仅在调用 update()
时进行分析(在 startAnimation
的前 2500 次更新内)
为了保持一致,我建议使用 update()
/show()
并理想地将实例命名为类似于其他变量(例如 objectOne
、objectTwo
、objectThree
)。理想情况下,import 语句应位于代码的顶部。
以下是对您的代码稍加调整的版本,并考虑到了上述说明:
import processing.sound.*;
One objectOne;
Two objectTwo;
Three objectThree;
int myAnimationCounter = 0; //set myAnimationCounter incerment to 0,so allows to reset to 0 to start the animation.
int myAnimationCounter1 = 0;
int myAnimationCounter2 = 0;//set a parallel myAnimationCounter incerment to 0.
boolean startAnimation = false; //Boolean switch to control starting animation.
boolean openingAnimation = false;
color bg = color(255);
color d = color(192,0);
void setup() {
size(displayWidth,displayHeight); //sketch present
frameRate(50);
objectOne = new One();
objectTwo = new Two();
objectThree = new Three();
}
void draw() {
background(255);
if (openingAnimation) { //start the opening seq when press spacebar
if ( myAnimationCounter>0 && myAnimationCounter<750) {
objectOne.show();
}
myAnimationCounter++;
}
//when clicking the mouse 'openingAnimation' stops,playing 'startAnimation'
if (startAnimation) {
if (myAnimationCounter1 > 0 && myAnimationCounter1 < 2500) {
objectTwo.update();
objectTwo.show();
} else if (myAnimationCounter1 > 2500 && myAnimationCounter1 < 3000) {
objectThree.update();
objectThree.show();
}
myAnimationCounter1++;
//rect shape drawing at the bottom of the screen showing the playing progression of the animation
if (myAnimationCounter2 > 0 && myAnimationCounter2 < 9000) { //independent animation through out the whole animation
fill(d);
noStroke();
rect(mouseX,width / myAnimationCounter2);
}
myAnimationCounter2++;
}
// debug
fill(127);
text("openingAnimation: " + openingAnimation + "\n" +
"startAnimation: " + startAnimation + "\n" +
"myAnimationCounter: " + myAnimationCounter + "\n" +
"myAnimationCounter1: " + myAnimationCounter1 + "\n" +
"myAnimationCounter2: " + myAnimationCounter2 + "\n",30);
}//end of draw
void keyPressed() {
if (key == ' ') {
openingAnimation = true;
}
}
void mousePressed() {
startAnimation = true;
openingAnimation=false;
myAnimationCounter=0;
myAnimationCounter1=0;
myAnimationCounter2=0;
}
class One {
void show() {
background(bg);
text("One instance show()",15);
}
}
class Two {
SoundFile loadSound;
Amplitude measureVol;
float howLoud;
Two() {
loadSound = new SoundFile(this,15);
}
}
class Three {
void update() {
}
void show() {
background(0);
text("Three instance show()",15);
}
}
此外,我还做了一些其他的小改动:
- 我在
text()
的底部添加了draw()
,以便更轻松地调试行为。您可以可视化布尔值和计数器的值。 - 每个
show()
方法还使用text()
来可视化正在渲染的实例 - 目前还不清楚
rect()
调用的目的是什么,但坐标在屏幕外。作为测试,我已将rect()
位置对齐到鼠标位置:随意将其更改为对您的应用程序有意义的内容。如果是动画持续时间的可视化,则假设总持续时间为 9000 个计数。仍然不清楚意图是将其呈现为水平条还是垂直条(可能是垂直的?)以及它是否应该增长或缩小(目前它会缩小)
我还注意到 myAnimationCounter2
与 myAnimationCounter1
的更新时间/速率相同,这使它们完全相同。如果是这种情况,也可以使用 myAnimationCounter1
来绘制矩形,并且可以丢弃 myAnimationCounter2
以简化代码。如果您确实计划在将来独立于 myAnimationCounter1
更新 myAnimationCounter2
,那么值得保留它。
每次用户点击计数器都会重置,但是打开动画只会在用户按下空格键后显示一次,但无法重置。