主题:  有限距离内拖动节点完全教程

janlay

职务:管理员
等级:7
金币:28.0
发贴:7244
#12001/9/7 17:55:02
终于写完了,对以前写的几篇作了一点小的改动。

这是一个关于在有限距离内拖动节点的试验程序。

与以前所见类似动画相比,增加对以下特性的支持:
1.实时改变限制长度
2.动态节点数目


页面:点这儿参观

源文件下载:
www.5dmedia.com/users/public/dragnode.fla

首先,我说说在这次制作过程中,一个有关for()循环的经验。

在这个动画中,有这样两条语句:
var t = 0;
for (var i = id-1, j = id+1; !leftFinished || !rightFinished; t++)
我并没有说明变量t是作什么用的,事实上,循环中并没有引用到它。为什么要写上呢?
我原来在expert mode中写的只有一句:
for (var i = id-1, j = id+1; !leftFinished || !rightFinished;)
这在C里是常有的事,for()具有其他循环语句所不具备的强大功能和灵活性。
我常常在写一段之后就切换到normal mode下,这样,flash会自动对我写的代码进行格式化,排成标准的写法格式。
但这次让我很意外,按下ctrl+n马上就出现flash执行了非法操作!。。。倒!,之前有一段还没保存。。。(在此奉劝各位win98的用户,要随时保存你的劳动成果!)
检查刚写的代码,没语法错误呀。我还以为是系统出了问题,启动msconfig检查,一切正常,重启(比尔的无敌绝招),再切换,倒!还是非法。。。这样经历数次,最后目光落在for()那一行,是不是不支持这种写法呢?加上一句试试!可是没有合适的语句。万不得已之下,写了两句废话:
var t=0;
for()中加入t++
再ctrl+n,OK!

这个临时变量也不是一无是处,它可以在调试阶段用来检查循环的执行次数,呵呵,意外发现

我想讲解主要放在actionscript的整体结构和模块化的编程方面,有其他意见或建议者请跟帖


好的,我认为有必要首先说一下这个文件的大致结构,以免后面叙述时不知所指为何物
第一讲:Movie Structure(结构) 上半节
1.symbols
用到两种类型
(1)button
symbol name:change
这个按钮向用户提供对动画的部分控制,做得很简单,没有任何变化效果。场景中有两处都引用到了它,它们各自都覆盖了一个文本对象。看起来两个不同的按钮实际上却是共享的一个symbol.
symbol name:but
这个按钮实际上只是用来使鼠标移上去变成手形,并增加mouse变化,以帮助用户区分哪些是可以拖动的,而哪些是不能拖动的。它的制作也很简单,就不多说了。由于完全没有捕获它的鼠标事件,所以,这个按钮的所有功能完全可以只用movieclip来完成。
(2)movieclip
symbol name:line
就是节点之间的连接线。输出linkage名为line.
symbol name:node
节点,直接引用了刚才说的but, 做成mc的形式是为了绑定后能够检测碰撞冲突(hittest)。输出linkage名为node.
第一讲:Movie Structure(结构) 下半节
symbol name:control
这个mc专门捕获鼠标事件,没有任何内容,被拖动主场景之外(实际上也可以放在场景中),实例名为con。
开始做这个动画的时候,并没有设计这样一个symbol,当时是用node来捕获鼠标事件的。如果这样的话,每个node实例都将独自检测,但真正有效的检测只发生在其中的一个实例上,但是考虑到如果节点数目多起来之后,可能会引起处理缓慢,程序执行效率也将降低,所以最终决定用一个单独的mc实例来捕获鼠标事件并作出相应控制。
为什么说“有效的检测只发生在其中的一个实例上”呢?因为用户鼠标点击在动画上只有一个点,如果这个点落在某一个节点上,就会被捕获,并且程序将这个节点的ID存储起来,以便后面的程序知道是哪一个节点被按下。如果落在两个(或多个)节点的重叠区域,会怎么样呢?这时,程序就认为最上面的节点获得鼠标拖动。因为在上面的按钮总是优先获得鼠标事件,所以程序也改为根据常识来处理。

如前所述,由于是统一由于实例来捕获鼠标事件,所以,程序需要确定是哪一个节点被拖动了。在con中设置一个which变量,指示被拖动节点的ID. ID的由来在后面的数组结构中描述,现在只需要知道它是唯一指示节点的标志就行了。在con载入时,which即被赋初值-1. 因为所有的节点ID都不可能为-1(0<=ID<_root.num),所以,我们就用-1来表示没有节点被拖动。这一点在后面也将用到。

当鼠标按下时,就依次按深度(Z)值或者说是ID从大到小检查到底是按下了哪一个,只要查到了,就把它的ID赋给which,再拖动这个which所代表的节点。最后跳出循环,因为只可以有一个被拖动。

松开鼠标也就意味着mouseUp事件被激活,用if(which!=-1)来判断是否有节点被拖动,如果有,就停止拖动,并做一次处理(由自定义函数check()来完成),重新设置which为没有拖动任何东西(即-1).

当鼠标移动的时候,和刚才一样,也要检查which是否指示为某一节点,如果是,说明鼠标正在拖动它,就对它进行处理;如果不是,则表明鼠标正在自由移动,不做任何处理。

control所包含的功能介绍完毕。至此,所有symbol都已讲完。下一次,将讲解时间轴actionscripts层的三个帧。

未完待续。

第二讲:Frame Action

现在只是讲解时间轴actionscripts层的三个帧。最上面的function层包括了所有的自定义函数,下次再说
frame 1:
先说说几个全局变量的意义:
maxdis:被限制最大距离,检查节点之间的距离是和它作对比的。
maxdef:用户输入的(或者说是显示出来的)最大距离。
num:节点数目。
numdef:用户输入的(或者说是显示出来的)节点数目。

考虑到执行效率和可用屏幕面积的问题(节点多了会使显示变得迟缓,距离大过了有效的拖动区域也就没什么意义了),所以,我们对用户输入的值作了一个范围限制:
0<=maxdef<=100
2<=numdef<=15
这些也可以从两个输入按钮的object actions中找到。由于两个按钮的处理比较简单,所以我不打算对它们作出解释;唯一要说的是,程序是这样处理的:如果输入合法,则把用户输入的值赋给真正在动画中起作用的变量(程序对它们进行检查);如果不合法,则本次输入无效,原值会替换用户输入的字符。

这一帧完成两个重要变量的初始化。

frame 2:
第一个for循环产生所需的节点和线段实例(个数已经被定义)。假设需要10个节点,则产生的节点实例名为n0,n1,n2...n9;产生的线段实例名为n0,n1,n2...n8.

接着产生一个拥有num-1个单元的数组实例node,不用说,它就是处理节点用的。在接下来的一个循环中,最重要的一条是:
node[i] = this["n"+i];
程序把this["n"+i]的值赋给node[i](节点的路赋给相应的数组单元)。这是用的“传址”的方式,和“传值”不同的是,对被传址的对象的修改会直接影响到源对象。之所以采用这种数组的形式来管理对象,有两点原因:
1.写起来简单
2.把零散的实例绑定到一个对象中集中管理,有利于对它们的连续存取访问。

传完之后就要确定节点的纵横坐标了,直接设置它们的x,y就行了。我希望它们横放在y值为69的地方,并每隔20个像素就放置一个。y值直接设置就可以了;对于x可以用一个形如y=ax+b的方程轻松搞定。

对连接节点线段的处理和刚才叙述的节点处理如出一辙,这里就不多说了

frame 3:
看一下就知道是怎么回事了

未完待续。


第三讲:Userdefine Fuctions(自定义函数)

主场景functions层包含了为完成特定操作而设计的自定义函数,有以下几个:
check 顾名思义,对节点进行检查,这是所有检查的入口。主场景中的实例con的鼠标事件对它进行调用,无返回值
reDraw 重绘被牵涉节点及确定需要重会的线段,无返回值
drawLine 重新连接两节点之间的线段,无返回值
getDistance 返回两节点之间的距离

所有函数的形参都是节点的ID(一个或连续的两个),ID值是通过_root.con.which获得的。

检查流程是这样的:
只要拖动节点,将影响它两边节点的位置以及它们之间的连线。首先检查被拖动节点(con.which指示)是否是第一个节点(编号最小,为0)。如果不是,则需要调整它和它左边节点的位置及其连线;如果是,则左边没有节点。同理,如果不是最后一个节点(编号最大,为_root.num-1),则需要调整它和它右边节点的位置及其连线。

接着分析是否有节点由于前面所述的三个节点的位置发生变化而导致与它们相邻节点之间的距离大于预设值,如果有,则要把与之相邻的节点向它靠拢。这句话说得有些烦琐,用图示如下(第一次用图,实在是有些难以表达):



当拖动节点4的时候(此时,con.which=4),引起3和5的位置变化,这是强制的,不论3、4或4、5之间的距离是否大于预设值。当3变化时,就检查2和3之间的距离有没有大于预设值,如果是,则,2向3的方向靠拢,节点3的新位置落在它原来和4的连接线段上。如果2、3的距离大于预设值,2的位置就发生变化,进而检查1和2的距离,如此反复,直到它左边没有节点(id>0)或者第一次碰到距离小于预设值时为止(此时,leftFinished=ture)。向右方检查的方式和这个一样。只要check()执行完了,也就表示本次处理完成,check()等待下一次调用。

流程大部包含在check()中,其他几个函数只是几个实现基本功能的模块。

编辑历史:[这消息被janlay编辑过(编辑时间2002-04-29 23:35:15)]
[这消息被janlay编辑过(编辑时间2002-04-29 23:42:21)]


Fierce

职务:普通成员
等级:2
金币:10.0
发贴:468
#22002/1/13 16:06:05
怎么fla都删了?
能不能再给我一份?



5D荣誉斑竹

职务:普通成员
等级:2
金币:2.0
发贴:617
#32002/1/13 19:07:47
好教程先置顶。
收藏!~~~偶先那回去研究研究~~~



Fierce

职务:普通成员
等级:2
金币:10.0
发贴:468
#42002/1/13 19:45:41
桃人在上个帖子中说
引用:
好教程先置顶。
收藏!~~~偶先那回去研究研究~~~

n年前的帖子了...
fla都丢了.
你猜看见阿...?



5D荣誉斑竹

职务:普通成员
等级:2
金币:2.0
发贴:617
#52002/1/13 19:51:43
哇哈哈哈~~~欧没看见~~那时我还没来呢~~fla没有丢!我刚才还有下的~~
不过不能置顶了~~呵呵~~~



Fierce

职务:普通成员
等级:2
金币:10.0
发贴:468
#62002/1/14 9:07:06
谢谢janlay把fla又放了上来



janlay

职务:管理员
等级:7
金币:28.0
发贴:7244
#72002/1/14 20:23:26
没有删啊

263的破空间不稳定

我把它转到5D的空间上来

Download: janlay.5dmedia.com/myfile/sources/dragnode.fla

现在才发现空间还没做好,还是用老地方先。

janlay.top263.net/myfile/dragnode/dragnode.fla

编辑历史:[这消息被janlay编辑过(编辑时间2002-01-14 20:55:28)]
[这消息被janlay编辑过(编辑时间2002-01-14 20:57:46)]


Fierce

职务:普通成员
等级:2
金币:10.0
发贴:468
#82002/1/15 12:59:15
确实是破263的问题
你的5d空间怎么地址是这样?
我的怎么是 www.5dmedia.com/users/lengyu/ 这种格式的?



Fierce

职务:普通成员
等级:2
金币:10.0
发贴:468
#92002/4/29 18:32:29
谁当过这个dragnode.fla啊?
能不能给我一个。。。
谢谢了。



janlay

职务:管理员
等级:7
金币:28.0
发贴:7244
#102002/4/29 23:44:51
OK,源文件终于找到了,放在5D公共空间里,那个抓图就没办法了。