主题:  了解父脚本和面向对象----1

D计划-混沌

职务:管理员
等级:6
金币:15.2
发贴:3528
#12003/11/2 9:27:14
翻译自 www.macromedia.com/devnet/mx/director/articles/oop_dir_flash.html
感谢英语角和eiffelqiu的大力帮助

1&1.计划和实现对象

开发者经常凭借图表来分析游戏各部件的交互关系 , 检查他们的状态 ,并且绘制映射出他们行为间的交互关系。

大多数开发者最先开始编程都是采用过程化编程。为了做这些,他们创建了一个方法(句柄或函数)来完成一些基本的运转并且有时返回一个值。每个方法都是一个过程。


注意: 过程程序设计是一种程序设计方法。它意谓程序撰写在一种逻辑上的连续的方式。 程序工作通过一个简单的顺序的 原因/结果 事件,而且顺序是比较容易预料的。 过程经常非常特定的到一个单一程序;你在另外的一个程序中不大可能再容易的使用它们。


Director 和 Flash都可以让你选择使用对象,将一些过程赋予脚本实例,让你可以为你的游戏创建一个更模块化的设计和执行。 这种轻微但有重要意义的改变在程序设计上通常被叫做面向对象设计, 或 OOP 。

注意: 面向对象设计也是一种程序设计方法。 在面向对象设计中,你仍然使用过程,但是你使用一个最初的脚本 (被称为一个类)作为模板去创建没有数目限制的实例对象, 每个知道如何运行过程和包含封装的全局变量即是属性。这些变量是专用的,因为他们属于这个对象的这一个实例, 但是它们是持久化的(当程序运行后他们持续存在变量中)。

考虑两者都程序地设计一艘船像一个对象。 一艘船需要加速,转向,倒退, 和其他的一些事件。它有许多属性像油漆彩色,大小和浮力。 现在如果你想制造一艘以上的船只,你需要给一艘船的每个实例一个单独的脚本。 使用过程程序设计方法,你会为第二艘船的油漆彩色,大小和浮力设置新的变量名。


boat1.bouyancy=20boat2.bouyancy=23
在一个面向对象的模式中,一个脚本的对象或实例包含类脚本所定义的元素。这意味着boat1有它自己的油漆属性,而且其它的船也是。

无论你呼叫它什么: 一个mc,一个父脚本,一个行为,一个精灵或一个实例, 在每个 Macromedia flash 和shockwave游戏中的基础单元是一个对象。 因为 ActionScript 和lingo都支持面向对象程序设计方法,而且因为游戏中经常要求相似对象的许多实例, 创造模仿这些结构时常是最直觉的和明智的方式。

遵循面向对象程序设计,还有其他的好处。代码可重用,容易修改而且极大的减轻了你的负担。不幸地,它也能变成糊涂的,费解的,和不稳定。实施起来需要花费更长的时间,而且需要大量的实践才能掌握它

如何选择适当的时间和地点使用面向对象程序设计方法,而不是过程化程序设计方法,对于成功的开发你的游戏非常的重要。

通过判断和小心地描述游戏的每个对象的状态和交互能力,它可能加速目前的项目 , 并且减轻以后的游戏开发。这是使用面向对象设计来增加过程程序设计的基本理由。



D计划-混沌

职务:管理员
等级:6
金币:15.2
发贴:3528
#22003/11/3 10:13:51
ActionScript 和 Lingo 的面向对象设计

对象的概念和面向对象的编程方式在许多计算机语言中都有,然而在对象模型的实现上,不同的语言却有着或大或小的差别。

ActionScript 和 Lingo 都可以按照面向对象的方式编程,事实上,Macromedia Flash 和 Director 都在自己的实现中遵循着基本的面向对象模式

Macromedia Flash 和 Director 在面向对象实现上最大的差别在于 Macromedia Flash 使用完全的对象驱动方式,因此它使用原型而不是类作为对象实例生成的基础

在 Macromedia Flash 中,一切事物都是对象,因此,几乎都是采用模板脚本来自动实例化对象本身的。 这些自动实例化的类脚本被称作原型

Director 使用了更典型的方式。 原始的类脚本在 Director 中被称为父脚本,它们仅仅作为创建对象的模板而存在。

考虑到这点,面向对象的结构对我们来说一点也不陌生,因为它们是在模仿我们对事物基本组织结构的理解以及现实世界中的想法



D计划-混沌

职务:管理员
等级:6
金币:15.2
发贴:3528
#32003/11/4 9:54:41
对象的性质

一个对象是一个不连续的实体。称它为一件东西,一个结点或一个构件是合理的。在面向对象编程中,对象是产物或被类产生的实例。对象可以有属性 , 展示行为,执行方法,和赋值。


图片如下:

图 1. 这一个类模板是八个实例的源。

可重复使用性和封装是二个最明显的面向对象编程的好处。你可以使用一个类脚本产生无限量的专用实例对象,这里的每一个对象都是一个受保护,封装的实例。换句话说,实例不影响或改变类;他们不影响或改变彼此。


图片如下:

图 2.每个实例包含相同的属性和句柄如类,但是每个实例的属性数值被封装。你可以改变他们而不影响其他实例或类脚本。


如果这是你第一次接触面向对象观念,最后一点可能很难理解。不要惊慌。我们就要去讲述一些不同类型的对象。首先是有点像一个实际的物体 , 举例来说,一台电冰箱 。称呼这个第一个种类为复合对象。


图片如下:

图 3. 复合对象是基于实际的事物的一个对象 , 像这台电冰箱。

电冰箱对象不是空的。它有一系列内部的架子,而且每一个包含其他的物体。举例来说,顶端架,在它里面有一盒牛奶。那么简要重述,电冰箱有一盒牛奶。这盒牛奶,再进一步,它里面牛奶有一个量。


图片如下:

图 4. 复合对象的在各种不同的对象之间的 "has-a" 关系与每天事物的空间关系类似。

复合对象有时被叫做 "has-a" 对象因为在多重对象之间的关系是一个暗示空间包含的关系。

你使用的电脑显示一个由带有色彩值的像素组成的屏幕。在这些对象之间的关系和复合对象之间的关系是类似的。

在这个系列文章里也许更有关的一个例子是一个Shockwave 3D成员组成。3D世界有一个几何模型节点, 依次分别有着色和纹理。在 Macromedia Flash 中一个矢量图形有一个顶点列表,分别有 x 和 y 的值和一个向量。

对象的另一个种类叫做继承对象。这些对象通常用来模仿概念上的关系而并非空间的。许多事物只存在概念或类别但没有实际形式。这些类型十分适和对象的结构。


图片如下:

图 5. 嗨,把你的头拿开! 你挡着我看电冰箱了。

一个水果是什么种类的水果?你能在早餐要求水果并且确定结果吗?在感觉上你可能给出一些水果的种类,但是你不知道如何分别草莓和香蕉。

其实在草莓和香蕉之间有一个重要差别。一粒草莓是一个水果。一根香蕉是一个水果, 但香蕉有外壳。因为在对象之间的概念上的 (类型) 关系,这些对象继承来自他们最初分类的共有性质。水果都是植物, 都是甜果, 而且都有种子.因而把它们叫做 "继承对象"。

他们都能够从一个类分下来的前提是,我们能够概念上把这些事物联系在一起。他们的共同来源已经在我们的大脑中存在。



D计划-混沌

职务:管理员
等级:6
金币:15.2
发贴:3528
#42003/11/6 0:39:19
Creating an object
创造一个对象

创建任何对象的一个简单实例, 你都要用到一个简单的命令—new。new命令调用一个block代码被称作构造函数。在支持OOP 的任何语言中,构造函数产生基于类模板对象的一个实例。

这个语句主要产生关于对象的引用,给他分配内存, 并且初始化。它的功能远远不只这些。它能做你想做的任何事情。现在,把它当做创建对象实例的block代码。

在 ActionScript 中,类脚本是一个专用函数,在 Lingo 中的时候,类是一个父脚本。在清单 1 ,清单 2 , 和清单 3 里,你将会分别找到一个 ActionScript 和 Lingo 对象的简单的类的例子。

清单1:Flash AS new命令。
oFlashWidget = new Object(); 


先关注一下Flash。在这里,这个对象并不打算做太多,基于这行代码将为一个普通对象初始化一个单一实例。Flash 和 Director 使用相同的代码命令,new(), 来初始化对象。


注意:如果你熟悉 Director 并且刚接触 Flash ,你可能会有点疑惑, 地球上怎么会有这样的脚本。句柄在哪里?包含了哪一个事件?放松。Flash 和想像的不一样。像这个命令, 不需要在句柄中被封装是为了要在Flash 脚本中运行。


在两个语言中,new() 是对象初始化命令。它调用构造函数,依次产生基于类模板对象的一个实例。

在 Director 中,模块是一个专用父脚本的表现形式。记住模块是定义类的脚本。一个对象的所有实例都是创造在这些模块的基础上。

注意: 如果你熟悉 Flash又刚接触 Director ,你可能疑惑脚本这样一个观念。更不幸的,你将会很快发现脚本有不同 "类型" 。放松。在 Flash 和 Director 之间有许多相似性。脚本是数据类型,就像图像或符号, 他们存放在一个叫做cast的库里。行为脚本和 Flash 的(符号,按钮或mc的)脚本类似。父脚本是用于对象的专用脚本类型。

因为 Lingo 需要一个专用的父脚本为对象担当模块或类,只需要运行一次new命令来建立一个类。( Flash 使用一个相似的结构,但是因为实例是从原型被建立, 构造函数存在于很多的方式。)如果你从 Director 消息窗口在运行下面的代码,系统会报告一个错误。找不到一个命名为 "scriptName" 的脚本。

为了启动对象,你必须首先建立一个父脚本 (类) 然后调用消息窗口来运行构造函数。

清单2:Director Lingo New命令。
oShockwaveWidget = new(script "scriptName" )


所有你确实需要创建的对象父脚本在Lingo里是一个单独句柄。因为你可能想当对象一旦被建立时访问他,你将需要在新的句柄返回对象,。

在Director 的一个父脚本窗口输入清单三出现的新句柄并且命名脚本成员 "scriptName"当你在消息窗口调用来自清单2 的代码的时候,父脚本将会产生一个子对象,或这个脚本的实例。

清单3:Director Lingo 对象类,构造函数。
on new(me)
return (me)
end


在两个范例中,清单产生了实例原型或一个父脚本。两个程序提供一个机制在它自己的类脚本里面提及对象。在 Flash 中,自身引用的关键词是 "this,", 在 Director 里通常使用 "me."

我说在Director通常用"me"来做Director Lingo 对象的自身引用关键词,因为实际上他可以是在第一次被设定的任何东西如果你喜欢this , 简单将清单 3 的 "me"改成"this"就可以了.



D计划-混沌

职务:管理员
等级:6
金币:15.2
发贴:3528
#52003/11/7 9:08:55
对象属性-lingo篇

另一个使对象这么酷的方面是封装。你能制造成百上千只要是计算机内存允许的对象的实例,而且每个都会有自身独有的属性。

注意: 本质上对象是一种组织代码的方法,他允许你在同一位置储存数据和设计的。因为对象的每个实例的数据不同, 数据必须是 "封装" 或被对象的实例包含。

一些语言保护变量甚至更深入,防止在外部访问和改变给定对象的属性。在 Lingo 和 ActionScript 中,对象的隔离没有这么严格。事实上你能从任何地方改变对象的属性和调用他们的句柄, 只要你适当地引用这个对象。

让我们试一个简单的例子并且产生一个基本的色彩拾取器。我们将会把每个色块作为一个对象。 清单 4 是你将产生每个实例的类脚本。 每个实例有四个属性, 在lingo第 1 -9行中定义 -这些属性在主要句柄之外被定义, 通常地在脚本的顶端。

构造函数从第 12 行开始。它调用初始化句柄 --init()-- 然后返回关于对象的引用。注意新的函数将会有三个参数。假设第一个参数将会是对象的引用,第二个参数将会是被创建对象的编号,第三的参数将会是被请求对象的大小。

初始化句柄从第 19 行开始。第 20-26行 只是将参数转换为色彩值,大小和索引属性。一个新的虚构的图像对象也在这次被建立,而且图像的引用被储存在 pImage 属性中。

列表 4: Lingo 的类 (父) 脚本
 1: property pColor 
 2: -- declare a property that will store color
    -- 定义一个属性储存色彩
 3: property pImage 
 4: -- the image object - a RAM only copy of the bitmap
    --图像对象 -图像在内存里的拷贝
 5: property pRect  
 6: -- the on screen rectangle occupied by the image
    -- 图像在舞台上的方形区域
 7: property pIndex 
 8: -- the position in the list of this instance 
    -- 这个实例在list表的位置
 9: property pSize
10: 
11: -- the size of the color chip
    -- 色块的大小
12:   on new(me, instanceID, size) 
13: -- the constructor
    -- 构造器
14:   init(me, instanceID, size) 
15: -- initialize the object - calls the on init handler.
     -- 初始化对象 - 调用 on init 句柄.
16:   return(me)
17: end
18: 
19: on init(me, instanceID, size)
20:   pIndex = (instanceID - 1)
21:   --store a reference to the relative position of this object
      --储存这个对象的相对位置的引用
22:   pColor = paletteIndex(pIndex)
23:   -- grab the next color from the palette
      -- 获得调色板的下一个色彩
24:   pSize = size
25:   -- take the size that was passed in and store it.
      -- 重储存器里得到大小的值
26:   pImage = image(pSize,pSize,8,0)
27:   -- create a blank image object with an 8 bit resolution and no alpha
      --创建一个8位没有a通道的空白图像对象
28:   tColumn = (pIndex) mod 32
29:   -- figure out which column into which this should fall
      -- 计算应该属于那一列
30:   tRow = (pIndex)/ 32
31:   -- figure out which row this should fall
      -- 计算应该属于那一行
32:   pRect = rect((tColumn * pSize), (tRow * pSize) ,((tColumn + 1) * pSize),33:   ((tRow + 1) * pSize))
34:   -- figure out the appropriate rect for this color chip.
      -- 计算出当前色块方形区域
35:   draw(me)
36:   -- draw the color chip - run the draw handler for the first time.
      --绘制色块-运行draw句柄
37: end
38: 
39: on draw(me)
40:   pImage.fill(0, 0, pSize, pSize, pColor, [#shapeType: #rect])
41:   -- Draw a filled rectangle onto the image object.
      -- 在图片对象上填充一个区域
42:   (the stage.image).copyPixels(pImage, pRect, pImage.rect)
43:   -- copy the image onto the stage.
      -- 将图像拷贝到舞台
44: end
45: 
46: on stepFrame(me)
47:   draw(me)
48:   -- this just keeps drawing the image over and over.
      --不停的绘制色块
49: end


从第28行开始有点难理解。基本上,它用列和行建立一个表格。然后,它要求每个色块对象在表格内排列。如果你没做过一些这类的事情,一部分代码可能看起来是多余的。看一下计算机的各种运算寻找一个好的办法?

第 28 行也产生一个临时变量而且将它设定为对象的索引数除以32 的余数。这个图表里有 8行 32列。mod运算返回一个数的余数

如果你想到这点,基本上确定我们得到了一个表格。举例来说,7 mod 32=7.这样你得到一个行的整数范围从 0 到 31 。总数为 32个列,而且他们自动正确的排列。举例来说 33 mod 32=1, 正如你期待的第33个块放在第一列。

怎么为色块产生八个行,每个在早先的色块上面,为了纵向展开,我们同样需要分开行。用第 30 行的语句来实现, tRow =( pIndex/32)

现在, 你需要了解 Director 的整数和浮点数,在这情况, pIndex 和 32 都是整数,因此,director 会将结果舍入为较小的整数。换句话说,如果 pIndex 是比 32小, 结果将会是 0 。这在 0 和 7(8个行) 之间给我们一个行的范围,让我们对每个色块自行放置有一个很好判断方法。

计算通过行和列的位置和每个块的大小来得到矩形区域 。用计算偏移行和列的数值得到矩形区域的右下角。

注意: 在 Director 和 Macromedia Flash 中,坐标是相对于舞台( 应用程序使用的屏幕部分)的左上角。

最后,draw句柄用适当的色彩填充区域柄并拷贝到舞台。draw句柄在每一次帧刷新被每个对象重复调用。


图片如下:

图6.每个色块是父脚本的一个独立色块。

在这里注意你在完成图像中的每个色块是一个独立对象的可见显示。他们全部有自己的属性, 现在很容易证明。事实上他们显示不同的颜色,然而,你用了同一脚本产生所有的色块。

色块通过清单 5 的新句柄建立。在清楚所有残余的全局变量后,这个简单的句柄调用对象构造函数为 255 次, 而且把关于每个对象的引用加入到 actorlist 。

注意:actorlist 是 Director 的一个专用属性。它使用一个列表有计划的储存活跃的对象。在这个专用列表里的对象通过一个专用的stepFrame 事件在回放移动时被通知改变。stepFrame 叫做每一次帧回放改变。

Director 使用一个repeat循环与类似 ActionScript 和其他的语言的for 循环。整个的句柄从startMovie 事件中被执行。

列表 5.Lingo movie脚本的对象初始化命令.

1: on startMovie()
2: clearGlobals()
3: -- get rid of the old globals before we start.
--清楚所有残余的全局变量
4: repeat with iter = 1 to 256
5: -- do this 255 times incrementing ++ the value of iter
--循环255次
6: tObject = new(script "colorChip", iter, 10)
7: -- create a new instance of the chip.
--创建一个新实例
8: add(the actorlist, tObject)
9: -- add this reference to the object to a built in list.
--将对象的这个实例加入到实例表
10: end repeat
11: end



D计划-混沌

职务:管理员
等级:6
金币:15.2
发贴:3528
#62003/11/13 9:48:24
对象属性-flash篇

和上篇一样创造完全一样的 Flash 对象没有什么不同。

因为没有imaging Lingo来处理位图, 所以语法有一些法改变,flash有一个用作矢量的绘图 API,而且mc将会为色块形成理想的基楚对象。

例子 2.2 中Flash movie的运行功能和在例子 2.1 的 Director movie做的基本相同的。

在清单 6中是Flash的ActionScript。

你可能首先注意到的有关对象的 ActionScript 版本只带有一个脚本, 因此用一个清单列出它们。

把脚本放在例子movie中层的第一个帧上。

为了完全理解, 注意到我已经把一个列表 (阵列) 加入到 Flash movie的_level0对象, 包括了director movie中的十六进制色彩的色块

这仅仅以便我们将会看到相同的最终结果图像。

我也可以设置色彩的数值,然后将这些数值转换成十六位, 但是这里最好不要把问题弄得太复杂了。

注意:如果你是 Flash 新手, _level0 对象是 Flash 文档中层次的最低的movie。

一旦列表被增加到那 _level0, 我使用"for in" 重复循环产生一系列的色块。

"但是,等一下!"你可能要说"类脚本呢?" 好问题。

记住 Macromedia Flash 使用原型并非类脚本,因此当我们要继续下去, 要先创建原型。

在 Flash ActionScript 里的一个技巧是记住事实上所有的东西都是对象,你通常需要说你想要做什么,和哪个对象你想要用来完成这个工作。

在清单 6 的第 20 行上,我指示基础级别的MC产生一个新的mC,而且命名为一个通过循环改变的值的名字。

我可以使用index []来访问this 的属性来查阅新的名字, 这比使用点句法引用好些。

"sq" and iter连接iter和字符串形成一个"sq1"这种格式的新串.

我使用清单 6 的第 22 行的产生一个储存最近创造的mc的引用变量。

现在新的mc将会形成我们的原型,我将要给他加一个句柄。

To do this, I store the function in a property reference of the newly generated clip.
为了要做这,我在一个属性引用里存储新产生clip的函数。

这些发生的很快, 一下就到了第 25 行。

当我说 " theClip.makeChip=function(x,y,z){", 我产生mc的一个新属性叫做makeChip。

同时,我分配一个属性的值到一个处理每个色块色彩、大小和位置信息的方法。

展示语句的左边设定属性,而且右边分配一个新创造的过程。

这一个方法将会使用三个参数 ,参数的名字直接在括号后列出。

列表6.色块对象的ActionScript
1: //制造一些色块
 2: // v.1
 3: 
 4: //为_level0增加一个属性包含了苹果调色板所有缺省十六进制的值
 5:  
 6:  
 7: 
 8: _level0.pHexList = ["0xFFFFFF", "0xFFFFCC", "0xFFFF99", "0xFFFF66", "0xFFFF33"];
 9: // 很明显我简化了列表,因为我想你不会有这个需要,列表出缺省苹果调色板的十六进制的值
10: 
11: 
12: // 一个小操作,设定原始影片的缩放比
13: 
14: _level0._scale = 100;
15: 
16: //我将要贯穿上面的属性列表的每一项做一个循环
17:  
18: for (iter in _level0.pHexList) {
19: //创建一个新mc对象命名为sq1,sq2及其他。
20:  	this.createEmptyMovieClip("sq"+iter, iter+1);
21://创建一个变量引用当前clip名
22:  	var theClip = this["sq"+iter];
23:  	//现在制作一个方法将绘制小的方块
24:  	//并且将movie移到属于自己的分配的位置
25:  	theClip.makeChip = function (index, size, alpha) {
26:  		//首先创建一个色彩对象
27:  		theclip.color = new Color(theclip);
28:  		 
29:  		// 现在创建一个变量引用是十六进制色彩列表的当前值
30:  		var tHex = (_level0.pHexList[index]);
31:  		//现在为色彩对象设定十六进制的色彩值
32:  		theClip.color.setRGB(tHex);
33:  		
34:  		//将来自参数的index值存储到一个命名为pIndex的属性
35:  		theclip.pindex = index;
36:  		 
37:  		// 将来自参数的sixze值存储到一个命名为psize的属性
38:  		theclip.pSize = size;
39:  		// 设定每个clip的pAlpha属性为参数Alpha
40:  		theclip.pAlpha = alpha;
41:  		 
42:  		// 现在准备绘制小的方块,先设定线风格
43:  		theclip.lineStyle(1, theclip.color, theclip.pAlpha);
44:  		// 第二,移动到起始点
45:  		theclip.moveTo(0, 0);
46:  		// 三,设置fill函数
47:  		theclip.beginFill(theclip.color, 100);
48:  		// 四,向下画根线
49:  		theclip.lineTo(0, theclip.pSize);
50:  		// 向右画根线
51:  		theclip.lineTo(theclip.pSize, theclip.pSize);
52:  		// 向上画根线
53:  		theclip.lineTo(theclip.pSize, 0);
54:  		// 完成这个方块。
55:  		theclip.lineTo(0, 0);
56:  		// 完成fill函数
57:  		theclip.endFill();
58:  		// 设定每个clip的宽
59:  		_width = pSize;
60:  		//设定高
61:  		_height = pSize;                
62: 
63:  		// 产生一个保存每个 mc 的列位置的变量 * 注意这里取得当前索引的 mod,以便我们得到32行
64:  		
65:  		var tColumn = (theclip.pIndex)%32;
66:  		
67:  		// 产生一个支保存行位置的变量。很重要地, 我们在这里不使用浮点数,因此,我们使用数学对象的floor函数的建造。
68:  		
69:  		var tRow = math.floor((theclip.pIndex)/32);
70:  		//移动mc到分配的x位置
71:  		theclip._x = ((tColumn)*(theclip.pSize));
72:  		//移动mc到分配的y位置
73:  		theclip._y = ((tRow)*(theclip.pSize));
74:  	};
75:  	//调用makeChip函数
76:  	theclip.makeChip(iter, 10, 100);
77:  }


在这里注意这个函数不期待第一个参数是一个对象引用,就像相关的 Lingo 句柄做的办法。

它知道对象而且不需要保留对它的引用。

当调用新创建的 mc 的 makeChip() 函数, open和close的之间的是将会运行的代码。

这些属性不在现在被设定;函数只是简单的被拷贝到对象。

稍后,当chip的 makeChip() 函数被调用时,它将会实际运行这个代码。

这在函数立刻发生在第 76 行上被建立之后.

这个最后命令简单的调用函数并且代码开始运行。

很容易看到代码将在新对象上执行,因为 Flash 格式化代码从最初函数缩进,而且它保持缩进直到函数在第 74 行上被关闭。


图片如下:

图 7. 像在 Lingo 例子中做的一样,一个单一脚本产生每一个色块。

虽然个每个的专用脚本都不同,仰赖他们个自语言的句法,可以看到他们基本上有相同的功效。

这些简单的对象在这里真实的设定显示一些原始的属性 (位置和色彩) 。

下一篇讲面向对象编程的高级部分,对象的怎么产生独立的能力,因为子们有自己的方法和工作链。



threem

职务:普通成员
等级:2
金币:1.0
发贴:338
#72003/11/15 4:08:00
巨好的帖子!我转载到 mobiusclub.com 了!
谢谢 54zq !