vvvv-plugin 入门教程
本篇文章的目标是让即使身份是设计师的朋友, 也会在连接patch时偶尔想到会不会自己写个plugin会更方便些.
从一道应用题开始:甲.乙两人同时从两地出发,相向而行,距离是50千米。甲每小时走3千米,乙每小时2千米,甲带着一条狗,狗每小时走5千米。这只狗同时和甲一起出发,当它碰到乙后,便回头跑向甲;碰到甲后又回头跑向乙,如此往复,直到两人相遇。问,小狗一共跑了多少千米?
一、我们纯初学
这只是一道简单的应用题, 我们希望有一个节点,可以让我们输入了距离,速度这些参数之后,节点可以输出小狗跑的距离。
1. 双击vvvv空白处, 输入template.
按住CTRL再点击Template(Value), 创建一个新的Plugin
2. 右键这个新的节点,进入编程模式。PluginInfo区域用来定义节点的基本信息。但我们初学,先不管它。我们最关心的是怎么定义距离、速度这些输入pin脚,和小狗跑步距离这个输出pin脚。它们是在fields & pins区域里定义的。
#region fields & pins [Input("People Distance")] //输入Pin脚名称为People Distance ISpread<double> Distance_In; //double是小数类型,int是整数类型. //Distance_In是我们自定义的变量名 [Input("A Speed")] ISpread<double> ASpeed_In; [Input("B Speed")] ISpread<double> BSpeed_In; [Input("Dog Speed", DefaultValue = 0.0)] //DefaultValue = 0.0说明Dog Speed这个pin脚默认值为0 ISpread<double> DogSpeed_In; [Output("Dog Distance")] //Output ISpread<double> DogDistance_Out; [Import()] ILogger FLogger; //调试信息输出. 我们不是程序员,此处略去. #endregion fields & pins
3. 编码。我们的代码要写在Evaluate这个函数里才可以被执行,现在我们来计算小狗运动的距离,并且输出它。
public void Evaluate(int SpreadMax) { /* 这里必须要插句嘴, vvvv的节点默认都是要操作Spreads的, 所以我们 在写节点时要考虑如果输入了很多数据, 我们要输出多少数据.在这里 如果我们输入了10组不同的数据, 就需要输出10个小狗运动的距离 */ //设置我们的输出数量. DogDistance_Out.SliceCount = SpreadMax; //遍历计算每组数据. for,if这种程序语法, C#教程看一会就明白了 for (int i = 0; i < SpreadMax; i++) { //计算相遇时间 double t = Distance_In[i] / (ASpeed_In[i] + BSpeed_In[i]); //输出距离 DogDistance_Out[i] = DogSpeed_In[i] * t; } }
4. Ctrl + S保存。新写的节点就可以用了。测试一下。
二、 哎,有那么点意思。
1. PluginInfo区域的参数我们简单介绍一下。
#region PluginInfo [PluginInfo(Name = "DogRun", //Plugin显示的名字 Category = "Value", //Plugin的所属目录 Version = "0.0.1", //Plugin的版本号 Help = "Help children against theri homoworks", //Plugin的帮//助信息, 挑选节点时,当鼠标放在这个Plugin上会显示此信息 Tags = "dog, run", //Plugin的标签 Author = "agalloch21" //Plugin的作者 )] #endregion PluginInfo
2. fields & pins区域,格式是固定的。
[Input("Dog Speed", DefaultValue = 0.0)] ISpread<double> DogSpeed_In;
1) Input表示这是一个输入pin脚, Output则是输出。
2) “Dog Speed”是这个pin脚的名称,鼠标放在pin脚上即可看到。
3) DefaultValue是这个pin脚的一个属性, 这个pin脚还可以有MaxValue、MinValue、IsSingle等很多属性。这里不详细讲。
4) ISpread类型表示pin脚传入传出的是一个铺展(Spreads),这里只有ISpread和IDiffSpread两种类型可以选。IDiffSpread类型的优势在于当这个pin脚的值改变了, 你可以收到通知.
5) double是小数类型, 说明这个pin脚传入的数据是小数。其它类型:int(整数), bool(布尔值), string(字符串)类型。
6) DogSpeed_In是我们自定义的变量,下面写代码时会用到它。
3. SpreadMax
SpreadMax的值等于输入的这些数据中最多的个数。比如输入了10个距离, 9个甲乙的速度, 8只小狗的速度, 那么SpreadMax的值为10.
三、 我有点晕, 但我还想继续往下看。
这次我们的需求提高了,我们希望模拟这只小狗的运动过程,我们输入甲乙的距离,甲乙的速度,小狗的速度,然后希望根据时间实时输出小狗和甲乙的位置。
1. 修改pin脚。
根据需求, 我们需要有甲乙距离、甲的速度、乙的速度、小狗的速度这4个输入pin脚,需要有小狗的位置、甲的位置、乙的位置这3个输出pin脚。我们可以更高端些,仿照LFO节点让我们的DogRun节点有Pause和Reset的功能,所以我们再加上Pause和Reset的输入pin脚。 这样改变后的fields & pins就是这样子
#region fields & pins //这里为了避免太复杂,我们只计算一组数据,即使有很多数据输入. [Input("Distance", IsSingle = true)]//IsSingle表明即使输入了很多数据, 该pin脚也只取第一个值 IDiffSpread<double> Distance_In; //这里我们用IDiffSpread类型, 希望在这个值改变时得到通知 [Input("ASpeed", IsSingle = true)] IDiffSpread<double> ASpeed_In; [Input("BSpeed", IsSingle = true)] IDiffSpread<double> BSpeed_In; [Input("Dog Speed", IsSingle = true)] IDiffSpread<double> DogSpeed_In; [Input("Pause", IsSingle = true)] ISpread<bool> Pause_In; [Input("Reset", IsSingle = true, IsBang = true)] //IsBang表明这个pin脚是Bang类型的 ISpread<bool> Reset_In; [Output("Dog Position")] //Output ISpread<double> DogPosition_Out; [Output("A Position")] ISpread<double> APosition_Out; [Output("B Position")] ISpread<double> BPosition_Out;
2. 成员变量是个好东西。
因为每时每刻甲乙和小狗的位置都在改变,所以我们需要有一个地方保存他们当前的位置,下一次计算时要用到。成员变量适时出现。
//定义成员变量, 并且初始化 double a_position_ = 0; //记录A君的当前位置 double b_position_ = 0; //记录B君的当前位置 double dog_position_ = 0; //记录小狗的当前位置 double dog_direction_ = 1; //记录小狗此时的运动方向 bool meet_ = false; //甲乙两人是否相遇
成员变量可以简单理解为定义在Evaluate 函数外面的变量,它一直存在,可以用来存储数据。定义在Evaluate函数里面的变量在Evaluate 函数运行完之后都会消亡,所以不能用来存储数据。
3. 编码。
//called when data for any output pin is requested public void Evaluate(int SpreadMax) { //设置输出数量为1,我们只计算一组数据 DogPosition_Out.SliceCount = 1; APosition_Out.SliceCount = 1; BPosition_Out.SliceCount = 1; //如果输入的数据变化了或者"Reset"pin脚设为true了,那么我们要重新模拟小狗的运动 if(Distance_In.IsChanged //只有IDiffSpread类型才有IsChanged标志,当Distance这个pin脚的值发生改变时,IsChanged的值为true || ASpeed_In.IsChanged || BSpeed_In.IsChanged || DogSpeed_In.IsChanged || Reset_In[0] == true) { a_position_ = 0; b_position_ = Distance_In[0]; dog_position_ = 0; dog_direction_ = 1; meet_ = false; } /*因为我们只计算一组数据, 所以读写数据时加上下标[0]表示我们操作的是第一个数据.*/ if(Pause_In[0] == false && meet_ == false) //如果没有暂停的话并且甲乙两人没有相遇的话 { //更新两人和小狗的位置 a_position_ = a_position_ + ASpeed_In[0]; b_position_ = b_position_ - BSpeed_In[0]; dog_position_ = dog_position_ + dog_direction_*DogSpeed_In[0]; //如果两人相遇 if(a_position_ >= b_position_) { a_position_ = Distance_In[0] / (ASpeed_In[0] + BSpeed_In[0]) * ASpeed_In[0]; b_position_ = a_position_; meet_ = true; } //如果小狗跟A相遇了, 折返 if(dog_position_ <= a_position_) { double temp_t = (a_position_ - dog_position_) / (ASpeed_In[0] + DogSpeed_In[0]); double temp_p = dog_position_ + temp_t * DogSpeed_In[0] + temp_t * DogSpeed_In[0]; dog_position_ = temp_p < b_position_ ? temp_p : b_position_; dog_direction_ = 1; } //如果小狗跟B相遇了, 折返 if(dog_position_ >= b_position_) { double temp_t = (dog_position_ - b_position_) / (BSpeed_In[0] + DogSpeed_In[0]); double temp_p = dog_position_ - temp_t * DogSpeed_In[0] - temp_t * DogSpeed_In[0]; dog_position_ = temp_p > a_position_ ? temp_p : a_position_; dog_direction_ = -1; } //输出他们三个的位置 DogPosition_Out[0] = dog_position_; APosition_Out[0] = a_position_; BPosition_Out[0] = b_position_; } //FLogger.Log(LogType.Debug, "hi tty!"); }
四、 都是代码,真没劲。写出个这节点有什么用啊
上面确实代码比较多,我也不太好讲,程序语法还是要找一本C#的书踏实的了解一下,一天就够。接下来我去用刚才新建的DogRun节点画点东西出来,以期望可以留住你们的眼球。
这是用DogRun画出来的一个图形,如果你觉得还不错,还剩点兴趣,请跳回第三章,把仅剩的兴趣献给代码吧。不要执着在小狗折返,速度计算这些代码,你需要在意的是vvvv-Plugin的框架。弄明白了Plugin的结构,照葫芦画瓢,就可以自己动手去写一个节点啦!