这个帖子估计列位道友都等了很久,但是请不要怪贫道哦,贫道近一段时间来一直很忙(忙着玩奶头),而且也找不到什么题材可以写的。不过最近发现可以写一个开发中不可避免的问题,那就是对于UI界面中那些透明层的处理。
对于鄙人这样一个游戏开发者来说,遇到可恶的透明层挡路也是家常便饭了,我们来看一个例子先。我需要为舞台上添加一个UI组件,它的外观是一个不规则图形,因此我们必须读取一个带透明层的.png图片来作为此组件的皮肤,当然,我们还得把此组件的buttonMode设置为true,以便我鼠标移上去后能够显示手型,下面是源码:
- package
- {
- import flash.display.Bitmap;
- import flash.display.Sprite;
- public class ByebyeAlpha extends Sprite
- {
- [Embed(source="assets/phoenix.png")]
- private var skin:Class;
- private var myUI:Sprite;
- public function ByebyeAlpha()
- {
- var bmp:Bitmap = new skin() as Bitmap;
- myUI = new Sprite();
- myUI.addChild( bmp );
- this.addChild( myUI );
- myUI.x = myUI.y = 100;
- myUI.buttonMode = true;
- }
- }
- }
运行后我们能够看到正确的组件外观,可是当我鼠标移到它的四周透明区域的时候也TM显示出了手型!
<ignore_js_op>
这是因为我们为Sprite里加入的子对象是一个Bitmap,那么对于一个Bitmap来说,它的所有像素,包括透明像素都是有效的,也就是说刚才的这个Sprite的实际有效区域是带透明像素的那一整个矩形区域。
那么应该怎么做才能去掉这些该死的透明层呢?
第一种方法,也是最简单的办法就是使用Flash CS工具的强大图形处理能力来帮助我们。然而使用CS工具来做,我们也有多种选择:
1.在Flash CS IDE下直接将.png图片素材导入到库中,之后新建一个元件,接下来在元件的编辑视图中把png图片素材从库中拖到元件场景中,此时点选场景中的这个图片,可以证实我刚才讲的话:有效区域是整一个矩形区域,包括了透明的像素,如下图蓝色边框内的区域都是有效区域:
<ignore_js_op>
那么为了把透明区域从有效区域中剔除,我们可以先选中元件场景中的那张图片,然后通过点击工具栏中的 修改----> 位图-----> 转换位图为矢量图 来实现将位图转换为矢量图。
<ignore_js_op>
转换后我们看到原先的图片外观有了一定变化,损失了一定的像素值(这也是直接转换位图为矢量图的缺点所在,但是对于简单图形,像素不是非常密集的图形来说还是够用的了),但是我们再通过鼠标点击图像,会发现只能选中一小块区域,且选中的区域是一个“形状”(Shape)类型的对象。
<ignore_js_op>
这就是转换为矢量图后的结果,它将一个位图整体拆分为了许多的“形状”对象,且略去了原来的透明像素,这样子做的话就能把有效区域规定于仅存在颜色的像素区域内。那么让我们来试试看效果如何吧。把刚才编辑的这个元件扔到舞台上,给此元件取名为myUI,并在舞台第一帧写上动作代码
- myUI.buttonMode = true;
<ignore_js_op>
此时测试一下影片会发现鼠标只有移动到有像素的区域内才会显示手型了。
2.由于直接转换图片为矢量图会导致图形遗失部分像素,可能会直接导致图片的样子产生严重形变,那么我们可以使用第二种方式:遮障。
在之前的案例中我曾多次提到遮障,遮障就像是一道可见光束,只有光束照到的地方才能被看见,其余地方看不见。看下图:
<ignore_js_op>
假设黑色区域是被遮障对象,黄色区域是一个遮障的实心区域(就是有不透明像素的区域),那么我们只能看见且选择被遮障对象中那些处于遮障范围(黄色区域)内的区域。
借助遮障技术,我们可以为原始图片上盖上一层遮障,这层遮障只覆盖原始图片中不透明的区域即可。那么如何生成这样一个完全合身的遮障呢?对,这位同学回答得非常正确,就是利用我刚才讲过的位图转换为矢量图的技术,这位同学你叫什么?哦~叫S_eVent啊,好,为师赐你五道杠!那么现在你来说说具体操作步骤吧。
首先还是进入元件的编辑视图中,在时间轴上增加一个图层
<ignore_js_op>
然后把原来那个图层的第一帧(就是带有图片的那帧)复制到新建图层中(通过右键单击帧,用弹出菜单中的“复制帧”和“粘贴帧”来做到),让上下两个图层一摸一样。之后,点选新建的那一层中的图片,进行“位图转换为矢量图”,完了之后右键单击新建的那一个图层,选择“遮障层”,希望你得到的结果和我一样哦~
<ignore_js_op>
这样就实现了为我们原来那一层图层设置遮障的目的,我们使用位图转换为矢量图的方式快速创建出了只包含源图像不透明的区域,之后把这个区域设置成了源图像的遮障,这样就可以让我们只能够看见并选择那些不透明的区域了。测试一下影片,嗯~perfect~而且图像没有失真。对于经过这种手段制作出来的元件,你还可以通过加载或者导出为SWC等多种方式在Flash Builder或者Flash Develop等纯代码IDE中使用,元件的类别也不仅限于是MovieClip,也可以在属性面板中改成Sprite等。
不过有时候我们必须使用Bitmap来呈现一个对象的时候,就像我一开始给出的那段源码,或者我们不愿意使用CS工具去把每一个组件都处理一遍的时候我们就没办法了么?别灰心孩子,我们的Action Script代码是万能的!
记得BitmapData的API中提供了一个方法叫做getPixel32么?它可以根据传入的参数x,y坐标位置得到bitmapData在该位置上的像素颜色信息,且包含透明通道,那么我们就可以使用该方法获知鼠标位置上的像素点是不是透明的。下面给出一个方法来检测一个目标bitmapData中的某一点是否非透明。
- /**
- * 判断某一位置是否是有效的(不透明的)
- */
- public function isActiveUnderPoint( targetBitmapData:BitmapData, x:Number, y:Number ):Boolean
- {
- if( targetBitmapData != null )
- {
- //首先得到全部的颜色信息
- var color:uint = targetBitmapData.getPixel32( x, y );
- //然后右移24位以取到透明通道的值
- var alpha:uint = color >> 24;
- //比较透明通道的值是否非0并返回比较结果
- return alpha != 0;
- }
- return false;
- }
那么通过这个方法再配合一个MOUSE_MOVE事件的侦听器就可以实现实时的透明度检测了。看到下面的代码:
- package
- {
- import flash.display.Bitmap;
- import flash.display.BitmapData;
- import flash.display.Sprite;
- import flash.events.MouseEvent;
- import flash.geom.Point;
- public class ByebyeAlpha extends Sprite
- {
- [Embed(source="assets/phoenix.png")]
- private var skin:Class;
- private var myUI:Sprite;
- private var bmp:Bitmap;
- public function ByebyeAlpha()
- {
- bmp = new skin() as Bitmap;
- myUI = new Sprite();
- myUI.addChild( bmp );
- this.addChild( myUI );
- myUI.x = myUI.y = 100;
- myUI.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
- }
- /**
- * 判断某一位置是否是有效的(不透明的)
- */
- public function isActiveUnderPoint( targetBitmapData:BitmapData, x:Number, y:Number ):Boolean
- {
- if( targetBitmapData != null )
- {
- //首先得到全部的颜色信息
- var color:uint = targetBitmapData.getPixel32( x, y );
- //然后右移24位以取到透明通道的值
- var alpha:uint = color >> 24;
- //比较透明通道的值是否非0并返回比较结果
- return alpha != 0;
- }
- return false;
- }
- private function onMouseMove( e:MouseEvent ):void
- {
- //首先需要把屏幕上的鼠标坐标位置转换为bitmap内的鼠标位置
- var localPos:Point = bmp.globalToLocal( new Point(e.stageX, e.stageY) );
- //接下来检测鼠标停留位置是否非透明,若是则显示手型,否则不显示手型
- myUI.buttonMode = isActiveUnderPoint( bmp.bitmapData, localPos.x, localPos.y )
- }
- }
- }
相信列位看这段代码时应该不会有问题,唯一需要注意的地方就是坐标系的转换,你不能直接把mouseX, mouseY就直接传参给isActiveUnderPoint方法,而是需要把屏幕坐标点转换为在Bitmap中的坐标点才能正确地让isActiveUnderPoint方法工作。当然,你也可以在CLICK鼠标点击事件中检查点击点是否有效,如果无效则不进行执行任何逻辑。
使用编程的方式虽然可以一劳永逸,但是还是会不断地消耗CPU,虽然消耗程度并不是很大,但是我还是建议列位开发者道友,先把素材处理好再导入使用,这样会省去很多的计算量,提高性能。正所谓凡事有两面性啊,虽然你力量很大,很暴力,很牛逼,能爆出肌肉撑破衣服,但是发过威后就没衣服穿了。
转载自:http://bbs.9ria.com/thread-84228-1-1.html